Efcore: one to one relationships that are not unique

Created on 30 Jan 2018  路  2Comments  路  Source: dotnet/efcore

Moved from https://github.com/aspnet/Home/issues/2838 filed by @andrew-hickton

Version: Entity Framework Core 2.0.1
Provider: SQL Server

I am finding that entity framework seems to want to ensure uniqueness for foreign key values where there is a one to one relationship. I have scenarios where uniqueness is not desired. The fabricated scenario below has an optional contact associated with an order. Both order 1 and order 2 should be saved with contact "John Smith" but order 1 ends up with a null value for OptionalContactId. Note also that EF will automatically create a unique index for OptionalContactId unless a non-unique index is explicitly created, but this seems to have no effect on the actual behavor when saving changes.

Is there a way to override this behavior for one to one relationships to have EF allow duplicate values for foreign key fields?

Note also that we have upgraded from EF Core 1.1.0 in recent months and I have only started to encounter this issue since upgrading to EF Core 2.0.1.

```C#
public class Contact
{
public int Id { get; set; }
public string ContactName { get; set; }
}

public class Order
{
    public int Id { get; set; }
    public string OrderNumber { get; set; }

    public int? OptionalContactId { get; set; }
    public Contact OptionalContact { get; set; }
}

public class TestContext : DbContext
{
    public DbSet<Order> Orders { get; set; }
    public DbSet<Contact> Contacts { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        => optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EfForeignKeys;Trusted_Connection=True;MultipleActiveResultSets=true");

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        var orderEntityBuilder = modelBuilder.Entity<Order>();

        orderEntityBuilder
            .HasKey(t => t.Id);

        orderEntityBuilder
            .Property(t => t.OrderNumber)
            .HasMaxLength(35);

        orderEntityBuilder
            .HasOne(t => t.OptionalContact)
            .WithOne()
            .OnDelete(DeleteBehavior.SetNull);

        orderEntityBuilder.HasIndex(t => t.OptionalContactId).IsUnique(false);   // Have to add this to ensure EF does not create a unique index

        var contactEntityBuilder = modelBuilder.Entity<Contact>();

        contactEntityBuilder
            .HasKey(t => t.Id);

        contactEntityBuilder
            .Property(t => t.ContactName)
            .HasMaxLength(35);
    }
}

class Program
{
    static void Main(string[] args)
    {
        using (var context = new TestContext())
        {
            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();

            if (!context.Orders.Any())
            {
                var contact = new Contact { ContactName = "John Smith" };
                context.Add(contact);
                context.Add(new Order { OrderNumber = "1", OptionalContact = contact });
                context.Add(new Order { OrderNumber = "2", OptionalContact = contact });
                context.SaveChanges();
            }
        }

        using (var context = new TestContext())
        {
            var orders = context.Orders.Include(o => o.OptionalContact);

            foreach (var order in orders)
            {
                Console.WriteLine($"Order: {order.OrderNumber}, Contact: {order.OptionalContact?.ContactName}");
            }
        }
    }
}

```
Output:
image

closed-question

All 2 comments

@andrew-hickton If each Contact can have more than one Order, then why would this be mapped as one-to-one instead of one-to-many?

@ajcvickers Ah sorry about that, you are correct it should be a one to many. A one line change to from WithOne() to WithMany() resolves the issue.

orderEntityBuilder
.HasOne(t => t.OptionalContact)
.WithMany()
.OnDelete(DeleteBehavior.SetNull);

Was this page helpful?
0 / 5 - 0 ratings