EF Core 中带有复合键的外键

CS-*_*ner 5 entity-framework ef-code-first entity-framework-core

我有以下课程:

    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; }
    }
Run Code Online (Sandbox Code Playgroud)

在原始情况下 Product 和 Attribute 是 AggregateRoot 所以我想通过属性引用跳过导航。Value 是一个简单的实体,但我需要它作为我的 ProductAttribute 类中的列表引用,因为您看到该类具有复合键。但我希望在 ProductAttribute 和 ProductAttributeValue 之间建立一个必需的级联删除关系。

这个项目是外部模块,所以我流畅的 API 配置是在目标应用程序 DbContext OnModelCreating 中调用的扩展。我应该配置每个属性和其他引用不起作用。

        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
        }
Run Code Online (Sandbox Code Playgroud)

您如何为 ProductAttribute 实体配置 Fluent API 以传递此场景?

Tan*_*jel 5

编写您的ProductAttribute配置如下:

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

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

    b.HasMany(pa => pa.Values).WithOne().IsRequired();
});
Run Code Online (Sandbox Code Playgroud)

但存在可读性问题。这会将列ProductAttributeProductIdProductAttributeAttributeId作为复合外键添加到表ProductAttributeValuesshadow 属性。如果您想让表中的复合外键ProductAttributeValues更具可读性,那么您可以ProductAttributeValue按如下方式更新您的模型模型类:

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

    public Guid ProductId { get; set; }

    public Guid AttributeId { get; set; }

    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

然后更新ProductAttribute配置如下:

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});
});
Run Code Online (Sandbox Code Playgroud)

现在表中的复合外键ProductAttributeValues将生成为ProductIdAttributeId

谢谢你。


Iva*_*oev 3

为了根据需要配置所需的关系并级联删除,您可以在ProductAttribute实体配置块中使用以下内容:

b.HasMany(e => e.Values)
    .WithOne()
    .IsRequired();
Run Code Online (Sandbox Code Playgroud)

IsRequired就足够了,因为按照惯例,级联删除对于必需关系是打开的,对于可选关系是关闭的。当然,.OnDelete(DeleteBehavior.Cascade)如果您愿意,您可以添加 - 这将是多余的,但不会造成伤害。

请注意,关系应在单个位置配置。ProductAttribute因此,请在或中执行此操作ProductAttributeValue,但切勿同时在两者中执行(容易出错,可能会导致意外的冲突或覆盖配置问题)。

为了完整起见,以下是如何配置相同的内部配置(由于缺少导航属性,ProductAttributeValue需要显式提供通用类型参数):HasOne

b.HasOne<ProductAttribute>()
    .WithMany(e => e.Values)
    .IsRequired();
Run Code Online (Sandbox Code Playgroud)