Foreign key with composite key in EF Core

巧了我就是萌 提交于 2020-01-24 10:17:30

问题


I have the following classes:

    public class ProductAttribute 
    {
        public Guid ProductId { get; set; }

        public Guid AttributeId { get; set; }

        public List<ProductAttributeValue> Values { get; set; }

        public object[] GetKeys()
        {
            return new object[] {ProductId, AttributeId};
        }
    }

    public class Product
    {
        public Guid Id { get; set; }

        public string Name { get; set; }
    }

    public class Attribute
    {
        public Guid Id { get; set; }

        public string Name { get; set; }
    }

    public class ProductAttributeValue
    {
        public Guid Id { get; set; }

        public string Name { get; set; }
    }

In origin case Product and Attribute are AggregateRoot so I want skip navigate that by property references. Value is a simple entity but I need that as list reference in my ProductAttribute class as you see that class have composite key. But I want a required relationship with cascade delete between ProductAttribute and ProductAttributeValue.

This project is external module, so my fluent API configurations are extension which called in target app DbContext OnModelCreating. I should config every properties and references else didn't work.

        builder.Entity<ProductAttribute>(b =>
        {
            b.ToTable("ProductAttributes");

            b.HasKey(x => new {x.ProductId, x.AttributeId});

            //I should config ProductAttributeValue one-to-many manually here
        }

        builder.Entity<Product>(b =>
        {
            b.ToTable("Products");

            b.HasKey(x => x.Id);
        }

        builder.Entity<Attribute>(b =>
        {
            b.ToTable("Attributes");

            b.HasKey(x => x.Id);
        }

        builder.Entity<ProductAttributeValue>(b =>
        {
            b.ToTable("ProductAttributeValues");

            b.HasKey(x => x.Id);

            //I should config ProductAttribute many-to-one manually here
        }

How can you configure your Fluent API for ProductAttribute entity to passing this scenario?


回答1:


In order to configure the desired relationship as required and cascade delete, you can use the following inside the ProductAttribute entity configuration block:

b.HasMany(e => e.Values)
    .WithOne()
    .IsRequired();

IsRequired is enough because by convention cascade delete is on for required and off for optional relationships. Of course you can add .OnDelete(DeleteBehavior.Cascade) if you want - it will be redundant, but won't hurt.

Please note that the relationships should be configured in a single place. So do it either in ProductAttribute or ProductAttributeValue, but never in both (error prone, may cause unexpected conflicting or overriding configuration issues).

For completeness, here is how you can configure the same inside ProductAttributeValue configuration (requires explicitly providing the HasOne generic type argument due to the lack of navigation property):

b.HasOne<ProductAttribute>()
    .WithMany(e => e.Values)
    .IsRequired();



回答2:


Write your ProductAttribute configuration as follows:

modelBuilder.Entity<ProductAttribute>(b =>
{
    b.ToTable("ProductAttributes");

    b.HasKey(x => new {x.ProductId, x.AttributeId});

    b.HasMany(pa => pa.Values).WithOne().IsRequired();
});

But there is a concern of readability. This will add the columns ProductAttributeProductId and ProductAttributeAttributeId as composite foreign key to the table ProductAttributeValues for the shadow property. If you want to make composite foreign key in table ProductAttributeValues more readable then you can update your model ProductAttributeValue model class as follows:

public class ProductAttributeValue
{
    public Guid Id { get; set; }

    public Guid ProductId { get; set; }

    public Guid AttributeId { get; set; }

    public string Name { get; set; }
}

Then update the ProductAttribute configuration as follows:

modelBuilder.Entity<ProductAttribute>(b =>
{
    b.ToTable("ProductAttributes");

    b.HasKey(x => new {x.ProductId, x.AttributeId});

    b.HasMany(pa => pa.Values).WithOne().HasForeignKey(pa => new {pa.ProductId, pa.AttributeId});
});

Now composite foreign key in table ProductAttributeValues will be generated as ProductId and AttributeId.

Thank you.



来源:https://stackoverflow.com/questions/54441615/foreign-key-with-composite-key-in-ef-core

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!