Foreign Keys in the Entity Framework

Published 16 March 09 09:47 PM | efdesign 

Background

A number of months ago we asked whether Foreign Keys (FKs) in Conceptual and Object models were important.

The feedback we got indicated that there are a real mix of opinions on the topic. Some people are all for them while others think that FKs pollute the conceptual model.

The fact that our customers were so divided meant we thought it was important to provide options here. Those who want FKs in their Entities should be able to do so, so they can gain all the benefits that having FKs in your Entities undoubtedly provide. On the other hand customers who are concerned that have FKs in their Entities in someway pollutes their model can continue to use the type of associations we had in .NET 3.5 SP1.

In the past we've called the .NET 3.5 SP1 style Associations "first class" associations. Which while accurate somehow implies that associations based on FKs in Entities are "second class". In fact when we talked about this new work recently with our MVPs one of the first questions was "are these second class?", which they most definitely are not.

The fact is these new associations are different but first class nevertheless.

So what's been added?

In .NET 4.0 we will add support for a new type of association called "FK Associations".

The way this works is we will allow you to include your FK columns in your entities as "FK properties", and once you have FK Properties in your entity you can create an "FK Association" that is dependent upon those properties.

Okay so we will have "FK Associations", what are we going to call the older style associations? Obviously saying something like ".NET 3.5 SP1 style Associations" every time isn't ideal. So we are going to call them "Independent Associations".

The term "Independent Association" resonates for us because they are independently mapped, whereas FK Associations need no mapping, simply mapping the Entity(Set) is sufficient.

How do I use an "FK Association"?

The real reason so many customers and partners are asking for "FK Associations" is that they significantly simplify some key coding patterns. So much so that we are convinced that for most people "FK Associations" will be an automatic choice going forward.

Some of the things that are hard with "Independent Associations" are trivially easy using FK Associations. Scenarios as diverse as DataBinding, Dynamic Data, Concurrency Control,  ASP.NET MVC Binders, N-Tier etc are all positively impacted.

Let's have a look at some code snippets that will work with "FK Associations":

1) Create a new Product and FK Association to an existing Category by setting the FK Property directly:

using (var context = new Context())
{
    //Create a product and a relationship to a known category by ID
    Product p = new Product
    {
        ID = 1,
        Name = "Bovril",
        CategoryID = 13
    }; 
    //Add the product (and create the relationship by FK value)
    context.Products.AddObject(p);
    context.SaveChanges();
}

This sort of approach works both for insert and update, and is particular useful for things like databinding, where you often have the new Value of the FK in a grid or something but you don't have the corresponding object, and you don't want to wear the cost of a query to pull back the principal object (i.e. the Category).

2) Create a new Product and a new FK Association to an existing Category by setting the reference instead:

public void Create_new_Product_in_existing_Category_by_reference()
{
    using (var context = new Context())
    {
        //Create a new product and relate to an existing category
        Product p = new Product
        {
            ID = 1,
            Name = "Bovril",
            Category = context.Categories
                        .Single(c => c.Name == "Food")
        };
        // Note: no need to add the product, because relating  
        // to an existing category does that automatically.
        // Also notice the use of the Single() query operator 
        // this is new to EF in .NET 4.0 too.
        context.SaveChanges();
    }
}

This programming pattern is not new to the Entity Framework, you could do this in .NET 3.5 SP1. I called it out because it shows you can still write the sort of code you write with Independent Associations even when using FK Associations.

3) Update an existing Product without informing the Entity Framework about the original value of the CategoryID (not supported with Independent Associations):

public void Edit(Product editedProduct)
{
    using (var context = new Context())
    {
        // Create a stand-in for the original entity
        // by just using the ID. Of the editedProduct
        // Note: you don't have to provide an existing Category or CategoryID
        context.Products.Attach(
            new Product { ID = editedProduct.ID });

        // Now update with new values including CategoryID
        context.Products.ApplyCurrentValues(editedProduct);
        context.SaveChanges();
    }
}

In this example "editedProduct" is a product which has been edited somewhere, this is exactly the sort of the code you might write in the Edit method of a ASP.NET MVC controller, and is a great improvement over the code you have to write using Independent Associations.

