Associations in EF Code First CTP5: Part 2 – Shared Primary Key Associations

In the previous blog post I demonstrated how to map a special kind of one-to-one association—a composition with complex types as the first post in a series about entity association mapping with EF Code First. We argued that the relationships between User and Address are best represented with a complex type mapping and we saw that this is usually the simplest way to represent one-to-one relationships but comes with some limitations.

In today’s blog post I’m going to discuss how we can address those limitations by changing our mapping strategy. This is particularly useful for scenarios that we want a dedicated table for Address, so that we can map both User and Address as entities. One benefit of this model is the possibility for shared references— another entity class (let’s say Shipment) can also have a reference to a particular Address instance. If a User has a reference to this instance, as her BillingAddress, the Address instance has to support shared references and needs its own identity. In this case, User and Address classes have a true one-to-one association.

Introducing the Revised Model

In this revised version, each User could have one BillingAddress (Billing Association). Also Shipment has to be delivered to an address so it always has one Delivery Address (Delivery Association). Here is the class diagram for this domain model (note the multiplicities on association lines):
In this model we assumed that the billing address of the user is the same as her delivery address. Now let’s create the association mappings for this domain model. There are several choices, the first being a One-to-One Primary Key Association.

Shared Primary Associations

Also know as One-to-One Primary Key Associations, means two related tables share the same primary key values. The primary key of one table is also a foreign key of the other. Let’s see how we map the primary key associations with Code First.

How to Implement a One-to-One Primary Key Association with Code First

First, we start with the POCO classes. As you can see, we've defined BillingAddress as a navigation property on User class and another one on Shipment class named DeliveryAddress. Both associations are unidirectional since we didn't define related navigation properties on Address class as for User and Shipment.
public class User
{
    public int UserId { getset; }
    public string FirstName { getset; }
    public string LastName { getset; }
    public virtual Address BillingAddress { getset; }
}
 
public class Address
{
    public int AddressId { getset; }
    public string Street { getset; }
    public string City { getset; }
    public string PostalCode { getset; }
}
 
public class Shipment
{
    public int ShipmentId { getset; }
    public DateTime CreatedOn { getset; }
    public string State { getset; }
    public virtual Address DeliveryAddress { getset; }
}
 
public class EntityMappingContext : DbContext
{
    public DbSet<User> Users { getset; }
    public DbSet<Address> Addresses { getset; }
    public DbSet<Shipment> Shipments { getset; }
}

How Code First Reads This Object Model: One-to-Many

Code First reads the model and tries to figure out the multiplicity of the associations. Since the associations are unidirectional, Code First takes this as if one Address has many Users and Many Shipments and will create a one-to-many association for each of them. So, what we were hoping for —a one-to-one association, is not inline with the conventions.

How to Change the Multiplicity to One-to-One by Using the Conventions

One way to turn our associations to be one-to-one is by making them bidirectional. That is, adding a new navigation property to Address class of type User and another one of type Shipment. By doing that we basically signal Code First that we are looking to have one-to-one associations since for example User has an Address and also Address has a User. Based on the conventions, Code First will change the multiplicity to one-to-one and this will solve the problem.

Should We Make This Association Bidirectional?

As always, the decision is up to us and depends on whether we need to navigate through our objects in that direction in the application code. In this case, we’d probably conclude that the bidirectional association doesn’t make much sense. If we call anAddress.User, we are saying “give me the user who has this address”, not a very reasonable request. So this is not a good option. Instead we'll keep our object model as it is and will resort to fluent API.

How to Change the Multiplicity to One-to-One with Fluent API

The following code is all that is needed to make the associations to be one-to-one. Note how the multiplicities in the UML class diagram (e.g. 1 on User and 0..1 on address) has been translated to the flunet API code by using HasRequired and HasOptional methods:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>().HasOptional(u => u.BillingAddress)
                               .WithRequired();
    
    modelBuilder.Entity<Shipment>().HasRequired(u => u.DeliveryAddress)
                                   .WithOptional();
}
Also it worth mentioning that in CTP5, when we are mapping a one-to-one association with fluent API, we don't need to specify the foreign key as we would do when mapping a one-to-many association with HasForeignKey method. Since EF only supports one-to-one primary key associations it will automatically create the relationship in the database based on the primary keys and we don't need to state the obvious as we did in CTP4.

Database Schema

The mapping result for our object model is as follows (note the Identity column):

Referential Integrity

