< 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!
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?
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?
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?
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!
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.
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.
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.
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.
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.
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 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With