'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!
Solution 1:[1]
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.
Solution 2:[2]
marking a property virtual makes the related objects to be lazy invoked
you need not add a foreign key field public Person Owner { get; set; } will add a foreign key mapping
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Rony |