9

I have this code in a Windows Service targeted to .Net 4.5 that uses a database-first Entity Framework layer:

var existingState = DataProcessor.GetProcessState(workerId);

existingState.ProcessStatusTypeId = (int)status;
existingState.PercentProgress = percentProgress;
existingState.ProgressLog = log;

DataProcessor.UpdateProcessState(existingState);

And this code in a data processing class in the same solution:

public ProcessState GetProcessState(int id)
{
    using (var context = new TaskManagerEntities())
    {
        var processes = (from p in context.ProcessStates.Include("ProcessType").Include("ProcessStatusType")
                         where p.IsActive && p.ProcessStateId == id
                         select p);

        return processes.FirstOrDefault();
    }
}

public ProcessState UpdateProcessState(ProcessState processState)
{
    using (var context = new TaskManagerEntities())
    {
        context.ProcessStates.Add(processState);
        context.Entry(processState).State = System.Data.EntityState.Modified;
        context.SaveChanges();
    }

    return processState;
}

ProcessState is a parent to two other classes, ProcessStatusType and ProcessType. When I run that code in the windows service, it retrieves a record, updates the entity and saves it. Despite the fact that the ProcessType child is never used in the above code, when the save on the ProcessState entity is performed, EF does an insert on the ProcessType table and creates a new record in it. It then changes the FK in the ProcessStatus entity to point it at the new child and saves it to the database.

It does not do this in the ProcessStatusType table, which is set up with an essentially identical FK parent-child relationship.

I now have a database full of identical ProcessType entries that I don't need, and I don't know why this is occurring. I feel like I'm making some obvious mistake that I can't see because this is my first EF project. Is the issue that I'm allowing the context to expire in between calls but maintaining the same entity?

RedBrogdon
  • 5,113
  • 2
  • 24
  • 31
  • 1
    Check to make sure that your ProcessType object being loaded has the property for the Primary Key of it's table and that it's being correctly populated. If for example, ´ProcessType.Id = 0´, EF will think it's a new object and insert it – Matt R Feb 15 '13 at 21:15
  • I just checked, and it does. The Primary Key is present and correct on the way in, and is set to the primary key of the new database record when it comes out. – RedBrogdon Feb 15 '13 at 21:22

1 Answers1

5

Using Add will set the state of all elements to Added, which is causing the child elements to be inserted. The parent element is not inserted as you specify EntityState.Modified for this element.

Try using the following in the UpdateProcessState rather than using Add.

context.ProcessStates.Attach(processState);
context.Entry(processState).State = EntityState.Modified;
context.SaveChanges();

Attach will set the state of all elements to Unchanged and by specifying Modified for the parent element you are indicating that only this element should be updated.

On another note. You should use the strongly-typed Include(x => x.ProcessType) rather than Include("ProcessType").

Martin4ndersen
  • 2,806
  • 1
  • 23
  • 32
  • Thanks for the help. The Attach/Add issue was causing the problem. I made that change, and everything's working properly now. In regard to the strongly-typed include statements, are those still available in EF5? My compiler throws an error at them, and I saw this post: http://stackoverflow.com/questions/4544756/using-include-in-entity-framework-4-with-lambda-expressions which seems to indicate they're deprecated. – RedBrogdon Feb 15 '13 at 21:54
  • 1
    Yes, the strongly-typed Include is supported, http://msdn.microsoft.com/en-us/library/gg671236(VS.103).aspx. Try adding a using for System.Data.Entity to make the compiler stop complaining. – Martin4ndersen Feb 15 '13 at 21:59
  • And there they are. Thanks again. Interesting that Intellisense will suggest "using" statements for unimported-but-available-in-a-referenced-assembly methods, but not if they're polymorphs of something already in your namespaces. – RedBrogdon Feb 15 '13 at 22:06