There is much more you can do with FK Associations, but these 3 samples give you a flavor of the sort of coding patterns FK Associations allow.

Keeping FKs and References in Sync

One thing that hasn't been mentioned so far is that the Entity Framework tries to keep related References and FKs in sync as much as possible.

When this synchronization occurs depends upon when the Entity Framework is notified of changes, which depends upon the type of Entity Classes involved: be they POCO with Proxies, POCO without Proxies, IPOCO or EntityObjects etc.

Another post will cover this in more detail.

How do I create an "FK Association"?

There are a number of ways.

One mainline scenario is when using the tools to infer a model from a database. We have added an option to choose whether "FK Associations" or "Independent Associations" are generated by default. The same is true if you use EdmGen.exe (i.e. our command line tool).

The second mainline scenario for creating FK Associations is creating a model from scratch, aka Model First which is new to .NET 4.0.

Here are the steps involved:

  1. Create two Entities (say Product and Category) that look something like this:

    ProductCategoryStep1
  2. Add a property that will be the FK Property: i.e. CategoryID

    ProductCategoryStep2
  3. Create an association between the two EntityTypes with the correct end multiplicities and NavigationProperty names:

    ProductCategoryStep3
  4. Double Click the line between the two Entities, that represents the Association, and add a Referential Integrity Constraint:

    ProductCategoryStep4
    This is the step that tells the Entity Framework that the CategoryID is an "FK Property" and that the "CategoryProduct" association is an "FK Association".

  5. You end up with something that looks like this:

    ProductCategoryStep5 

And you are done you've set up an FK Association. Notice that this association doesn't need to be mapped you simply need to make sure you map all the properties of both Entities.

Very easy.

Summary

In .NET 4.0 we will add support for FK Properties and FK Associations to the Entity Framework. The existing style Independent Associations will still be possible, but we expect that FK Associations will become most peoples automatic choice because they simplify so many common Entity Framework coding tasks.

As always we'd love to hear your thoughts on this work.

Alex James,
Program Manager, Entity Framework Team, Microsoft.

This post is part of the transparent design exercise in the Entity Framework Team. To understand how it works and how your feedback will be used please look at this post.

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

# Click & Solve » Foreign Keys in the Entity Framework said on March 16, 2009 5:00 PM:

PingBack from http://www.clickandsolve.com/?p=23814

# DotNetShoutout said on March 16, 2009 7:33 PM:

Thank you for submitting this cool story - Trackback from DotNetShoutout

# Michael Hart said on March 16, 2009 10:52 PM:

Code snippet #3, with the editedProduct doesn't seem to make much sense to me.

Where does newProduct come from? Shouldn't you just attach the editedProduct directly (which will have the updated Category/ID in it)?

# Alex James said on March 16, 2009 11:05 PM:

Michael,

there was a typo... it was suppose to be editedProduct...

Let me know if it still doesn't make sense

Cheers

Alex

# KristoferA said on March 17, 2009 12:00 AM:

I'm with Michael here - why not context.Products.Attach(editedProduct); ?

# cravier said on March 17, 2009 5:47 AM:

Really good news !

I had to manage this on current EF release and it gave me a big headhache to keep FK and references sync.

I still have some problems (setting FKs in constructor for example) but I'll now wait for next release instead of adding some code in my framework.

Chris

# efdesign said on March 17, 2009 1:01 PM:

Kristofer and Michael,

The idea is to create a fake entity and attach that, to save the query to the database. And then to modify that with the values, including the FK value, in editedProduct with a call to ApplyCurrentValues().

At this point the EF knows the new state of the product and knows that it is modified.

Does that make sense?

Alex

# KristoferA said on March 18, 2009 12:23 AM:

But you can do that by attaching the existing editedProduct instance too.

It is a basic n-tier scenario; get object, serialize/disconnect, do stuff, re-attach, update. Much cleaner if it can be done in one step IMO...

# Mike Borozdin said on March 18, 2009 5:05 PM:

Alex,

Good work! I'm really happy that the new version of the Entity Framework will a lot better and easy to use than the current version.

