Associations in EF Code First: Part 5 – One-to-One Foreign Key Associations

This is the fourth post in a series that explains entity association mappings with EF Code First. I've described these association types so far: In the third part of this series we saw the limitations of shared primary key association and argued that this type of association is relatively rare and in many schemas, a one-to-one association is represented with a foreign key field and a unique constraint. Today we are going to discuss how this is done by learning about one-to-one foreign key associations.

Introducing the Revised Model

In this revised version, each User always have two addresses: one billing address and another one for delivery. The following class diagram demonstrates the domain model:

One-to-One Foreign Key Association

Instead of sharing a primary key, two rows can have a foreign key relationship. One table has a foreign key column that references the primary key of the associated table (The source and target of this foreign key constraint can even be the same table: This is called a self-referencing relationship.). An additional constraint enforces this relationship as a real one to one. For example, by making the BillingAddressId column unique, we declare that a particular address can be referenced by at most one user, as a billing address. This isn’t as strong as the guarantee from a shared primary key association, which allows a particular address to be referenced by at most one user, period. With several foreign key columns (which is the case in our domain model since we also have a foreign key for DeliveryAddress), we can reference the same address target row several times. But in any case, two users can’t share the same address for the same purpose.

The Object Model

Let's start by creating an object model for our domain:
public class User
{
    public int UserId { getset; }
    public string Name { getset; }
    public int BillingAddressId { getset; }
    public int DeliveryAddressId { getset; }
        
    public Address BillingAddress { getset; }
    public Address DeliveryAddress { getset; }
}
 
public class Address
{
    public int AddressId { getset; }
    public string Street { getset; }
    public string City { getset; }
    public string ZipCode { getset; }
}
 
public class Context : DbContext
{
    public DbSet<User> Users { getset; }
    public DbSet<Address> Addresses { getset; }
}
As you can see, User class has introduced two new scalar properties as BillingAddressId and DeliveryAddressId as well as their related navigation properties (BillingAddress and DeliveryAddress).

Configuring Foreign Keys With Fluent API

BillingAddressId and DeliveryAddressId are foreign key scalar properties representing the actual foreign key values that the relationships are established on. However, Code First will not recognize them as the foreign keys for the associations since their names are not aligned with the conventions that it has to infer foreign keys. Therefore, we need to use fluent API (or Data Annotations) to let Code First know about the foreign key properties. The following fluent API code shows how:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>()
                .HasRequired(a => a.BillingAddress)
                .WithMany()
                .HasForeignKey(u => u.BillingAddressId);
 
    modelBuilder.Entity<User>()
                .HasRequired(a => a.DeliveryAddress)
                .WithMany()
                .HasForeignKey(u => u.DeliveryAddressId);
}
Alternatively, we can use Data Annotations to achieve this. EF 4.1 introduced a new attribute in System.ComponentModel.DataAnnotations namespace called ForeignKeyAttribute. We can place this on a navigation property to specify the property that represents the foreign key of the relationship:
public class User
{
    public int UserId { getset; }
    public string Name { getset; }
    public int BillingAddressId { getset; }
    public int DeliveryAddressId { getset; }
 
    [ForeignKey("BillingAddressId")]
    public Address BillingAddress { getset; }
 
    [ForeignKey("DeliveryAddressId")]
    public Address DeliveryAddress { getset; }
}
That said, we won't use this data annotation and will go with the fluent API way for a reason that you'll soon see.

Creating a SQL Server Schema

The object model seems to be ready to give us the desired SQL schema, however, if we try to create a SQL Server database from it, we will get an InvalidOperationException with this message:
The database creation succeeded, but the creation of the database objects did not. See InnerException for details.
The inner exception is a SqlException containing this message:
Introducing FOREIGN KEY constraint 'User_DeliveryAddress' on table 'Users' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. Could not create constraint. See previous errors.
As you can tell from the type of the inner exception (SqlException), it has nothing to do with EF or Code First; it has been generated purely by SQL Server when Code First was trying to create a database based on our object model.

What's a Multiple Cascade Path Anyway?

