views:

178

answers:

1

Through various questions I have asked here and other forums, I have come to the conclusion that I have no idea what I'm doing when it comes to the generated entity context objects in Entity Framework.

As background, I have a ton of experience using LLBLGen Pro, and Entity Framework is about three weeks old to me.

Lets say I have a context called "myContext". There is a table/entity called Employee in my model, so I now have a myContext.Employees. I assume this to mean that this property represents the set of Employee entities in my context. However, I assume wrong, as I can add a new entity to the context with:

myContext.Employees.AddObject(new Employee());

and this new Employee entity appears nowhere in myContext.Employees. From what I gather, the only way to find this newly added entity is to track it down hiding in the myContext.ObjectStateManager. This sounds to me like the myContext.Employees set is in fact not the set of Employee entities in the context, but rather some kind of representation of the Employee entities that exist in the database.

To add further to this confusion, Lets say I am looking at a single Employee entity. There is a Project entity that has a M:1 relationship with Employee (an employee can have multiple projects). If I want to add a new project to a particular employee, I just do:

myEmployee.Projects.Add(new Project());

Great, this actually adds the Project to the collection as I would expect. But this flies right in the face of how the ObjectSet properties off of the context work. If I add a new Project to the context with:

myContext.Projects.AddObject(new Project());

this does not alter the Projects set.

I would appreciate it very much if someone were to explain this to me. Also, I really want a collection of all the Employees (or Projects) in the context, and I want it available as a property of the context. Is this possible with EF?

Thanks.

+2  A: 

An ObjectSet is a query. Like everything in LINQ, it's lazy. It does nothing until you either enumerate it or call a method like .Count(), at which point a database query is run, and any returned entities are merged with those already in the context.

So you can do something like:

var activeEmployees = Context.Employees.Where(e => e.IsActive)

...without running a query.

You can further compose this:

var orderedEmployees = activeEmployees.OrderBy(e => e.Name);

...again, without running a query.

But if you look into the set:

var first = orderedEmployees.First();

...then a DB query is run. This is common to all LINQ.

If you want to enumerate entities already in the context, you need to look towards the ObjectStateManager, instead. So for Employees, you can do:

var states = EntityState.Added || EntityState.Deleted || // whatever you need
var emps = Context.ObjectStateManager.GetObjectStateEntries(states)
                                     .Select(e => e.Entity)
                                     .OfType<Employee>();

Note that although this works, it is not a way that I would recommend working. Typically, you do not want your ObjectContexts to be long-lived. For this, and other reasons, they are not really suitable to be a general-purpose container of objects. Use the usual List types for that. It is more accurate to think of an ObjectContext as a unit of work. Typically, in a unit of work you already know which instances you are working with.

Craig Stuntz
This is the idea I was started to get. Those properties on the context (like .Employees) are not there for state management, they are there for DB querying. The fact that they are there makes the context not look like a unit of work, but rather a data container of some kind. So, in an application, would you suggest creating and using my own data containers? For example an ObservableCollection<Employee> where I do my own change tracking?
Mike Gates
Let the `ObjectContext` do change tracking. But if you have a need for a list of active employees and you don't want to query the DB each time, by all means use a `List` or similar. Just realize that if the list outlives the context, you have to detach and attach before you can use the entity in an update, so often it's easier (and fast enough) to just query again. Cache policy is hard to get right.
Craig Stuntz
Right, so the List needs to be synchronized with the ObjectContext. So an add/delete from the list leads to an add/delete to the ObjectContext and vice-a-versa. Man, this is a pain to wire up...wish Microsoft had something like this built in.
Mike Gates
Again, if you're actually doing this, you have bigger problems. Reconsider the ObjectContext so that you're not using the same context for display and update. Again, think of it as a unit of work.
Craig Stuntz
This is for a WPF application. If the user does an add on one screen, a delete on another screen and a couple updates on another screen, then hits "Save", all these actions need to be tracked by a continuous unit of work (or else I need to create another custom change tracking mechanism). Therefore I need to keep the ObjectContext around between Saves. Therefore I need to keep this ObjectContext synchronized with my data containers (List of Employees, or whatever)...That is unless I am approaching this completely wrong (?).
Mike Gates
Use the MVC or MVVM pattern. Build up an edit model as the user goes through screens, then submit it all in a single update action. The context only lasts as long as the update, because that is your unit of work. Other units of work are populating each screen.
Craig Stuntz