In relational database design the referential integrity rule states that each non-null value of a foreign key must match the value of some primary key. But wait, how does it even applies here? All we have is just three primary keys referencing each other. Who is the primary key and who is the foreign key? The best way to find the answer of this question is to take a look at the properties of the relationships in the database that has been created by Code First:
As you can see, Code First adds a foreign key constraint which links the primary key of the Addresses table to the primary key of the Users table and adds another foreign key constraint that links the primary key of the Shipments table to the primary key of the Addresses table. The foreign key constraint means that a user has to exist for a particular address but not the other way around. In other words, the database guarantees that an Addresses row’s primary key references a valid Users primary key and a Shipments row’s primary key references a valid Addresses primary key.

How Code First Determines Principal and Dependent?

Code First has rules to determine the principal and dependent ends of an association. For one-to-many relationships the many end is always the dependent, but it gets a little tricky in one-to-one associations. In one-to-one associations Code First decides based on our object model, and possible data annotations or fluent API that we may have. For example in our case, we wrote this fluent API code to configure User-Address association:
modelBuilder.Entity<User>().HasOptional(u => u.BillingAddress).WithRequired();
This reads as "User entity has an optional association with one Address object but this association is required for Address entity."
For Code First this is good enough to make the decision: It marked User as the principal end and Address as the dependent end in the association. Since we have the same fluent API code for the second association between Address and Shipment, it marks Address as the principal end and Shipment as the dependent end in this association as well.

The referential integrity that we saw, is the first result of this Code First's principal/dependent decision.

Second Result of Code First's Principal/Dependent Decision: Database Identity

If you take a closer look at the above DB schema, you'll notice that only UserId has a regular identifier generator (aka Identity or Sequence) and AddressId and ShipmentId does not. This is a very important consequence of the principal/dependent decision for one-to-one associations: the dependent primary key will become non-Identity by default. This make sense because they share their primary key values and only one of them can be auto generated and we need to take care of providing valid keys for the rest.

What about Cascade Deletes?

As we saw, each Address always belongs to one User and each Shipment always delivered to one single Address. We want to make sure that when we delete a User the possible dependent rows on Address and Shipment also get deleted in the database. In fact, this is one of the Referential Integrity Refactorings which called Introduce Cascading Delete. The primary reason we would apply "Introduce Cascading Delete" is to preserve the referential integrity of our data by ensuring that related rows are appropriately deleted when a parent row is deleted. By default, Code First does not enable cascade delete when it creates a relationship in the database. As always we can override this convention with fluent API:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>().HasOptional(u => u.BillingAddress)
                               .WithRequired()
                               .WillCascadeOnDelete();
    
    modelBuilder.Entity<Shipment>().HasRequired(u => u.DeliveryAddress)
                                   .WithOptional()
                                   .WillCascadeOnDelete();
}

What If Both Ends are Required?

We saw that the only reason Code First could figure out principal and dependent in our 1:1 associations was because our fluent API code clearly specified one end as Required and the other as Optional. But what if both endpoints are the same in terms of being required in the association? For example what if in our domain model, User always has one Address and Address always has one User (required on both end)? The answer is that ultimately, this scenario need to be configured by fluent API and the interesting point is that fluent API is designed in a way that will force you to explicitly specify who is dependent and who is principal in such cases that this cannot be inferred by Code First.

To illustrate the idea, let's see how we can configure mapping for this User-Address association (Required/Required) with fluent API:
modelBuilder.Entity<User>().HasRequired(u => u.BillingAddress).WithRequiredDependent();
So we invoke WithRequiredDependent() after HasRequired() method. To see the reason, we need to take a look at the RequiredNavigationPropertyConfiguration type which is returned by HasRequired():
public class RequiredNavigationPropertyConfiguration<TEntityType, TTargetEntityType>
{
    public DependentNavigationPropertyConfiguration<TEntityType, TTargetEntityType> WithMany();
    public CascadableNavigationPropertyConfiguration WithOptional();
    public CascadableNavigationPropertyConfiguration WithRequiredDependent();
    public CascadableNavigationPropertyConfiguration WithRequiredPrincipal();
}
As you can see, if you want to go another Required after HasRequired() method, you have to either call WithRequiredDependent() or WithRequiredPrincipal() since there is no WithRequired() method on this RequiredNavigationPropertyConfiguration class which is returned by HasRequired() method.
Both WithRequired and WithOptional methods return a CascadableNavigationPropertyConfiguration type which has a WillCascadeOnDelete() method. Now if we run the code and check the database, we'll see that cascade delete on both relationships are switched on.

