Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF4 code-first: defining object relationships, foreign keys

< RANT_MODE >

EF code-first approach is meant to save lots of time but for the time being I've only seen toy examples and spent hours trying to understand how I can make it generate the db I want. But still hoping that Eureka moment :-)

< RANT_MODE />

On to the questions!

Virtual vs. concrete properties

I'm trying to understand how EF maps and retrieves object relationships. When should I mark a property as virtual and when not? (As in public Person Owner { get; set; } vs. public virtual Person Owner { get; set; }.) In the dozens of examples of code-first I've seen they seem to use these interchangably, without much in terms of explanation. What I do know is that navigation properties (public virtual ICollection<Person> Owners { get; set; }) need to be virtual in order to make lazy loading possible (correct..?), but how does that apply in the world of non-collections?

Object relationships and foreign keys

I wasn't able to find any information on whether I should include a foreign key field (public int OwnerId { get; set; }) in addition to the 'main' property I'm interested in (public Person Owner { get; set; }). I tried not to, and EF kindly auto-added an int column named Owner_Id in my table, seemingly understanding what I meant to achieve.

In Conventions for Code First (section 'Foreign Keys') the EF Team mention that "it is common to include a foreign key property on the dependent end of a relationship", and that "Code First will now infer that any property named '' (i.e. OwnerId) [...] with the same data type as the primary key, represents a foreign key for the relationship". Ie. if I have both EF will know they're related.

But is it considered good practice to explicitly specify such properties holding FKs, in addition to 'foreign objects' themselves?

Foreign objects, foreign keys - continued

As I mentioned above, even if I only have public Person Owner { get; set; } in my object (say Event), the table Events will feature an Owner_Id column automagically added by EF. What's more, upon retrieval I will have access to properties of Owner.

However, consider the following scenario. I've got two classes:

public class Account
{
    public int Id { get; set; }
    public Person Owner { get; set; }
}

public class OpenIdAccount : Account
{
    public string Identifier { get; set; }
}

I want them to be TPT-related. This means manual mapping:

modelBuilder.Entity<Account>().MapHierarchy(a => new
{
    a.Id, 
    Owner_Id = a.Owner.Id
}).ToTable("Account");

modelBuilder.Entity<OpenIdAccount>().MapHierarchy(a => new
{
    a.Id,
    a.Identifier
}).ToTable("OpenIdAccount");

As you may notice, I tried to recreate what EF does with my Owner_Id column. Yet upon retrieval, myAccountInstanceFromDb.Owner is null. Why is that? How do I tell EF that it should do its magic and populate my Owner property?

Pointers, pointers

I will be most grateful if you could clarify the above - got to the point of really wishing to know the answers, but being unable to read yet another article that merely showcases another toy example of how easy it is to play with EF. That said, if you do have an in-depth up-to-date reference on EF's brains, please do post links too.

Thank you in advance for your time!

like image 822
Dav Avatar asked Nov 26 '10 01:11

Dav


People also ask

How do you create a foreign key relationship in code first approach?

To create Foreign Key, you need to use ForeignKey attribute with specifying the name of the property as parameter. You also need to specify the name of the table which is going to participate in relationship. I mean to say, define the foreign key table. Thanks for reading this article, hope you enjoyed it.

How do you set a foreign key in a one-to-one relationship?

Primary Key as Foreign Key One way to implement a one-to-one relationship in a database is to use the same primary key in both tables. Rows with the same value in the primary key are related. In this example, France is a country with the id 1 and its capital city is in the table capital under id 1.

Does Entity Framework support foreign keys?

When you change the relationship of the objects attached to the context by using one of the methods described above, Entity Framework needs to keep foreign keys, references, and collections in sync.

How do I add a foreign key in Fluent API?

You can then configure foreign key properties by using the HasForeignKey method. This method takes a lambda expression that represents the property to be used as the foreign key.


1 Answers

Virtual vs. Final properties:

This has really nothing to do with code first, it's the topic of EF and POCOs: When you have POCOs you lose a bunch of EF supports for your navigation properties and you can opt in to them by making them virtual. This lets EF to make a proxy at runtime and give you the support by overriding the nav properties in that proxy class. Those supports are Change Notification, Relationship Fixup and Lazy Loading.

Lazy Loading works the same for Collection and non Collection type navigation properties. Also it's considered to be a good practice to always mark your navigation properties as virtual.

Foreign Key Association or Independent Associations

EF3.5 does not support FKs in the associations and makes them hidden (a.k.a Independent Associations). EF4 starts to support FKs in the associations (a.k.a Foreign Key Association). Depend on which one you like, you can explicitly include or not include FK properties, but it's definitely a good practice to explicitly specify FK properties, in addition to navigation properties since it gives you ultimate flexibility to work with your objects.

Upon retrieval, myAccountInstanceFromDb.Owner is null. Why is that? How do I tell EF that it should do its magic and populate my Owner property?

Of course, you didn't marked it as virtual so Lazy Loading is not supported yet you did not explicitly eager load or defer load it as well. To resolve that either use virtual keyword and let EF lazy load it for you or use Include method to eager load it at the time the whole object is materialized.

Scalar properties vs. Navigation properties

Scalar properties are properties whose values are literally contained in the entity and correspond with the table columns (e.g. Account.Id).
Navigation properties are merely pointers to the related entities. For example the Account entity has an Owner property that will enable the application to navigate from an Account to the Owner that owns that Account.
So, back to your question, all you need to do is to specify a navigation property as virtual Person Owner and optionally specify a FK property as int OwnerId and you are good to go.

like image 140
Morteza Manavi Avatar answered Nov 07 '22 10:11

Morteza Manavi