A Multiple Cascade Path happens when a cascade path goes from column col1 in table A to table B and also from column col2 in table A to table B. For example in our case Code First attempted to turn on cascade delete for both BillingAddressId and DeliveryAddressId columns in the Users table. In fact, Code First was trying to use Declarative Referential Integrity (DRI) to enforce cascade deletes and the problem is that SQL Server is not fully ANSI SQL-92 compliant when it comes to the cascading actions. In SQL Server, DRI forbids cascading updates or deletes in a multiple cascade path scenario.

A KB article also explains why we received this error:
"In SQL Server, a table cannot appear more than one time in a list of all the cascading referential actions that are started by either a DELETE or an UPDATE statement. For example, the tree of cascading referential actions must only have one path to a particular table on the cascading referential actions tree".
And it exactly applies to our example: The User table appeared twice in a list of cascading referential actions started by a DELETE from the Addresses table. Basically, SQL Server does simple counting of cascade paths and, rather than trying to work out whether any cycles actually exist, it assumes the worst and refuses to create the referential actions (cascades). Therefore, depend on your database engine, you may or may not get this exception.

Overriding The Code First Convention To Resolve the Problem

As you saw, Code First automatically turns on cascade delete on a required one-to-many association based on the conventions. However, in order to resolve the exception that we got from SQL Server, we have no choice other than overriding this cascade delete behavior detected by convention. Basically we need to switch cascade delete off on at least one of the relationships and as of EF 4.1, there is no way to accomplish this other than using fluent API. Let's switch it off on DeliveryAddress association for example:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>()
                .HasRequired(a => a.BillingAddress)
                .WithMany()
                .HasForeignKey(u => u.BillingAddressId);
 
    modelBuilder.Entity<User>()
                .HasRequired(a => a.DeliveryAddress)
                .WithMany()
                .HasForeignKey(u => u.DeliveryAddressId).WillCascadeOnDelete(false);
}

One-to-One Foreign Key Associations in EF Code First

As you may have noticed, both associations in the fluent API code has been configured as a many-to-one—not one-to-one, as you might have expected. The reason is simple: Code First (and EF in general) does not natively support one-to-one foreign key associations. In fact, EF does not support any association scenario that involves unique constraints at all. Fortunately, in this case we don’t care what’s on the target side of the association, so we can treat it like a to-one association without the many part. All we want is to express “This entity (User) has a property that is a reference to an instance of another entity (Address)” and use a foreign key field to represent that relationship. EF (of course) still thinks that the relationship is many-to-one. This is a workaround for the current EF limitation which comes with two consequences: First, EF won't create any additional constraint for us to enforces this relationship as a one to one, we need to manually create it ourselves. The second limitation that this lack of support impose to us is more important: one to one foreign key associations cannot be bidirectional (e.g. we cannot define a property for the User on the Address class).

Create a Unique Constraint To Enforce the Relationship as a One to One

We can manually create unique constraints on the foreign keys in the database after Code First creates it for us but if you are like me and prefer to create your database in one shot then there is a way to have Code First create the constraints as part of its database creation process. For that we can take advantage of the new EF 4.1 ExecuteSqlCommand method on Database class which allows raw SQL commands to be executed against the database. The best place to invoke ExecuteSqlCommand method for this purpose is inside a Seed method that has been overridden in a custom initializer class:
protected override void Seed(Context context)
{
    context.Database.ExecuteSqlCommand("ALTER TABLE Users ADD CONSTRAINT uc_Billing UNIQUE(BillingAddressId)");
    context.Database.ExecuteSqlCommand("ALTER TABLE Users ADD CONSTRAINT uc_Delivery UNIQUE(DeliveryAddressId)");
}
This code adds unique constraints to the BillingAddressId and DeliveryAddressId columns in the DDL generated by Code First.

SQL Schema

The object model is ready now and will result in the following database schema:
It is also worth mentioning that we can still enforce cascade deletes for the Delivery Address relationship. SQL Server allows enforcing referential integrity in two different ways. DRI that we just saw is the most basic yet least flexible way. The other way is to use Triggers. We can write a Delete Trigger on the primary table that either deletes the rows in the dependent table(s) or sets all corresponding foreign keys to NULL (In our case the foreign keys are Non-Nullable so it has to delete the dependent rows).

Source Code

