EF Core (5) 无数据库关系的导航属性

问题描述

我有一个数据库,现在无法更改。在数据库中,表之间没有关系。请参阅下面的数据库图像。这里定义了多对多关系但没有物理关系(外键)的三个表。使用 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):无效的列名“TagGroupId”。无效的列名“TagId1”。在 Microsoft.Data.sqlClient.sqlConnection.OnError(sqlException 异常,Boolean breakConnection,Action1 wrapCloseInAction) at Microsoft.Data.sqlClient.sqlInternalConnection.OnError(sqlException exception,Boolean breakConnection,Action1 wrapCloseInAction) 在

感谢任何帮助。提前致谢。

enter image description here

解决方法

问题是,如果module.exports = { entry: { background: 'src/background.ts' },output: { filename: '[name].js' },optimization: { runtimeChunk: false } } 不是唯一的,那么你在Tag.IdTagCompany之间存在隐藏的一对多关系,需要集合导航属性,一般无法映射到单个 Tag FK。

您可以通过将 TagCompany.TagId 映射为 alternate key 来欺骗 EF,然后将其映射为 Tag.Id 作为多对一的 FK 替换

TagCompany.TagId

.HasPrincipalKey(o => new { o.GroupId,o.Id }

但是现在有些查询会在包含重复的 .HasPrincipalKey(o => o.Id) 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 不是(包含较少的项目)。

因此,似乎适用于 EFC 5 的更好的 hack 是配置与所谓的跳过导航的多对多关系。这是修改后的模型(必不可少的是两个集合导航属性):

includedTags

和流畅的配置:

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
    );

产生相同的结果。


现在,最后一个 hack 似乎在 EFC 5 中工作(未使用投影和其他非实体返回 LINQ 查询进行测试),但可能会在未来的 EFC 版本中失效,因此使用它需要您自担风险.另一方面,没有其他方法可以映射这样的数据库模型,所以...

,

TAG_COMPANY 必须有一个 TAG_GROUP_ID 列才能使 EF 关系起作用。技术上不需要数据库中的外键,但需要有所有的键列。

如果 TAG.TAG_ITEM_ID 是唯一的,您可以将其配置为 Tag 的 Key。

,

你有一个错误,试试这个

modelBuilder.Entity<TagCompany>().HasKey(o => new { o.CompanyId,o.TagId });