ef核心:单向多对多关系

问题描述 投票:0回答:1

EF Core 初学者,请耐心等待。我们正在尝试将现有应用程序从 NHibernate 迁移到 EF Core,但我们面临着几个有趣的问题。其中之一与单向多对多关系有关。

根据文档,EF 支持单向多对多关系。不幸的是,我在尝试配置它时遇到了一些问题。这是一个片段,展示了我们需要映射到表中的类:

// this is the class that has the reference to the other entity
public class GuiaAbate : Entity 
{
    private IList<Equipamento> _equipamentos = new List<Equipamento>( );

  // other properties/methods removed

   // unidirectional navigation to many
   public IEnumerable<Equipamento> Equipamentos {
        get => new ReadOnlyCollectionBuilder<Equipamento>(_equipamentos);
        internal set => _equipamentos = value.ToList( );
    }

}

// even though I believe it's not that important, here's the other class
// this is an abstract class since there are several derived classes
// for this type, all derived types are mapped to the same table
public abstract class Equipamento : Entity, IVersionamento 
{
  // it has some properties and collections which reference other entiites
  // however, there's nothing point back to GuiaAbate (ie, no IEnumerable<GuiaAbate> prop)
  // because we're only interested in the other side of the navigation
}

我们无法更改数据库表,所以现在的样子:

CREATE TABLE [dbo].[GuiaAbate](
    [IDGuiaAbate] [int] IDENTITY(1,1) NOT NULL,
    [Data] [datetime] NOT NULL,
    [Login] [varchar](50) NOT NULL,
    [LoginAD] [int] NOT NULL,
 CONSTRAINT [PK_GuiaAbate] PRIMARY KEY CLUSTERED 
(
    [IDGuiaAbate] ASC
) ) ON [PRIMARY]


CREATE TABLE [dbo].[Equipamento](
    [IDEquipamento] [int] IDENTITY(1,1) NOT NULL,

    /* other fields removed */ 

 CONSTRAINT [PK_Equipamento] PRIMARY KEY CLUSTERED 
(
    [IDEquipamento] ASC
)) ON [PRIMARY]



CREATE TABLE [dbo].[GuiaAbateEquipamento](
    [IDGuiaAbateEquipamento] [bigint] IDENTITY(1,1) NOT NULL,
    [IDGuiaAbate] [int] NOT NULL,
    [IDEquipamento] [int] NOT NULL,
 CONSTRAINT [PK_GuiaAbateEquipamento] PRIMARY KEY CLUSTERED 
(
    [IDGuiaAbateEquipamento] ASC
)) ON [PRIMARY]

如你所见,没什么太花哨的。

现在,由于我无法重用默认约定,因此我尝试了以下

GuiaAbate
类型的映射:

public sealed class GuiaAbateTypeConfiguration: IEntityTypeConfiguration<GuiaAbate> 
{
    public void Configure(EntityTypeBuilder<GuiaAbate> builder) 
    {
        builder.ToTable("GuiaAbate");

        builder.HasKey(g => g.Id);
        builder.Property(g => g.Id).HasColumnName("IdGuiaAbate");

        builder.Property(g => g.Data)
               .HasConversion(d => d,
                              d => new DateTime(d.Ticks, DateTimeKind.Utc));

        builder.Property(g => g.Data)
               .HasConversion(
                   d => d,
                   d => new DateTime(d.Ticks, DateTimeKind.Utc));
        builder.Property(g => g.Tecnico).HasColumnName("Login");

        builder.HasMany(g => g.Equipamentos)
               .WithMany( )
               .UsingEntity(
                   "GuiaAbateEquipamento",
                   r => r.HasOne(typeof( Equipamento )).WithMany( ).HasForeignKey("IdEquipamento"),
                   l => l.HasOne(typeof( GuiaAbate )).WithMany( ).HasForeignKey("IdGuiaAbate"));
    }
}

这个想法是指定连接表的名称以及应用作该表上的外键的列的名称。不幸的是,我一定在这里遗漏了一些东西,因为我最终得到了一个错误,说明了这一点

The skip navigation 'Equipamento.GuiaTransporte' doesn't have a foreign key associated with it. Every skip navigation must have a configured foreign key.

有人可以帮忙吗?

谢谢。

entity-framework-core many-to-many
1个回答
0
投票

你有一张桌子

