EF Core 2.0 Multiple one to one relationships

时间:2018-03-23 00:32:01

标签: entity-framework-core

I am trying to create a model that has a Customer entity with two references to Address entities: BillingAddress and ShippingAddress.

Customer

public class Customer
{
    public Guid CustomerId {  get;set;}

    public Guid? BillingAddressId { get; set; }
    public Address BillingAddress { get; set; }

    public Guid? ShippingAddressId { get; set; }
    public Address ShippingAddress { get; set; }
}

Address

public class Address
{
    public Guid AddressId { get; set; }

    public Customer Customer { get; set; }
    public Guid CustomerId { get; set; }
}

OnModelCreating

modelBuilder.Entity<Address>(eb =>
{
    eb.HasOne(e => e.Customer).WithOne(o => o.BillingAddress).OnDelete(DeleteBehavior.Cascade);
});

modelBuilder.Entity<Address>(eb =>
{
    eb.HasOne(e => e.Customer).WithOne(o => o.ShippingAddress).OnDelete(DeleteBehavior.Cascade);
});

I get the following error when trying to create the migration:

Cannot create a relationship between 'Customer.ShippingAddress' and 'Address.Customer', because there already is a relationship between 'Customer.BillingAddress' and 'Address.Customer'. Navigation properties can only participate in a single relationship.

I'm trying to configure the model so that when the Customer is deleted the referenced Addresses are deleted as well. I would like to be able to do this without loading the Addresses into the Context and relying on the database to cascade.

3 个答案:

答案 0 :(得分:2)

The relationship between an address and a customer is a many-to-one (determined by the foreign key CustomerId on Address). This means that you will need to specify a collection of Addresses on your customer entity.

public class Customer
{
    public Guid CustomerId { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Address> Addresses { get; set; }
}

So we need some other way to determine which address is for billing and which is for shipping:

public class Address
{
    public Guid AddressId { get; set; }
    public bool IsBillingAddress { get; set; }
    public bool IsShippingAddress { get; set; }
    public Guid CustomerId { get; set; }
    public virtual Customer Customer { get; set; }
}

Then, OnModelCreating you can do this to reference your addresses from the customers and add the cascaded delete behaviour:

modelBuilder.Entity<Customer>()
    .HasMany(c => c.Addresses)
    .WithOne(a => a.Customer)
    .OnDelete(DeleteBehavior.Cascade);

This then leaves us with one small additional problem - a customer can currently have multiple billing or multiple shipping addresses, so we need to add some form of constraint:

modelBuilder.Entity<Address>()
    .HasIndex(a => new { a.CustomerId, a.IsBillingAddress })
    .HasName("UQ_CustomerBillingAddress")
    .IsUnique(true)
    .HasFilter("IsBillingAddress = 1");

modelBuilder
    .Entity<Address>()
    .HasIndex(a => new { a.CustomerId, a.IsShippingAddress })
    .HasName("UQ_CustomerShippingAddress")
    .IsUnique(true)
    .HasFilter("IsShippingAddress = 1");

This will allow a customer to have as many addresses as you like, but there will be at most one billing address and one shipping address.

答案 1 :(得分:1)

我有两点。

  1. 您需要从地址中删除客户参考。
  2. 地址应返回客户的集合。
  3. 请参阅value object

答案 2 :(得分:1)

直接从这里开始,[InverseProperty]部分:https://docs.microsoft.com/en-us/ef/core/modeling/relationships

  

您可以使用数据注释来配置相关实体​​和主体实体的导航属性配对方式。当两个实体类型之间存在多对导航属性时,通常会执行此操作。

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }

    public int AuthorUserId { get; set; }
    public User Author { get; set; }

    public int ContributorUserId { get; set; }
    public User Contributor { get; set; }
}

public class User
{
    public string UserId { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }

    [InverseProperty("Author")]
    public List<Post> AuthoredPosts { get; set; }

    [InverseProperty("Contributor")]
    public List<Post> ContributedToPosts { get; set; }
}
相关问题