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.
答案 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)
我有两点。
请参阅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; }
}