Working with the Model

Here is an example for adding a new user along with its billing address. EF is smart enough to use the newly generated UserId for the AddressId as well:
using (var context = new EntityMappingContext())
{
    Address billingAddress = new Address()
    {   
        Street = "Yonge St.",
        City   = "Toronto"                    
    };
    User morteza = new User()
    {                    
        FirstName      = "Morteza",
        LastName       = "Manavi",
        BillingAddress = billingAddress
    }; 
            
    context.Users.Add(morteza);                        
    context.SaveChanges();                
}
The following code is an example of adding a new Address and Shipment for an existing User (assuming that we have a User with UserId=2 in the database):
using (var context = new EntityMappingContext())
{
    Address deliveryAddress = new Address()
    {
        AddressId = 2,
        Street    = "Main St.",
        City      = "Seattle"
    };
    Shipment shipment = new Shipment()
    {
        ShipmentId      = 2,
        State           = "Shipped",
        CreatedOn       = DateTime.Now,
        DeliveryAddress = deliveryAddress
    };                
               
    context.Shipments.Add(shipment);
    context.SaveChanges();      
}

Limitations of This Mapping

There are two important limitations to associations mapped as shared primary key:
  • Difficulty in saving related objects: The main difficulty with this approach is ensuring that associated instances are assigned the same primary key value when the objects are saved. For example, when adding a new Address object, it's our responsibility to provide a unique AddressId that is also valid (a User can be found with such a value as UserId.)

  • Multiple addresses for User is not possible: With this mapping we cannot have more than one Address for User. At the beginning of this post, when we introduce our model, we assumed that the user has the same address for billing and delivery. But what if that's not the case? What if we also want to add a Home address to User for the deliveries? In the current setup, each row in the User table has a corresponding row in the Address table. Two addresses would require an additional address table, and this mapping style therefore wouldn’t be adequate.

Summary

In this post we learned about one-to-one associations which shared primary key is just one way to implement it. Shared primary key associations aren’t uncommon but are relatively rare. In many schemas, a one-to-one association is represented with a foreign key field and a unique constraint. In the next posts we will revisit the same domain model and will learn about other ways to map one-to-one associations that does not have the limitations of the shared primary key association mapping.

References

