Entity Framework 核心不支持具有多对多关系的通用抽象实体

Entity Framework Core not supporting generic abstract entities with many to many relationships

我遇到了一个奇怪的问题 EF Core 1.1。我正在尝试构建可以标记某些实体的应用程序,因此我为关系 table 列表创建了一个抽象通用 class。问题是,似乎 EF 不支持具有 FK(Id 属性 有效)的通用抽象 classes。

以下是模型:

public abstract class TaggedEntityBase<T> : EntityBase
{
    public ICollection<T> EntityTags { get; set; }
    public List<Tag> Tags { get { return EntityTags?.Select(x => x.Tag).ToList(); } }
}

public class AddressTag
{
    public long TagId { get; set; }
    public Tag Tag { get; set; }
    public long EntityId { get; set; }
    public Address Entity { get; set; }
}

public class Address : TaggedEntityBase<AddressTag>
{
    public string Street { get; set; }
    public string City { get; set; }
}
public class Tag : EntityBase
{
    public string Name { get; set; }
    public virtual ICollection<AddressTag> AddressTags { get; set; }
}

模型构建器映射:

public DbSet<Address> Addresses { get; set; }
    public DbSet<AddressTag> AddressTag { get; set; }
    public DbSet<Tag> Tags { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<AddressTag>()
            .ToTable("AddressTag");

        modelBuilder.Entity<AddressTag>()
            .HasKey(t => new { t.EntityId, t.TagId });
        modelBuilder.Entity<AddressTag>()
        .HasOne(pt => pt.Entity)
            .WithMany(p => p.EntityTags)
            .HasForeignKey(p => p.EntityId);
        modelBuilder.Entity<AddressTag>()
        .HasOne(pt => pt.Tag)
            .WithMany(p => p.AddressTags)
            .HasForeignKey(p => p.TagId);

    }

EF 尝试获取标签时出错

An unhandled exception of type 'System.Data.SqlClient.SqlException' occurred in Microsoft.EntityFrameworkCore.dll Additional information: Invalid column name 'AddressId'.

我什至没有那个 Id 约定。

注意:当我明确地将 public ICollection<AddressTag> EntityTags { get; set; } 放置在 Address POCO 中时,它可以正常工作,也包括 EntityTags.Tag。 感谢您的帮助:)

此问题与通用 and/or 抽象基础实体 classes 无关。

首先,为了编译您的示例模型,我添加了以下 classes

public abstract class EntityBase
{
    public long Id { get; set; }
}

public abstract class EntityTagBase
{
    public long TagId { get; set; }
    public Tag Tag { get; set; }
}

修改AddressTagclass如下:

public class AddressTag : EntityTagBase
{
    public long EntityId { get; set; }
    public Address Entity { get; set; }
}

并向 TaggedEntityBase<T> class 添加了 where T : EntityTagBase 约束以允许 Tag 属性 访问器进入 Select(x => x.Tag).

到目前为止一切顺利。生成的迁移的 Tag 相关部分如下所示:

migrationBuilder.CreateTable(
    name: "Tags",
    columns: table => new
    {
        Id = table.Column<long>(nullable: false)
            .Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
        AddressId = table.Column<long>(nullable: true),
        Name = table.Column<string>(nullable: true)
    },
    constraints: table =>
    {
        table.PrimaryKey("PK_Tags", x => x.Id);
        table.ForeignKey(
            name: "FK_Tags_Addresses_AddressId",
            column: x => x.AddressId,
            principalTable: "Addresses",
            principalColumn: "Id",
            onDelete: ReferentialAction.Restrict);
    });

查看 AddressId 列并 FK 到 Addresses table?这是为什么?因为你的 Tags 属性:

public List<Tag> Tags { get { return ...; } }

这可能是映射只读集合的​​当前 EF Core 错误 属性,但实际效果是它考虑了 AddressTag 之间的一对多关系当然不是你的本意。

一般来说,我建议保持实体模型干净,不要包含此类 "helper" 属性 - 集合和引用类型。它们看起来像导航属性,但实际上不是,并且很容易在查询中错误地使用它们,这将完全改变执行计划并导致意外异常或错误结果(如果底层 属性 不是加载)。不是说违反不创建 属性 返回 List 的一般规则,它不是 class 的成员,而是在每个 属性 访问调用中创建。

很快,只需删除 属性,问题就会消失。或者你非要保留,那就用NotMapped数据注解装饰一下:

[NotMapped]
public List<Tag> Tags { get { return ...; } }