Currently, I'm working with the version in .NET 3.5 SP1 it provides a really great level of abstraction, but sometimes it's difficult to write code mainly because of absense of foreign keys in the conceptual model.

# Mike Borozdin said on March 18, 2009 5:29 PM:

Многие, кто сейчас работают с Entity Framework, жалуются на отсутствие внешних к

# shawn said on March 20, 2009 3:51 PM:

Great to hear this, it will make dealing with foreign keys much easier!

# Jon said on March 23, 2009 2:44 PM:

Does anyone know if Entity Framework will be usable in .NET 4? Or, is this blog just a bunch of hot air for another Microsoft ORM that will never see the light of day? What about generating the database schema from the model? Is that going to be supported? Why can't Microsoft get this stuff right and somehow open source projects like NHibernate are able to figure it out? I don't know why EF was even included in .NET 3.5.

# AlexJ said on March 23, 2009 5:57 PM:

Jon,

Generating a database from a model is a supported scenario in the next version of the Entity Framework (.NET 4.0) see the post on "Model First" on this blog.

As for your other concerns and questions. we know we didn't get everything right in .NET 3.5, but we are listening to what our customers have been saying and are doing our best to rectify the situation for .NET 4.0, which will be a major improvement.

Other than "model first" have you got any specific concerns with the first version of the Entity Framework? If so I'd love to hear from you. My email is alexj at microsoft dot com.

Cheers

Alex

# Jaroslaw Kowalski said on March 23, 2009 10:42 PM:

I’d like to turn your attention to two blog posts on EF Design Blog about exciting new features planned

# Y2Kstephen said on March 24, 2009 10:25 PM:

GREAT !!!

i can't wait any longer for .NET 4.0 !!!

# Meta-Me said on March 24, 2009 10:43 PM:

Background If you've been reading the EF Design Blog you will have seen that we recently announced something

# Meta-Me said on March 27, 2009 1:38 AM:

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

# Les news d'Aspectize said on March 31, 2009 2:54 AM:

Cet article a pour objectif de décrire notre approche à propos de l'accès aux données. En préambule

# Meta-Me said on April 3, 2009 12:41 AM:

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

# Meta-Me said on April 16, 2009 3:27 PM:

Background A while back I was writing a web app to try the Entity Framework and MVC together. I knew

# Baum said on May 9, 2009 5:41 PM:

Are there any plans for Microsoft to include EF 2.0 support for databases other than SQL Server such as Oracle or DB2. I know that there is a 3rd part provider that allows EF 1.0 to work with Oracle, but it would be nice if Microsoft provided support out-of-the-box.

# Daniel Simmons said on May 14, 2009 10:33 AM:

@Baum,

There are not plans for Microsoft to ship EF providers for databases outside of the SQL family--except for some work that the Host Integration Server team is doing around DB2.  For databases such as Oracle, we will depend on 3rd parties (or Oracle itself) to create those providers.

- Danny

# Alexander Walker said on June 14, 2009 12:17 PM:

I'm using vs2010 beta 1. I created a .net 4.0 console project, added an empty ADO.NET Entity Model, then performed the steps detailed above for FK Associations for the Model First scenario. I found that I could not make the foreign key association in step 4, the CategoryID was not in the Dependant Property dropdown and typing it in didn't work either, is this a bug or did I miss something out?

# divega@microsoft.com said on June 14, 2009 12:41 PM:

@Alexander,

Beta 1 doesn’t actually include support for Foreign Key Associations. You can expect the feature to be there in the next publically available release.

Thanks,

Diego

# Paul said on June 18, 2009 3:17 PM:

Thanks for the interesting information.

Will it be possibly for the EF to generate the FK properties automatically? If you have a huge data model this will be a big hassle.

Maybe, I'm a special case because we have our own data layer, but I'm fundamentally interested in having classes generated that can be mapped directly onto the underlying data columns.

Currently, I've been trying to figure out how to have the current entity framework completely ignore foreign key associations. Anybody know if this is possible?

Thanks,

Paul

# Meta-Me said on June 19, 2009 2:00 PM:

What are Stub Entities? A stub entity is a partially populated entity that stands in for the real thing.

# VS2010学习 said on June 19, 2009 10:36 PM:

What are Stub Entities? A stub entity is a partially populated entity that stands in for the real thing

# VS2010学习 said on June 21, 2009 2:30 PM:

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

# VS2010学习 said on June 21, 2009 2:31 PM:

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

# Dmitry said on July 11, 2009 1:36 AM:

I think a better way to go about this would be to be able to create entity proxies that only have the ID/primary key properties populated. Once you access another property, it would load the entity data from the database. This way you can assign relations without hitting the database or redundant properties.

# Fedir said on September 14, 2009 1:42 PM:

I've tried to use the "FK Association" in the VS2010 Beta1 and DataServices CTP2 release - it doesn't  work as were outlined in this article. You can't create an FK association on the dependent object property if its not a part of the entity key!? Why this was changed and how can I work with the "FK association" now?

# Daniel Simmons said on September 14, 2009 3:50 PM:

#Fedir,

This wasn't "changed".  This feature just hasn't been released yet.  FK Associations will be in beta 2 of VS2010, but it hadn't yet made it into the product as of beta 1.  Sorry, but you have a little while to wait yet.

- Danny

# Andrey said on October 12, 2009 3:03 AM:

If I've not missed something, so please note that IObjectSet in Beta1 contains small amount of possible methods from ObjectSet, so the scenario #3 could not be covered unit tests (by mocking ObjectSet) at all because of using ApplyCurrentValues method. Is it possible to enlarge IObjectSet interface with that methods or introduce new interface for that.

Thanks.

# Aquilax said on October 13, 2009 1:20 AM:

I hope this will also improve the performance of the Data Services. Actually the Data Services doesn't include any information about related object / foreign keys, to have them you have to $expand the relation, what sometimes generate only a bigger response and it is useless.

For example I want to load a product, on the client I have already loaded all the available product categories, so I really don't need to $expand the product category relation to have it but if I don't do it I don't have it.

Now if my product have 6 different relations to some mapping tables (which I have already loaded on the client) and I show 20 products per page, the Data Services includes to each request 20x6=120 completely useless informations, which make only the response bigger and worse the performance of the application.

Instead of the foreign key could have also been used a "ghost" entity, which contains only the primary key. A "ghost" entity can interpreted as "this entity exists but it has not yet materialized". So loading entities from the DB without including referenced entities will generated "ghost" entities for all the relations which are not null.

So for example to change the category of a product could be done by just assigning a new "ghost" entity to the product:

//Create a "ghost" entity with a static method

product.Category = Category.Ghost(3);

//Create a "ghost" entity with a regular constructor

product.Category = new Category(true){Id=3};

Moreover it would be nice if "ghost" entity could also been materialized, this means load the entity from the DB:

//Loading a product without including the relations

Product product = context.Product.Where(p => p.Id==3).First();

//Materializing the category relation = loading it

product.Category.Materialize();

# Jason said on October 28, 2009 8:06 PM:

Hi Alex,

Scenario 1 represents the greatest improvement among the scenarios presented. Please continue to strive for this level of simplicity.

I have to agree with Kristofer and Michael regarding scenario 3 though. This is another convoluted solution for something that has an obviously simple solution. I get your explanation of the scenario. I don't get why these steps couldn't be placed within the attach method itself. Doing so would increase the value of the EF because it would make the EF more intuitive. As it is, I look at it, scratch my head, and hope its a typo. Just like I did when reading about the use/necessity of EntityKeys. Simplicity is king.

This may seem like a goofy recommendation, but I think the EF team would benefit from hiring an information architect/interface designer (not a Comp Sci grad and not someone who bleeds MS). Someone who can bridge the gap between the users and the EF team. Just a suggestion.

I think the product shows huge potential and I want it to be successful. Mostly because I want to spend more time building apps and less time programming/struggling with the eccentricities of my tools.

Thanks for hearing me out...

- Jason

Leave a Comment

(required) 
(optional)
(required) 

  
Enter Code Here: Required

Search

This Blog

Syndication

Page view tracker