GuiaAbate设备

虽然这张表上有 2 个“FK”列....

你也有

IDGuiaAbateEquipamento

虽然看起来微不足道,但这是“关系上的财产”。

..

因此,您的设计需要第三个实体..来捕获关系。

我将展示一个通用示例。

public partial class EmployeeEntity
{
    public long EmployeeKey { get; set; } /* PK */


    public string LastName { get; set; }


    /* Navigation */
    
    public ICollection<EmployeeToJobTitleLinkEntity> EmployeeToJobTitleLinks { get; set; } = new List<EmployeeToJobTitleLinkEntity>();

}

public partial class JobTitleEntity
{
    public long JobTitleKey { get; init; } /* PK */
      
    public string JobTitleName { get; init; }
      
    public string JobTitleDescription { get; init; }
      

    /* Navigation */
    public ICollection<EmployeeToJobTitleLinkEntity> EmployeeToJobTitleLinks { get; set; } = new List<EmployeeToJobTitleLinkEntity>();

}

public class EmployeeToJobTitleLinkEntity
{
    public long EmployeeToJobTitleLinkKey { get; set; } /* PK */
    
    public long EmployeeKey { get; set; } /* FK as scalar (long) */
    
    public long JobTitleKey { get; set; } /* FK  as scalar (long) */
    

    /* navigation properties */
    public EmployeeEntity Employee { get; set; }

    public JobTitleEntity JobTitle { get; set; }


}

请注意,我有一个专用的“EntityObject”(在我的通用示例中为 EmployeeToJobTitleLinkEntity),用于处理关系。

这是“地图”。 (我使用 Fluent)。

“EmployeeMap”(未显示)和“JobTitleMap”很简单,正如您所期望的那样。

更重要的地图如下所示。

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

public class EmployeeToJobTitleLinkMap : IEntityTypeConfiguration<EmployeeToJobTitleLinkEntity>
{
    public void Configure(EntityTypeBuilder<EmployeeToJobTitleLinkEntity> builder)
    {
        builder.ToTable("EmployeeToJobTitleLink", "dbo");

        builder.HasKey(k => k.EmployeeToJobTitleLinkKey);
        builder.Property(cn => cn.EmployeeToJobTitleLinkKey)
            .HasColumnName("EmployeeToJobTitleLinkKey");
        builder.Property(req => req.EmployeeToJobTitleLinkKey).IsRequired();

        builder.Property(cn => cn.EmployeeKey).HasColumnName("EmployeeKey");
        builder.Property(req => req.EmployeeKey).IsRequired();

        builder.Property(cn => cn.JobTitleKey).HasColumnName("JobTitleKey");
        builder.Property(req => req.JobTitleKey).IsRequired();



        builder.HasOne(ent => ent.Employee)
            .WithMany(c => c.EmployeeToJobTitleLinks)
            .HasForeignKey(chd => chd.EmployeeKey)
            .HasConstraintName(
                "Your_Custom_Name_1");


        builder.HasOne(ent => ent.JobTitle)
            .WithMany(c => c.EmployeeToJobTitleLinks)
            .HasForeignKey(chd => chd.JobTitleKey)
            .HasConstraintName("Your_Custom_Name_2");
    }
}

“问题”(恕我直言)是所有学习网站。

例如:

https://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration

不要区分“琐碎的 M:N”和“非琐碎的、关系上的财产”M:N。

...

虽然您的“链接”表上只有指定的 PK 列,但它是关系的属性。

在我的通用示例中...假设您想在 RELATIONSHIP 上添加额外的属性。

“EmployeeStartedThisJobTitleDateOf”。

“Jose”于 2020 年 1 月 1 日开始担任开发人员。 “Jose”于 2021 年 9 月 2 日开始担任建筑师。

“Maria”于 2022 年 2 月 28 日开始担任开发人员。 “Maria”于 2023 年 7 月 14 日开始担任建筑师。

这个“开始日期”不是员工的值。 它不是职位名称上的值。

这是 M:N 关系上的值。

..

现在,您可能会想忽略链接表上的 PK 列...但是,如果您想向链接添加新列,这确实会“将您逼入墙角”,以便进行重大重构 -表。

因此,99% 的情况下,我只是继续将原始版本编码为“高级“M:N”设置”。

© www.soinside.com 2019 - 2024. All rights reserved.