Click here to download the source code for the one-to-one foreign key association sample that we have built in this post.

Summary

In this post we learned about one-to-one foreign key associations as a better way to create one to one relationships. We saw some limitations such as the need for manual creation of unique constraints and also the fact that this type of association cannot be bidirectional, all due to the lack of unique constraint support in EF. The good news is that the ADO.NET team is working on enabling unique constraints in EF but support for unique constraints requires changes to the whole EF stack which won't happen until the next major release of EF (EF 4.1 is merely layered on top of the current .NET 4.0 functionality) and until then the workaround that I showed here is going to be the way to implement one-to-one foreign key associations in EF Code First.
 

26 Comments

  • Nice article.

    But not able insert records.

    Getting error that u should provide values of parent table which is not null.

    Can you please tell me why this is happening.?

  • @Prajakta: Could you please show your code that causes the exception?

  • @Frank: Thanks. Could you please show your object model as well as the client code that adds a new UserImage to the database?

  • POCOs:
    public class User{&nbsp; &nbsp;&nbsp;&nbsp; public Guid UserId { get; set; } //have to use Guid as it maps to aspnet db&nbsp;&nbsp;&nbsp; public int CampaignId { get; set; }&nbsp;&nbsp;&nbsp; public virtual ICollection&lt;UserImage&gt; { get; set; }}
    public class UserImage{&nbsp;&nbsp;&nbsp; public int UserImageId { get; set; }&nbsp;&nbsp;&nbsp; public Guid UserId { get; set; }&nbsp;&nbsp;&nbsp; public int CampaignId { get; set; }}
    public class Campaign{&nbsp;&nbsp;&nbsp; public int CampaignId { get; set; }&nbsp;&nbsp;&nbsp; public virtual ICollection&lt;User&gt; Users { get; set; }}
    User Mapping:
    &nbsp;&nbsp;&nbsp; HasMany(u =&gt; u.UserImages)&nbsp;&nbsp;&nbsp; .WithOptional()&nbsp;&nbsp;&nbsp; .WillCascadeOnDelete();
    &nbsp;&nbsp;&nbsp; HasMany(u =&gt; u.UserCampaigns)&nbsp;&nbsp;&nbsp; .WithOptional()&nbsp;&nbsp;&nbsp; .WillCascadeOnDelete();
    Campaign Mapping:
    &nbsp;&nbsp;&nbsp; HasKey(c =&gt; c.CampaignId)&nbsp;&nbsp;&nbsp; .HasMany(c =&gt; c.Users)&nbsp;&nbsp;&nbsp; .WithMany(u =&gt; u.Campaigns)&nbsp;&nbsp;&nbsp; .Map(m =&gt; m.MapLeftKey("UserId"));
    No mapping for UserImage

  • Morteza,

    So the user object has a campaigns collection and in my case, it will contain one campaign in the collection. The UserImage has a property called CamapaignId. Would I have to set that manually? If so, any other way I can remodel my classes so the CampaignId gets set automatically when adding a UserImage?

  • @Frank: Your fluent API code doesn’t match your object model, but I still got the idea. Yes, adding a new UserImage to the UserImages collection wouldn’t cause UserImage.CampaignId to be populated since the association between User and UserImage has nothing to do with the association between UserImage and Campaign classes. In fact, I don’t see any reason to have yet another association between UserImage and Campaign entites by defining a CampaignId property on UserImage class because once you have a UserImage object you can simply access the Campaign information by accessing it on the related User (something like userImage.User.UserCampaign). Therefore, I would design the object model slightly different:



    public class User
    {&nbsp;
    &nbsp;&nbsp;&nbsp; public Guid UserId { get; set; }
    &nbsp;&nbsp;&nbsp; public int? CampaignId { get; set; }


    &nbsp;&nbsp;&nbsp; public virtual Campaign UserCampaign { get; set; }
    &nbsp;&nbsp;&nbsp; public virtual ICollection&lt;UserImage&gt; UserImages { get; set; }
    }



    public class UserImage
    {
    &nbsp;&nbsp;&nbsp; public int UserImageId { get; set; }
    &nbsp;&nbsp;&nbsp; public Guid UserId { get; set; }
    &nbsp;&nbsp;&nbsp; public User User { get; set; }
    }



    public class Campaign
    {
    &nbsp;&nbsp;&nbsp; public int CampaignId { get; set; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;



    public class Context : DbContext
    {
    &nbsp;&nbsp;&nbsp; public DbSet&lt;User&gt; Users { get; set; }
    &nbsp;&nbsp;&nbsp; public DbSet&lt;UserImage&gt; UserImages { get; set; }
    &nbsp;&nbsp;&nbsp; public DbSet&lt;Campaign&gt; Campaigns { get; set; }
    }



    And then you can work with the object model like the following code:



    using (var context = new Context())
    {
    &nbsp;&nbsp;&nbsp; UserImage userImage = new UserImage()
    &nbsp;&nbsp;&nbsp; {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; User = new User()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UserCampaign = new Campaign()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
    &nbsp;&nbsp;&nbsp; };



    &nbsp;&nbsp;&nbsp; context.UserImages.Add(userImage);
    &nbsp;&nbsp;&nbsp; context.SaveChanges();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    }




    You can also use the method I showed in this post to enforce a one to one relationship between User and UserCampaign entities. Hope this helps.

  • Thank you for taking the time. I got you and it makes sense assuming you only have 1:1 relationship. What I was trying to say (unsuccessfully) is that there could be possibilities of multiple campaigns. So it's actually many-to-many between user and campaign. So I think I'm left to setting the CampaignId manually. I even tried creating a dynamic property called ActiveCampaign which always returns the current campaign but it's useless and is not forcing the CampaignId to be set.

    I welcome any other thoughts.

  • Hello,

    I'm trying to use this approach to achieve a one-to-one association. In my case there's an association between a User and a Team, and I need a navigation property in each of them.

    I bumped into a problem when adding data.

    This is what the models look like:

    public class Team
    {
    &nbsp;&nbsp;&nbsp; public int ID { get; set; }
    &nbsp;&nbsp;&nbsp; public string Name { get; set; }
    &nbsp;&nbsp;&nbsp; public int OwnerID { get; set; }
    &nbsp;&nbsp;&nbsp; public virtual User Owner { get; set; }
    }
    public class User
    {
    &nbsp;&nbsp;&nbsp; public int ID { get; set; }
    &nbsp;&nbsp;&nbsp; public string UserName { get; set; }
    &nbsp;&nbsp;&nbsp; public int TeamID { get; set; }
    &nbsp;&nbsp;&nbsp; public virtual Team Team { get; set; }
    }

    I added these bits to the DBContext:

    modelBuilder.Entity&lt;User&gt;()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasRequired(u =&gt; u.Team)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .WithMany()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasForeignKey(u =&gt; u.TeamID);
    modelBuilder.Entity&lt;Team&gt;()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasRequired(t =&gt; t.Owner)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .WithMany()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasForeignKey(t =&gt; t.OwnerID)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .WillCascadeOnDelete(false);

    And now when adding data like this:

    u = new User();
    u.UserName = "farinha";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    t = new Team("Flour Power");
    u.Team = t;
    t.Owner = u;
    context.Users.Add(u);
    context.Teams.Add(t);
    context.SaveChanges();

    or even like this:

    u = new User();
    u.UserName = "farinha";
    u.Team = new Team("Flour Power");
    context.Users.Add(u);
    context.SaveChanges();

    I'm getting the following error:

    Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.

    Any idea of how to solve this? Or should I be adding the data in some different way?

    Thanks in advance

  • @asrfarinha: Of course it won’t work. Think about it, you are trying to add a new user which has a team, and the team itself is also new and this new team has the very same user as its owner. When you invoke SaveChanges(), EF tries to add these two new objects, now if it tries to add the User first, then it would need a TeamId to send along with other values, if it tries to add the Team object first in order to obtain a TeamId, then it would need an OwnerId which has to be obtained from the User record. Therefore, having&nbsp;two foreign keys that referencing each other table’s primary keys created a mutual dependency that would stop you from inserting related records in one single unit of work (i.e. a call to SaveChanges method). Although there are ways to solve this issue and proceed with the inserts but you should be aware that you haven’t really created a one-to-one FK association here. In fact, you’ve created two unidirectional one-to-many associations between User and Team which are totally unrelated and have nothing to do with each other. Basically if you need to have a bidirectional one-to-one association between your entities, then you should consider creating a shared primary key association instead&nbsp;since like I explained in the post, one-to-one foreign key associations cannot be bidirectional. Hope this helps.

  • Thanks for the reply. I believe I managed to solve this by making a few changes:
    http://stackoverflow.com/questions/5957515/entity-framework-saving-data-in-one-to-one-associations

    These one-to-one associations are somewhat weird and uncommon, and that's why I'm a bit lost as to how to implement this one. According to the sample of the model I described, do you reckon a shared primary key association would be a better option? Thanks.

  • @asrfarinha: Like I said, there are ways to get over this exception and save the dependent objects like the one that has been proposed on Stackoverflow but the main problem in essence that you can’t have a bidirectional association when creating a one-to-one FK relationship is still out there. Unfortunately you don’t have much of a choice here, if you need a one-to-one bidirectional association then shared primary key is the only type that EF currently supports and&nbsp;it is your only option. That being said, if your domain model allows at least one of the navigation properties to be read-only then there is a way to make your one-to-one FK association bidirectional. The trick is to replace the missing navigation property with a custom query. For example, let’s assume that you need to read/write the Owner information of a Team object (hence the Owner navigation property) but on the User side you only need to read the Team information of a User object: public class Team{&nbsp;&nbsp;&nbsp; public int ID { get; set; }&nbsp;&nbsp;&nbsp; public string Name { get; set; }&nbsp;&nbsp;&nbsp; public int OwnerID { get; set; }
    &nbsp;&nbsp;&nbsp; public virtual User Owner { get; set; }}
    public class User{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public int ID { get; set; }&nbsp;&nbsp;&nbsp; public string UserName { get; set; }
    &nbsp;&nbsp;&nbsp; [NotMapped]&nbsp;&nbsp;&nbsp; public Team Team { get; set; }}
    public class Context : DbContext{&nbsp;&nbsp;&nbsp; public Context()&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ((IObjectContextAdapter) this).ObjectContext.ObjectMaterialized += OnObjectMaterialized;&nbsp;&nbsp;&nbsp; }
    &nbsp;&nbsp;&nbsp; public DbSet&lt;Team&gt; Teams { get; set; }&nbsp;&nbsp;&nbsp; public DbSet&lt;User&gt; Users { get; set; }
    &nbsp;&nbsp;&nbsp; protected override void OnModelCreating(DbModelBuilder modelBuilder)&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modelBuilder.Entity&lt;Team&gt;()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasRequired(t =&gt; t.Owner)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .WithMany()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasForeignKey(t =&gt; t.OwnerID);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }
    &nbsp;&nbsp;&nbsp; private void OnObjectMaterialized(object sender, ObjectMaterializedEventArgs e)&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (e.Entity is User)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; User user = (User) e.Entity;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; user.Team = this.Teams.Single(t =&gt; t.OwnerID == user.ID);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp; }}As you can see above, every time you retrieve a User, the Team property gets populated at the time of object materialization. Hope you find this useful.

  • Uff... I'm getting even more confused as to what option should I follow.
    In practice, both the User and the Team entities will be created at the same time, never deleted, and a Team won't ever change user. There will be exactly one Team per User and exactly one User per team. I'll need to have a way to navigate to the Team from the User and vice-versa.

    I'm almost leaning towards having it all in the same table as the same entity, but it doesn't make a lot of sense from a OO point of view, if you're thinking about each object as a "real-world" Entity.
    And thinking forwards, maybe a User will be allowed to have more than one Team in the future (maybe). So having them in separate tables would be good if I ever decide to change.

  • @asrfarinha: If that’s the case then don’t create a one-to-one shared primary key. Your best bet would be to create a one-to-one FK association and make it bidirectional like the way I showed above. The only change you need to do is&nbsp;to have a real navigation property on User object (User.Team) and make the Owner property on Team object to be a read only query based navigation property since you said a Team would never change an Owner. If in the future the requirement for having more than one Team for a User comes up, then all you need to do is to drop the unique constraint on OwnerID and change User.Team to be of type ICollection&lt;User&gt; and your&nbsp;database schema&nbsp;would perfectly support that.

  • Great to see you finish this second series on the 4.1 release. Extremely helpful!

    I'm wondering how you might enforce a 1 to 0..1 relationship on the DB though? Consider a uni-directional relationship between Person and User, where a Person has public int? UserId and public User User (you cannot navigate from User to Person).

    EF maps this as a one-to-many, where technically in the DB a User can have 0..n People. In this case putting a unique index constraint on Person.UserId won't work, as the DB will throw a DbException as soon as you try and insert a second Person row with null UserId.

    Triggers? Or is there something easier?

  • @Dan: Great question! First of all, I should mention that this is a limitation of SQL Server&nbsp;as according to the ANSI standards SQL:92, SQL:1999, and SQL:2003, a Unique constraint should disallow duplicate non-NULL values, but allow multiple NULL values. In SQL Server however, a single NULL is allowed but multiple NULLs are not.
    There are a couple of workarounds to this problem but if you are using SQL Server 2008, then your best bet is to define a Unique Filtered Index based on a predicate that excludes NULLs.
    Consider the following as the object model for your scenario:



    public class User
    {
    &nbsp;&nbsp;&nbsp; public int UserId { get; set; }
    &nbsp;&nbsp;&nbsp; public string Username { get; set; }
    }

    public class Person
    {
    &nbsp;&nbsp;&nbsp; public int PersonId { get; set; }
    &nbsp;&nbsp;&nbsp; public string Name { get; set; }
    &nbsp;&nbsp;&nbsp; public int? UserId { get; set; }


    &nbsp;&nbsp;&nbsp; public User User { get; set; }
    }


    public class Context : DbContext
    {
    &nbsp;&nbsp;&nbsp; public DbSet&lt;User&gt; Users { get; set; }
    &nbsp;&nbsp;&nbsp; public DbSet&lt;Person&gt; Persons { get; set; }


    &nbsp;&nbsp;&nbsp; protected override void OnModelCreating(DbModelBuilder modelBuilder)
    &nbsp;&nbsp;&nbsp; {
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; modelBuilder.Entity&lt;Person&gt;()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasOptional(p =&gt; p.User)
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .WithMany()
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .HasForeignKey(u =&gt; u.UserId);
    &nbsp;&nbsp;&nbsp; }
    }



    And here is how we can create a filtered index to enforce an optional one-to-one relationship between User and Person:



    protected override void Seed(Context context)
    {
    &nbsp;&nbsp;&nbsp; context.Database.ExecuteSqlCommand("CREATE UNIQUE NONCLUSTERED INDEX idx_UserId_NotNULL ON People(UserId) WHERE UserId IS NOT NULL");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
    }


    Now you can have as many Persons as you want with a NULL UserId while it still disallows Persons with a same UserId. Thanks for your comment and hope this helps :)



  • @Fred: Those Stackoverflow threads discuss about shared primary key associations which I fully explained in the third part of this series. Like I described in that post, shared primary keys have a few limitations (like difficulty in saving related objects or the impossibility for having multiple dependent navigation properties) which don’t make them uncommon&nbsp;but relatively rare and in many schemas, a one-to-one association is represented with a foreign key field and a unique constraint which is the motivation behind the mapping described in this post. Please take a look at the part 3 of this series if you haven’t already. Hope this series help you choose the best mapping type for your one to one relationship scenarios :)

  • OK I see now. The the "shared" 1:1 scenario is where you have PK of one table related to PK of another table.

    This scenario is also 1:1 but PK of one table relates to FK of another table. And the approach is to model n:1 and then convert it to 1:1 in the db, using a unique constraint.

    This is what I don't get: I can model it as 1:1 and it still works for me? Unless I am doing something wrong. I can do this:

    1) .HasRequired(q => q.SomeEntity)
    .WithRequiredPrincipal() m.MapKey("SomeOtherEntityID"))
    .WillCascadeOnDelete(true);
    2) set unique constraint in db

    So I model a 1:1, and I exactly that, and I also get cascade deletes without using triggers. I trust your code, so I don't understand why mine is working? I am using EF4.1.

  • @Fred: Your fluent API code precisely creates an independent one-to-one association. In EF we usually create a 1:1 association by making the PK of the dependent entity to be also a FK of the principal entity, something we called a shared primary key association&nbsp;which you saw&nbsp;in the third part of this series. But let’s say you don’t like this and want to create a 1:1 association on a column other than the PK (and yet want to keep the association as a 1:1).&nbsp;EF would let you do that with one restriction which is you can't expose the FK property in your object model and that’s exactly what you did with your fluent API code. This of course works, but you have to be aware of 2 caveats: 1. If you look at your database you’ll see that while your object model showing a 1:1 association, the resulting relationship on the database is not, it’s a typical 1:* relationship and if you reverse engineer the generated database into an EDMX file, you’ll see that the relationship will be even picked up as a 1:* association by EF! Long story short, you still have to create&nbsp;a unique constraint on the FK (SomeOtherEntityID in your example) yourself, if you want to ensure&nbsp;your database consistency. 2. Like I mentioned, your code essentially creates an independent association as opposed to the&nbsp;FK association I have created in this article. Independent associations are the only type of relationship available in the first release of the EF and have been obsoleted&nbsp;ever since&nbsp;FK associations introduced in EF 4.0. The reason for that is because changing relationships between entities and concurrency checks was really difficult especially in N-Tier application scenarios when using independent associations. Therefore, the recommendation is to use this new foreign key association feature whenever possible. If you are using your fluent API code to have a 1:1 bidirectional association, as that’s the only benefit I can see with your mapping, then have a look at my answer to Asrfarinha above where I show a trick to make&nbsp;a one-to-one FK association bidirectional. Hope this helps.

  • All of this is confusing. I guess its because EF doesnt really support 1:1 FK relations.

    In my scenario i have a main class that has many related 1:1 classes.

    A 1-1 B
    A 1-1 C
    A 1-1 D
    C 1-* E

    what i need is to be able to have A.B .. and B.A .. so that i can do B.A.C.SomeProp += someval etc.


    At this point, i'm not 100% sure which direction i should take.

    Oh, and i'm working off an existing database and getting relations done properly is a nightmare when it should be simple. What i don't understand is why FK isnt enough. (I'm also thinking of using lists and having a property that takes the first element of the list for the many side which i want as a 1 side)

    There also seems to be some importance into how the entities are created

    A = new A() { B = new B() }
    context.AList.Add(A)
    may work but
    B = new B() { A = new A() }
    context.BList.Add(B)
    could fail.

    So there seems to be a logical way to set members that makes sense depending on how the relations are set which is not explained anywhere. At least i havent found somewhere explaining it.

    Is this something you plan on explaining at some point ?

  • @Michel C: You can use the trick I showed in this post to create 1:1 FK relationships between the entities in your model, hence avoiding “collection type navigation properties treated like a single object“.


    About saving a graph of related objects, it doesn’t really matter which object you pick to save your graph with, as long as the relations are set properly so I don’t see any reason why context.BList.Add(B) could ever fails. If you got an exception while trying to save the objects then can you please be more specific?

  • This helped me tackle the fluent api configuration for my model. Thank you very much!

  • Thanks and "Damet Garm" ;)

  • You are always doing optional your foreign keys via fluent api but why don't you mention that we can also achieve it via Nullable types. I feel that it is much better to make it nullable because model define itself more well without any configuration. Is there any downside to prefer nullable type for making foreign key optional ?

  • Yakup you want to read Fred's question and Morteza's answer. This 2 posts only could make another blog serie. Morteza thank you for the effort.

  • Awesome post. Thanks a ton!!!

  • I found this post very useful in understanding one-to-one relationships in EF CF. I too have an instance where Class X has two properties of type Class Y and I have followed the example here closely. At first, it all seemed to work - I get my not null on the columns, I get the correct Ids and these are correctly set. However, when performing an integration test to check that both properties have been set, I leave one as null and expect it to throw an exception. But it doesn't. Instead, it copies the values and id of the not-null version. So I end up with, for instance, the billing address and the delivery address the same if one is null which is not the intended behaviour.
    Has anyone else noticed this behaviour at all?
    Regards

Comments have been disabled for this content.