EF Core (5) 导航属性 无数据库关系
EF Core (5) navigation property without DB relation
我有一个旧数据库,现在无法更改。在数据库中,表之间没有关系。请参阅下面的数据库图像。这里的三个表定义了多对多关系但没有物理关系(外键)。使用 EFCore DB 优先方法,现在我需要使用导航器映射 EFCore 模型。我创建了如下模型 类,
public class Company {
[Key]
[MaxLength(36)]
[Column("COMPANY_ID")]
public string CompanyId { get; set; }
[Column("FULL_NAME")]
public string FullName { get; set; }
/*Others properties*/
public virtual ICollection<TagCompany> TagCompanies { set; get; }
}
public class Tag {
[Column("TAG_GROUP_ID")]
public string GroupId { get; set; }
[Column("TAG_ITEM_ID")]
public string Id { get; set; }
[Column("TAG_ITEM_NAME")]
public string Name { get; set; }
[Column("TAG_ITEM_DESCRIPTION")]
public string Description { get; set; }
public virtual ICollection<TagCompany> TagCompanies { set; get; }
}
public class TagCompany {
[Column("COMPANY_ID")]
public string CompanyId { get; set; }
[Column("TAG_ITEM_ID")]
public string TagId { get; set; }
public virtual Company Company { set; get; }
public virtual Tag Tag { set; get; }
}
以及如下的 OnModelCreating 方法,
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<Company>().HasKey(o => o.CompanyId);
modelBuilder.Entity<Tag>().HasKey(o => new { o.GroupId, o.Id });
modelBuilder.Entity<TagCompany>().HasKey(o => new { o.CompanyId, o.TagId });
modelBuilder.Entity<TagCompany>().HasOne(s => s.Tag).WithMany(s => s.TagCompanies).HasPrincipalKey(o => new { o.GroupId, o.Id });
modelBuilder.Entity<TagCompany>().HasOne(s => s.Company).WithMany(s => s.TagCompanies).HasForeignKey(s => s.CompanyId);
}
使用上面的代码我遇到了以下错误,
Microsoft.Data.SqlClient.SqlException (0x80131904): Invalid column name 'TagGroupId'. Invalid column name 'TagId1'. at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction) at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action
1 wrapCloseInAction) at
感谢您的帮助。提前致谢。
TAG_COMPANY 必须有一个 TAG_GROUP_ID 列才能使 EF 关系起作用。技术上不需要数据库中的外键,但需要具有所有键列。
如果TAG.TAG_ITEM_ID是唯一的,您可以将其配置为Tag
的Key。
你有一个错误,试试这个
modelBuilder.Entity<TagCompany>().HasKey(o => new { o.CompanyId, o.TagId });
问题是,如果Tag.Id
不是唯一的,你在TagCompany
和Tag
之间有一个隐藏的一对多关系,这需要集合导航属性 并且通常不能映射到单个 TagCompany.TagId
FK。
您可以通过将 Tag.Id
映射为 alternate key 来愚弄 EF,这样您就可以通过替换 [=] 将 TagCompany.TagId
映射为多对一 FK 27=]
.HasPrincipalKey(o => new { o.GroupId, o.Id }
与
.HasPrincipalKey(o => o.Id)
但现在有些查询会 return 不正确的结果 Tag
数据包含重复的 Id
。例如这里
var companies = db.Set<Company>()
.Include(e => e.TagCompanies).ThenInclude(e => e.Tag)
.ToList();
var includedTags = companies
.SelectMany(e => e.TagCompanies).Select(e => e.Tag)
.ToList();
var actualTags = db.Set<Company>()
.SelectMany(e => e.TagCompanies).Select(e => e.Tag)
.ToList();
actualTags
是正确的,includedTags
不是(包含较少的项目)。
因此,一个似乎适用于 EFC 5 的更好的技巧是使用所谓的跳过导航配置多对多关系。这是修改后的模型(本质是两个集合导航属性):
public class Company
{
[Key]
[MaxLength(36)]
[Column("COMPANY_ID")]
public string Id { get; set; }
[Column("FULL_NAME")]
public string FullName { get; set; }
/*Others properties*/
public virtual ICollection<Tag> Tags { get; set; } // <--
}
public class Tag
{
[Column("TAG_GROUP_ID")]
public string GroupId { get; set; }
[Column("TAG_ITEM_ID")]
public string Id { get; set; }
[Column("TAG_ITEM_NAME")]
public string Name { get; set; }
[Column("TAG_ITEM_DESCRIPTION")]
public string Description { get; set; }
public virtual ICollection<Company> Companies { get; set; } // <--
}
public class TagCompany
{
[Column("COMPANY_ID")]
public string CompanyId { get; set; }
[Column("TAG_ITEM_ID")]
public string TagId { get; set; }
public virtual Company Company { set; get; }
public virtual Tag Tag { get; set; }
}
以及流畅的配置:
// composite PK
modelBuilder.Entity<Tag>().HasKey(e => new { e.GroupId, e.Id });
// M2M relationship and join entity configuration
modelBuilder.Entity<Company>()
.HasMany(e => e.Tags)
.WithMany(e => e.Companies)
.UsingEntity<TagCompany>(
j => j.HasOne(e => e.Tag).WithMany().HasForeignKey(e => e.TagId)
.HasPrincipalKey(e => e.Id), // fake alternate key
j => j.HasOne(e => e.Company).WithMany().HasForeignKey(e => e.CompanyId),
j => j.HasKey(e => new { e.CompanyId, e.TagId }) // composite PK
);
现在和以前一样的测试
var companies = db.Set<Company>()
.Include(e => e.Tags)
.ToList();
var includedTags = companies
.SelectMany(e => e.Tags)
.ToList();
var actualTags = db.Set<Company>()
.SelectMany(e => e.Tags)
.ToList();
产生相同的结果。
现在,最后一个 hack 似乎 可以在 EFC 5 中使用(未使用投影和其他非实体 returning LINQ 查询进行测试),但将来可能会中断EFC 版本,因此使用它需要您自担风险。另一方面,没有其他方法可以映射这样的数据库模型,所以...
我有一个旧数据库,现在无法更改。在数据库中,表之间没有关系。请参阅下面的数据库图像。这里的三个表定义了多对多关系但没有物理关系(外键)。使用 EFCore DB 优先方法,现在我需要使用导航器映射 EFCore 模型。我创建了如下模型 类,
public class Company {
[Key]
[MaxLength(36)]
[Column("COMPANY_ID")]
public string CompanyId { get; set; }
[Column("FULL_NAME")]
public string FullName { get; set; }
/*Others properties*/
public virtual ICollection<TagCompany> TagCompanies { set; get; }
}
public class Tag {
[Column("TAG_GROUP_ID")]
public string GroupId { get; set; }
[Column("TAG_ITEM_ID")]
public string Id { get; set; }
[Column("TAG_ITEM_NAME")]
public string Name { get; set; }
[Column("TAG_ITEM_DESCRIPTION")]
public string Description { get; set; }
public virtual ICollection<TagCompany> TagCompanies { set; get; }
}
public class TagCompany {
[Column("COMPANY_ID")]
public string CompanyId { get; set; }
[Column("TAG_ITEM_ID")]
public string TagId { get; set; }
public virtual Company Company { set; get; }
public virtual Tag Tag { set; get; }
}
以及如下的 OnModelCreating 方法,
protected override void OnModelCreating(ModelBuilder modelBuilder) {
modelBuilder.Entity<Company>().HasKey(o => o.CompanyId);
modelBuilder.Entity<Tag>().HasKey(o => new { o.GroupId, o.Id });
modelBuilder.Entity<TagCompany>().HasKey(o => new { o.CompanyId, o.TagId });
modelBuilder.Entity<TagCompany>().HasOne(s => s.Tag).WithMany(s => s.TagCompanies).HasPrincipalKey(o => new { o.GroupId, o.Id });
modelBuilder.Entity<TagCompany>().HasOne(s => s.Company).WithMany(s => s.TagCompanies).HasForeignKey(s => s.CompanyId);
}
使用上面的代码我遇到了以下错误,
Microsoft.Data.SqlClient.SqlException (0x80131904): Invalid column name 'TagGroupId'. Invalid column name 'TagId1'. at Microsoft.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action
1 wrapCloseInAction) at Microsoft.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action
1 wrapCloseInAction) at
感谢您的帮助。提前致谢。
TAG_COMPANY 必须有一个 TAG_GROUP_ID 列才能使 EF 关系起作用。技术上不需要数据库中的外键,但需要具有所有键列。
如果TAG.TAG_ITEM_ID是唯一的,您可以将其配置为Tag
的Key。
你有一个错误,试试这个
modelBuilder.Entity<TagCompany>().HasKey(o => new { o.CompanyId, o.TagId });
问题是,如果Tag.Id
不是唯一的,你在TagCompany
和Tag
之间有一个隐藏的一对多关系,这需要集合导航属性 并且通常不能映射到单个 TagCompany.TagId
FK。
您可以通过将 Tag.Id
映射为 alternate key 来愚弄 EF,这样您就可以通过替换 [=] 将 TagCompany.TagId
映射为多对一 FK 27=]
.HasPrincipalKey(o => new { o.GroupId, o.Id }
与
.HasPrincipalKey(o => o.Id)
但现在有些查询会 return 不正确的结果 Tag
数据包含重复的 Id
。例如这里
var companies = db.Set<Company>()
.Include(e => e.TagCompanies).ThenInclude(e => e.Tag)
.ToList();
var includedTags = companies
.SelectMany(e => e.TagCompanies).Select(e => e.Tag)
.ToList();
var actualTags = db.Set<Company>()
.SelectMany(e => e.TagCompanies).Select(e => e.Tag)
.ToList();
actualTags
是正确的,includedTags
不是(包含较少的项目)。
因此,一个似乎适用于 EFC 5 的更好的技巧是使用所谓的跳过导航配置多对多关系。这是修改后的模型(本质是两个集合导航属性):
public class Company
{
[Key]
[MaxLength(36)]
[Column("COMPANY_ID")]
public string Id { get; set; }
[Column("FULL_NAME")]
public string FullName { get; set; }
/*Others properties*/
public virtual ICollection<Tag> Tags { get; set; } // <--
}
public class Tag
{
[Column("TAG_GROUP_ID")]
public string GroupId { get; set; }
[Column("TAG_ITEM_ID")]
public string Id { get; set; }
[Column("TAG_ITEM_NAME")]
public string Name { get; set; }
[Column("TAG_ITEM_DESCRIPTION")]
public string Description { get; set; }
public virtual ICollection<Company> Companies { get; set; } // <--
}
public class TagCompany
{
[Column("COMPANY_ID")]
public string CompanyId { get; set; }
[Column("TAG_ITEM_ID")]
public string TagId { get; set; }
public virtual Company Company { set; get; }
public virtual Tag Tag { get; set; }
}
以及流畅的配置:
// composite PK
modelBuilder.Entity<Tag>().HasKey(e => new { e.GroupId, e.Id });
// M2M relationship and join entity configuration
modelBuilder.Entity<Company>()
.HasMany(e => e.Tags)
.WithMany(e => e.Companies)
.UsingEntity<TagCompany>(
j => j.HasOne(e => e.Tag).WithMany().HasForeignKey(e => e.TagId)
.HasPrincipalKey(e => e.Id), // fake alternate key
j => j.HasOne(e => e.Company).WithMany().HasForeignKey(e => e.CompanyId),
j => j.HasKey(e => new { e.CompanyId, e.TagId }) // composite PK
);
现在和以前一样的测试
var companies = db.Set<Company>()
.Include(e => e.Tags)
.ToList();
var includedTags = companies
.SelectMany(e => e.Tags)
.ToList();
var actualTags = db.Set<Company>()
.SelectMany(e => e.Tags)
.ToList();
产生相同的结果。
现在,最后一个 hack 似乎 可以在 EFC 5 中使用(未使用投影和其他非实体 returning LINQ 查询进行测试),但将来可能会中断EFC 版本,因此使用它需要您自担风险。另一方面,没有其他方法可以映射这样的数据库模型,所以...