27 Comments

  • Great article! I can&#39;t get enough of code first. I have a problem and have been looking for a solution, it&#39;s with an e-commerce database that has a Product table and a Category table, it also has a ProductCategory table with columns ProductID (ASC), CategoryID (ASC),
    FK_ProductCategory_Category


    Tables And Columns Specification:


    Foreign Key Base Table: &nbsp; ProductCategory


    Foreign Key Columns: &nbsp;CategoryID


    Primary/Unique Key Base Table: Category


    Primary/Unique Key Columns: &nbsp; CategoryID


    And the same for ProductID. My problem is how do you work with such a table in code first?

  • Where was this post 7 hours ago when I was moving my repo to cpt5!??? Great info and immediately applicable in real world aps.

  • Interesting post !

    Took me a moment to grok what you're doing here. The db-schema made me scratch my head. (You explained it later though)

    Linking UserId to AddressId 'looks' wrong to me. I would at least rename AddressId to UserId, because that's what it really is, but that's just my opinion.

    Just discovered your blog through Twitter, looking forward for your next posts !

  • @Designation_One: In database design, a one-to-one relationship on primary keys usually represents Entity Splitting (splitting a single entity into two or more tables), in that scenario choosing one name for the primary keys makes the best sense and is recommended (e.g. Individual and Customer tables in AdventureWorks DB where both have a CustomerID column as their PK). However, we’re dealing with a different situation here: Users, Addresses and Shipments tables are mapping back to three completely different entities that just happen to share their primary key values in this particular type of mapping. Like I said in the post, shared primary key associations are relatively rare and a one-to-one relationship is usually represented with a foreign key field and a unique constraint. Thanks :)

  • @ rickj1: Thanks! About your question, it’s a classic model of a many-to-many association. Code First maps this by creating a join table (i.e. ProductCategory) that has a one-to-many relationship with each Product and Category tables. I will explain this association type in my future posts but for now an object model like this will do the trick: public class Product { &nbsp; &nbsp;public int ProductId { get; set; } &nbsp; &nbsp;public virtual ICollection&lt;Category&gt; Categories { get; set; } } public class Category { &nbsp; &nbsp;public int CategoryID { get; set; } &nbsp; &nbsp;public virtual ICollection&lt;Product&gt; Products { get; set; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }

  • Having AddressId and ShipmentId keys might be confusing. Why not to call them just UserId?

    User ( UserId, FirstName, LastName )
    Address ( UserId, Street, City, PostalCode )
    Shipment ( UserId, CreatedOn, State )

  • Is there a way to make make one-to-one unidirectional association work with conventions? For example by adding the following properties to Address entity:

    protected virtual User User { get; set; }
    protected virtual Shipment Shipment { get; set; }

  • @Koistya `Navin: If you add navigation properties for User &nbsp;and Shipment &nbsp;to Address type then your associations are going to be bidirectional and yes, Code First will change the multiplicity to one-to-one like I explained in the post under the title “How to Change the Multiplicity to One-to-One by using the Conventions”. If you want to keep your associations unidirectional then fluent API is the only way to make them one-to-one. Regarding your first question, choosing different names for primary keys are intentional. Please read my reply to Designation_One above. Thanks :)

  • Know this is just an example to illustrate how, but one other limitation must be that a user can only have 1 shipment, or am I missing something.
    Great post though, looking forward to this coming out of ctp

  • First of all thank you for this great post that you covered a very common real-world scenario having shared primary keys for one-to-one relationships.
    My question is, when one design a database with such association, it is also very common to set cascade delete operation in database itself. So to say, when I delete a User, it automatically deletes the Address, and that deletion of Address cascades to Shipment. Is this automatically handled or will it throw foreign key violation exception? If it is not handled automatically is there a way to tell this to model binder? Thanks.

  • @Yusuf Demirag: In CTP5, by default Code First does not enable cascade delete when it creates a relationship in the database. &nbsp;As a result, if you try to delete a User, you’ll get a SQLException because of the foreign key constraint violation if an Address holds a reference to that particular User. We can enable cascade delete by chaining WillCascadeOnDelete() method at the end of our fluent API code. I’ve added a new section to the post and explained how to do this. Please find it under the title “What about Cascade Deletes?”. Thanks for your great question!

  • @Steve: Like I explained in the post under the Referential Integrity title, the database guarantees that there is always one user exists for an address and also one address always exists for a Shipment because of the foreign key constraints that links the primary keys. Therefore, if you have a Shipment, it holds a reference to exactly one Address and that particular Address always holds a reference to one User. Hence, a User can only have one Shipment. Thanks :)

  • I'm wondering if it's possible to do entity splitting in the following scenario.

    class {Id; ItemId; ItemName; ItemDisplayName}

    MainTable {Id, ItemId, ItemName}
    OptionalTable{ ItemId, ItemDisplayName}

    Some of the items have optional display names that need to be used if present. In SQL term, it's MainTable left join OptionalTable on ItemId, so you have something like this

    Id ItemId ItemName ItemDisplayName
    1 aaa NameA
    2 bbb NameB DisplayNameB
    3 ccc NameC

    How do I map the class to the two underlying table? Thanks

  • @whoever: Yes, EF Code First fully supports Entity Splitting. In CTP5, you can make use of the EntityMappingConfiguration class that you access through the Map() method to split an entity across multiple tables: public class FooItem { &nbsp;&nbsp;&nbsp;public int Id { get; set; } &nbsp; &nbsp;public string ItemId { get; set; } &nbsp; &nbsp;public string ItemName { get; set; } &nbsp; &nbsp;public string ItemDisplayName { get; set; } } public class CTP5Context : DbContext { &nbsp; &nbsp;public DbSet&lt;FooItem&gt; FooItems { get; set; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;protected override void OnModelCreating(ModelBuilder modelBuilder) &nbsp; &nbsp;{ &nbsp; &nbsp; &nbsp; &nbsp;modelBuilder.Entity&lt;FooItem&gt;() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.Map(m =&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;m.Properties(p =&gt; new &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;p.Id, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ItemId = p.ItemId, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;p.ItemName &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;m.ToTable("MainTable"); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.Map(m =&gt; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;m.Properties(p =&gt; new &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;{ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;p.Id, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ItemId = p.ItemId, &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;p.ItemDisplayName &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;m.ToTable("OptionalTable"); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;}); &nbsp; &nbsp;} }

  • To be accurate, because all my tables have different name than the classes, so my mapping is like this
    .Map(m =&gt;{&nbsp;&nbsp;&nbsp; m.Properties(p =&gt; new&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A_Id = p.Id,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A_ItemId = p.ItemId,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; A_ItemName = p.ItemName&nbsp;&nbsp;&nbsp; });&nbsp;&nbsp;&nbsp; m.ToTable("MainTable");&nbsp;&nbsp;&nbsp; HasKey(x=&gt; x.Id);})
    .Map(m =&gt;{&nbsp;&nbsp;&nbsp; m.Properties(p =&gt; new&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B_ItemId = p.ItemId,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; B_ItemDisplayName = p.ItemDisplayName&nbsp;&nbsp;&nbsp; });&nbsp;&nbsp;&nbsp; m.ToTable("OptionalTable");&nbsp;&nbsp;&nbsp; HasKey(x=&gt; x.ItemId);});&nbsp;
    Not sure if any of these extras cause the problem. &nbsp;I was hoping this will generate the equivalent of
    SELECT FROM MainTable LEFT JOIN OptionaTable ON ItemId

  • @whoever: What you are trying to accomplish is impossible with Entity Splitting. You should be aware that even though you end up getting&nbsp;two tables in this mapping, you still have one single entity (FooItem) which&nbsp;only can have one primary key&nbsp;and not two (i.e. ItemId and Id). In other words, when mapping an entity to two different tables, you must map the same primary key for both tables (Of course you can rename the primary key column in the second table but it still has to refer the same primary key property). It also worth noting that EF uses INNER JOIN (and not LEFT JOIN) to read the&nbsp;split entity from the database. For example, EF submits the following SQL statements to the database as a result of this query: &nbsp;context.FooItems.ToList();&nbsp;SELECT [Extent1].[Id] AS [Id], [Extent2].[ItemId] AS [ItemId], [Extent2].[ItemName] AS [ItemName], [Extent1].[ItemDisplayName] AS [ItemDisplayName]FROM&nbsp; [dbo].[OptionalTable] AS [Extent1]INNER JOIN [dbo].[MainTable] AS [Extent2] ON [Extent1].[Id] = [Extent2].[Id]&nbsp; Therefore, Entity splitting is not meant to be used for&nbsp;your particular&nbsp;scenario. Your desired schema would be best achieved by a one-to-one foreign key association which involves two separate entities. Hope this helps :)

  • I'm trying to use shared primary key one-to-one association in our project. In our tests we have FixupE table with required reference to FixupA table via primary key sharing. Entity classes have bidirectional relationship properties.private class ConfigurationE : EntityTypeConfiguration&lt;FixupE&gt; { public ConfigurationE() { HasKey(e =&gt; e.Id); Property(e =&gt; e.Id) .HasDatabaseGenerationOption(DatabaseGenerationOption.None) .HasColumnName("id"); HasRequired(e =&gt; e.A) .WithOptional(a =&gt; a.E); ToTable("FixupE", "dbo"); } }
    However, EF treats FixupE as having "FixupAId" column (int not null) referencing FixupA table instead of using shared primary key ("id").

  • Ah, got it. We removed OneToOneConstraintIntroductionConvention. Should such a thing be a convention really?

  • @Ihar Bury: Good question! Let me clarify it. I agree that having a convention like OneToOneConstraintIntroductionConvention doesn’t make a good sense given that EF only supports Shared Primary Keys for one-to-one associations. However, you have to be aware that this is just one way for creating a 1:1 association and EF team is actively working on a new feature that will enable us to create 1:1 associations in another (better) way which is called One-to-One Foreign Key Associations. Now let’s assume the RTM version ships with this One-to-One&nbsp; Foreign Key association support and Shared Primary Keys still remains as the default mapping for one-to-one associations. As a result, we have to use fluent API every time we want to create a Foreign Key association for our one-to-one relationships. &nbsp;In this case, being able to&nbsp;override this convention by&nbsp;removing this OneToOneConstraintIntroductionConvention would be really helpful. Hope this helps :)

  • First of all, great article. Just starting on code-first and loving it. I have a problem similar to one you already answered. In the example you gave (below) there are two tables Product and Category. My problem is that I'm trying to map with tables that have prefixes but the classes don't. So in the example you answered, my table names would be tblProduct and tblCategory so I have to use the ToTable() method to map them. It's trying to create a FK relationaship called ProductCategory and it doesn't use my mapping table to make the association.
    @ rickj1: Thanks! About your question, it’s a classic model of a many-to-many association. Code First maps this by creating a join table (i.e. ProductCategory) that has a one-to-many relationship with each Product and Category tables. I will explain this association type in my future posts but for now an object model like this will do the trick: public class Product { &nbsp; public int ProductId { get; set; } &nbsp; public virtual ICollection&lt;Category&gt; Categories { get; set; } } public class Category { &nbsp; public int CategoryID { get; set; } &nbsp; public virtual ICollection&lt;Product&gt; Products { get; set; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }

  • @Victor Ponce: Using the ToTable method to customize Product and Category table names wouldn’t stop Code First to create the join table to map the many to many association between them but you can still customize the join table name and columns using fluent API like the following: public class Product{&nbsp;&nbsp;&nbsp; public int ProductId { get; set; }&nbsp;&nbsp;&nbsp; public virtual ICollection&lt;Category&gt; Categories { get; set; }} public class Category{&nbsp;&nbsp;&nbsp; public int CategoryId { get; set; }&nbsp;&nbsp;&nbsp; public virtual ICollection&lt;Product&gt; Products { get; set; }} public class Context : DbContext{&nbsp;&nbsp;&nbsp; public DbSet&lt;Product&gt; Products { get; set; }&nbsp;&nbsp;&nbsp; public DbSet&lt;Category&gt; Categories { get; set; } &nbsp;&nbsp;&nbsp; protected override void OnModelCreating(ModelBuilder modelBuilder)&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modelBuilder.Entity&lt;Product&gt;().ToTable("tblProduct");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modelBuilder.Entity&lt;Category&gt;().ToTable("tblCategory"); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modelBuilder.Entity&lt;Product&gt;().HasMany(p =&gt; p.Categories).WithMany(c =&gt; c.Products).Map(c =&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c.MapLeftKey(p =&gt; p.ProductId, "ProductId");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c.MapRightKey(p =&gt; p.CategoryId, "CategoryId");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; c.ToTable("tblProductCategory");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });&nbsp;&nbsp;&nbsp; }} That said, I’m not sure what you are exactly trying to achieve but if this does not answer your question, then please post your desired database schema and I’ll create a Code First object model to match that for you. Thanks :)

  • Hey!

    GRRRRRRRRRRRRRRR...

    This got me confused.. I got I'm trying to add a new entity as an item in a collection but its failing miserably

    I got an entity Session with an ICollection&lt;Discussion&gt; Discussions{get;set;}

    how do I add a new discuss object?

    I'm getting a PK violation? cannot understand why

  • @bob: If you show your object model as well as the code that throws the exception then I will have a better idea of the reason you get an exception while trying to save a new discuss object. Thanks.

  • I'm trying to configure one-to-one association.
    I have an Apartment class and a Contract class.
    public class Apartment{&nbsp;&nbsp;&nbsp; public int ID { get; set; }&nbsp;&nbsp;&nbsp; public virtual Contract Contract { get; set; }}
    public class Contract{&nbsp;&nbsp;&nbsp; public int ID { get; set; }}&nbsp;&nbsp;&nbsp;&nbsp;
    I've tried using
    modelBuilder.Entity&lt;Apartment&gt;()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasOptional(u =&gt; u.Contract)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .WithRequired();
    But the contract is always null and the query performs 2 left joins.
    SELECT[Extent1].[ID] AS [ID]...FROM&nbsp;&nbsp; [dbo].[Apartments] AS [Extent1]LEFT OUTER JOIN [dbo].[Contracts] AS [Extent2] ON [Extent1].[ID] = [Extent2].[ID]LEFT OUTER JOIN [dbo].[Contracts] AS [Extent3] ON [Extent2].[ID] = [Extent3].[ID]
    What am I missing and how can I change the mapping name for the association to be
    [Contracts] AS [Extent3] ON [Extent1].[ContractID] = [Extent3].[ID]
    10x,
    Sagi.

  • I ahve a similar one to one relationship. the problem happens when updating entities of the table with the foregn key by calling:
    dbcontext.Entry(Product).State = EntityState.Modified;
    this returns "Make sure that the key values are unique".

    what do you recommend?

  • @TarekShawadfy: I couldn’t repro the exception you are getting; you have duplicate keys, most probably in some of your associations, which isn't allowed. That's all I can say without seeing your code. Any chance you could post your complete code here? You can also send it to me at bmanavi@gmail.com, if it’s a project.

  • I'd like to know how to do the associations above with data annotations instead of using the fluent API, is that possible?

Comments have been disabled for this content.