Side effects of first class Associations


You’ve probably all heard someone say that Entity Data Model (EDM) and more specifically the Entity Framework (EF) treat associations as first class concepts.

This sentence, seems reasonably benign, but it has a profound effect on the Entity Framework’s behavior.

Allow me to illustrate by way of an example:

Imagine you want to model a Product/Category relationship in the database. A Product belongs to a category, which is usually modeled as a non-nullable FK from Product to Category, and typically looks something like this:

ProductCategory_DB

Now if you want to create a new Product that belongs to an existing category, no problem, you just do one insert, which includes the FK value.

This may seem incredibly obvious but it really is the crux of the whole matter. So bear with me.

When viewed in Entity Framework the above data model will look like this:

ProductCategory_EF

Notice that there is no CategoryID in the model. Instead we have a strongly typed Category (Navigation) property layered over the Association.

Now if you want to create a new Product in an existing Category in EF you do something like this:

Product p = new Product();
p.Name = “Bovril”;
p.Category = ctx.Categories.FirstOrDefault(
                   c => c.Name == “Food”
            
);
ctx.SaveChanges();

 

When we call SaveChanges() conceptually the Entity Framework does two inserts here. One to create the new product and one to create an association that relates that new product to the existing category.

 

Conceptually it is the association insert that sets the CategoryID in the database. In fact there is nothing in the conceptual model (aka EDM, aka CSDL) that indicates that this could be done with just one insert.


Without the mapping information we just don’t know how the Association is modeled.


However once mapping information is available the Entity Framework can and does compensate where appropriate.In this particular example the Entity will recognize that only one insert is required.


This typically means that for the most part you the end user are unaware of the way associations are treated by the Entity Framework.


Like all abstractions however, sometimes it leaks, and those leaks can occasionally cause some problems in end user code.


More on that next time.

Comments (9)

  1. Meta-Me says:

    Problem The most common way to delete an Entity in the Entity Framework is to pull the Entity you want

  2. Martin Laufer says:

    Hi Alex,

    "Imagine you want to model a Product/Category relationship in the database. A Product belongs to a category, which is usually modeled as a non-nullable FK from Product to Category, and typically looks something like this:"

    I think, one should state the problem another way. If one wants to model the Product-Category relationship then one has 3(!) conceptual things:

    the Product entity, the Category entity and the relationship between them.

    Any of those 3 things will be modeled as a table in the database. One will have a Product table, a Category table and a Product_has_Category table. (That is, what E-R is all about) Between the tables there are FK-constraints ensuring integrity (Note: FK-constraints do NOT model relationships! They are about integrity and can be applied only in trivial cases. Nontrivial cases ensure integrity with triggers/SP…)

    (Note: there are NO foreign keys in the entity tables! This is conceptually the case, as entities do NOT depend on each other)

    The fact that every Product MUST have a category is not modeled yet, but that is not an integrity constraint but rather a business constraint.

    In the case of the multiplicity of the Product_has_Category relationship being n:1 the cardinality of the Product table is the same as the cardinality of the Product_has_Category table. ONLY in that case one can do an OPTIMIZATION to reduce the overall number of tables. The relationship does not disappear, but is incorporated into the Product table. (Note: this is not conceptually, but result of an optimization) The reltionship is actually modeled by the non-functional dependency between Product.ID and Product.CategoryID and NOT by the FK-constraint. (Note: the business constraint that every product must have a category can now be modeled via the Not-Null constraint on the CategoryID)

    As we have a business constraint that a Product must have a Category, conceptually 2 inserts are nessesary. This is because the constraint requires an entity instance and a relationship instance as well.

    EF will detect the optimization and ensure, that the number of calls to the database is minimized.(Great!)

    BTW. premature optimization should be avoided. Those optimizations are (one of) the root cause of being unable to derive a conceptual model from a database…

    Thanks for Your patience

    Best regards

    Martin

  3. Alex D James says:

    Martin,

    Yeap you obviously really get this stuff… you are also preaching to the choir somewhat, I get it too 🙂

    I know you can model the relationship independently in the database too.

    The reason I didn’t talk about that scenario though is most of our customers don’t think that way! They think in terms of FKs in the Dependent Table.

    Anyway it is great to see at least one person outside our team understands the Independent Association model completely!!!

    Alex

  4. Meta-Me says:

    The Entity Framework is pretty big, so when the Entity Framework team talks about things internally we

  5. doberman8u says:

    Thanks for the post…it has helped me quite a bit.  However, could you please explain how one would go about populating p.Category using a returned value from a DropDownList?

    Assuming your DropDownList id is "ddlFood", I was under the impression that it would look as follows:

    Product p = new Product();

    p.Name = "Bovril";

    p.Category = ctx.Categories.Where(

        "it.CategoryID = @myFood",

        new ObjectParameter("myFood",

        this.ddlFood.SelectedValue)).First();

    ctx.SaveChanges();

    I feel like I am very close, but am getting an error:

    "The argument types ‘Edm.Int32’ and ‘Edm.String’ are incompatible for this operation., near WHERE predicate"

    Any help is appreciated and thanks for your posts.

  6. Alex D James says:

    why not try this:

    int categoryID = Int32.Parse(this.ddlFood.SelectedValue);

    p.Category = ctx.Categories.First(c => c.CategoryID == categoryID);

    That should do it.

    Another way to do this that avoids the query on the database is to set the CategoryReference directly, see this post for more:

    http://blogs.msdn.com/alexj/archive/2009/03/25/tip-7-faking-foreign-key-properties-in-net-3-5-sp1.aspx

  7. doberman8u says:

    Alex, Thank you very much for your response…that absolutely worked.

    I also appreciate the link you provided and will study it to find out how to accomplish returning this without the additional call to the db.

  8. VS2010学习 says:

    The Entity Framework is pretty big, so when the Entity Framework team talks about things internally we

  9. VS2010学习 says:

    Problem The most common way to delete an Entity in the Entity Framework is to pull the Entity you want

Skip to main content