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; }
}
修改AddressTag
class如下:
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 错误 属性,但实际效果是它考虑了 Address
和 Tag
之间的一对多关系当然不是你的本意。
一般来说,我建议保持实体模型干净,不要包含此类 "helper" 属性 - 集合和引用类型。它们看起来像导航属性,但实际上不是,并且很容易在查询中错误地使用它们,这将完全改变执行计划并导致意外异常或错误结果(如果底层 属性 不是加载)。不是说违反不创建 属性 返回 List
的一般规则,它不是 class 的成员,而是在每个 属性 访问调用中创建。
很快,只需删除 属性,问题就会消失。或者你非要保留,那就用NotMapped
数据注解装饰一下:
[NotMapped]
public List<Tag> Tags { get { return ...; } }
我遇到了一个奇怪的问题 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; }
}
修改AddressTag
class如下:
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 错误 属性,但实际效果是它考虑了 Address
和 Tag
之间的一对多关系当然不是你的本意。
一般来说,我建议保持实体模型干净,不要包含此类 "helper" 属性 - 集合和引用类型。它们看起来像导航属性,但实际上不是,并且很容易在查询中错误地使用它们,这将完全改变执行计划并导致意外异常或错误结果(如果底层 属性 不是加载)。不是说违反不创建 属性 返回 List
的一般规则,它不是 class 的成员,而是在每个 属性 访问调用中创建。
很快,只需删除 属性,问题就会消失。或者你非要保留,那就用NotMapped
数据注解装饰一下:
[NotMapped]
public List<Tag> Tags { get { return ...; } }