EF Migrations series
- Using Entity Framework to Create a Database
- Adding Indexes with EF Migrations
- Updating a Table with EF Migrations
- Indexes in Code-Based EF Migrations
- EF Migrations and a Merge Conflict
- Prevent EF Migrations from Creating or Changing the Database
- EF Code First Change Tracking
- EF Migrations Command Reference
- EF Code First Navigation Properties and Foreign Keys
- Update-Database MSI Custom Action
An Entity Framework Code First class corresponding to a table with a foreign key typically has two fields for the foreign key. The foreign key as represented in the database and a C# reference.
[ForeignKey("BrandId")] public Brand Brand { get; set; } [ForeignKey("Brand")] public int BrandId { get; set; } |
This is a violation of the DRY principle. In C# there is nothing keeping the two properties in sync. We can set BrandId
to the Id of Saab and the Brand
reference to the Volvo object. If I do that, what will Entity Framework do when saving to the database? In this post I’ll investigate how Entity Framework handles the situation.
Updating the Foreign Key
For my first example I’ll use the Car
entity.
public class Car { public int CarId { get; private set; } [ForeignKey("BrandId")] public Brand Brand { get; set; } [ForeignKey("Brand")] public int BrandId { get; set; } [Required] [StringLength(6)] public string RegistrationNumber { get; set; } [Column("BodyStyle", TypeName = "int")] public CarBodyStyle BodyStyle { get; set; } public int? TopSpeed { get; set; } [Required] [StringLength(20)] public string Color { get; set; } } |
My first test is to update the foreign key. I’ve added debug outputs to the code to show exactly what happens.
The car has BrandId 7 pointing to Brand "Volvo" Setting BrandId to 8 The car has BrandId 8 pointing to Brand "Volvo" Saving Changes... The car has BrandId 8 pointing to Brand "Saab" |
As expected, setting the BrandId
won’t change the Brand
property. Before saving the item, the state is inconsistent. When saving, Entity Framework updates the database according to the changed key and also updates the Brand
property accordingly.
Updating the Navigation Property
My second test is to go the other way around, updating the navigation property. Will the unchanged BrandId
overrule the changed navigation property, or will Entity Framework let the property that was updated decide?
The car has BrandId 7 pointing to Brand "Volvo" Setting Brand to Saab The car has BrandId 7 pointing to Brand "Saab" Saving Changes... The car has BrandId 8 pointing to Brand "Saab" |
Entity Framework figures out that this time it is the navigation property that has changed and updates accordingly. That’s great.
Making a conflicting Update
My third test is a bit more cruel. I’ll update both to new values, but to point to different objects. How will Entity Framework handle the inconsistency?
The car has BrandId 7 pointing to Brand "Volvo" Setting Brand to Saab The car has BrandId 7 pointing to Brand "Saab" Setting BrandId to 9 The car has BrandId 9 pointing to Brand "Saab" Saving Changes... A first chance exception of type 'System.InvalidOperationException' occurred in System.Data.Entity.dll |
When saving the changes, an exception is thrown.
Conflicting changes to the role ‘Car_Brand_Target’ of the relationship ‘TestLib.Entities.Car_Brand’ have been detected.
That’s also great, aborting is the only reasonable alternative in this place. Having tested three cases so far, this post could end here with Entity Framework having passed the tests. However there is yet another alternative that I want to show. I’ve saved the best for last.
Entity Framework Dynamic Proxy in Action
I’ve previously shown how an Entity Framework Dynamic Proxy is automatically created and how it can improve change tracking. When running the tests above, I had to be careful to run them on a test class that does not meet the requirements for dynamic proxy objects. Let’s rerun the first test with another class, that do fulfil the requirements for dynamic proxies.
The person has GenderId 0 pointing to Gender "UnSpecified" Setting GenderId to 1 The person has GenderId 1 pointing to Gender "Male" Saving Changes... The person has GenderId 1 pointing to Gender "Male" |
With the proxy wrapped around our Gender
object the setting of GenderId
is intercepted and the Gender
navigation property is automatically updated. No more inconsistencies. The other way around works too, so whenever one of the properties are updated, the other one is updated too. I think that this feature makes a very clear case for always trying to conform to the proxy creation rules when working with code first. The proxies have been designed to intercept calls to the entity objects, providing extra services and added safety. Without them we are on our own, having to constantly be on guard against nasty bugs caused by inconsistencies. It’s not worth it. It’s better to make the entities compatible with the proxy requirements.
I’ve been with this problem since the day you made this post. Actually, that is the day when I started to work with EF.
I’ve made those test of yours but with diferent results. Changing the ID would change the navigation property but not the other way around. Is there a chance that you send me you codes via e-mail.
tks…
I just double checked that I indeed had tested both ways, and it works for me:
Are your properties
virtual
? Without that the EF proxy won’t be able to intercept the changes. These are my properties:I tried version 4.3.1 and EF5 Beta2. no go.
Would really help if you can post the complete working program if you have one.
This is a complete listing of the code involved in the test.
If this helps, please point out what information was missing in the original post so that I can add it.
Could you post the complete working solution please..
I just can’t get it to work the way you have described.
How did you manage to let the foreign key property immediately update the navigation property, before SaveChanges? I’ve seen this in my code, too, but only if ALL mapped properties were virtual, not just the two involved in this relationship. Also, what do you mean with “proxy”? When I call ToString on my code first entity classes, I get a lenghty string with the word “proxy” in it. Does that mean I have such proxies? Why are they not always working as expected? I haven’t disabled the change tracker or anything.