Entity Framework STEs and many-To-many associations

372 Views Asked by At

I'm fairly new to EF and STE's, but I've stumbled on a painful point recently, and I'm wondering how others are dealing with it...

For example, suppose I have two STE's: Employee and Project. It's a many-to-many relationship. Each entity has a navigation property to the other (i.e. Employee.Projects and Project.Employees).

In my UI, a user can create/edit an Employee and associate it with multiple Projects. When the user is ready to commit, a list of Employees is passed to the server to save. However, if an Employee is not added to the "save list" (i.e. it was discarded), but an association was made to one or more Projects, the ApplyChanges extension method is able to "resurrect" the Employee object because it was "connected" to the object graph via the association to a Project.

My "save" code looks something like this:

 public void UpdateEmployees(IEnumerable<Entities.Employee> employees)
 {
     using (var context = new EmployeeModelContainer(_connectionString))
     {
        foreach (var employee in employees)
        {
           context.Employees.ApplyChanges(employee);
        }

        context.SaveChanges();
     }
 }

I've been able to avoid this issue to now on other object graphs by using FKs to manipulate associations as described here: http://blogs.msdn.com/b/diego/archive/2010/10/06/self-tracking-entities-applychanges-and-duplicate-entities.aspx

How does one handle this when a many-to-many association and navigation properties are involved?

Thanks.

1

There are 1 best solutions below

0
On

While this answer's a year late, perhaps it will be of some help to you (or at least someone else)

The simple answer is this: do not allow Entity Framework to infer m:m relationships. Unfortunately, I'm not aware of a way of preventing this, only how to deal with it after the fact.

By default, if I have a schema like this:

Employee        EmployeeProject    Project
-----------     ---------------    ----------
EmployeeId ---> EmployeeId    |--> ProjectId
Name            ProjectId -----    Name
...                                ...

Entity Framework will see that my EmployeeProject table is a simple association table with no additional information (for example, I might add a Date field to indicate when they joined a project). In such cases, it maps the relationship over an association rather than an entity. This makes for pretty code, as it helps to mitigate the oft-referenced impedence mismatch between a RDBMS and object-oriented development. After all, if I were just modeling these as objects, I'd code it the same way, right?

As you've seen, however, this can cause problems (even without using STE's, which cause even MORE problems with m:m relationships). So, what's a dev to do?

(The following assumes a DATABASE FIRST approach. Anything else and you're on your own)

You have two choices:

  • Add another column to your association table so that EF thinks it has more meaning and can't map it to an association. This is, of course, bad design, as you presumably don't need that column (otherwise you'd already have it) and you're only adding it because of the particular peculiarities of the ORM you've chosen. So don't.

  • After your context has been generated, map the association table yourself to an entity that you create by hand. To do that, follow the following steps:

    1. Select the association in the designer and delete it. The designer will inform you that the table in question is no longer mapped and will ask you if you want to remove it from the model. Answer NO
    2. Create a new entity (don't have it create a key property) and map it to your association table in the Mapping Details window
    3. Right-click on your new entity and add an association
    4. Correct the entity and multiplicity values (left side should have your association entity with a multiplicity of *, right should have the other entity with a multiplicity of 1)
    5. Check the option that says "Add foreign key properties to the Entity"
    6. Repeat for the other entity in the association
    7. Fix the property names on the association entity (if desired...not strictly necessary but they're almost certainly wrong) and map them to the appropriate columns in the Mapping Details window
    8. Select all of the scalar properties on your association entity and set them as EntityKey=True in the Properties window

Done!