【问题标题】:Entity Framework Core - mapping two joins to the same tableEntity Framework Core - 将两个连接映射到同一个表
【发布时间】:2019-12-02 09:09:02
【问题描述】:

我有两张表 Clients 和 Jobs。一个客户可以有许多工作。但是,我在 Jobs 上有第二个关系,它也应该指称为 WarrantyCompany 的客户。基本上,保修公司也是客户。好吧,客户/工作关系是主要关系,但我想再次参考客户表以获取保修公司的详细信息。

这是客户表:

using JobsLedger.DATA.Entities.Interfaces;
using JobsLedger.DATA.Interfaces;
using System;
using System.Collections.Generic;

namespace JobsLedger.DATA.Entities
{
#nullable enable
    public class Client : IEntityBase, IAuditedEntityBase
    {

        public Client()
        {
            Jobs = new List<Job>();

        }
        public int Id { get; set; }

        // One warranty company client to a job.
        public int? WarrantyCompanyId { get; set; }
        public Job? WarrantyCompany { get; set; }

        // One suburb to a client.
        public int? SuburbId { get; set; }
        public Suburb? Suburb { get; set; }

        // If its a warranty company then we simply link it one to one to the brand id.
        public Brand? Brand { get; set; }

        public virtual ICollection<Job> Jobs { get; set; } = default!;

        public virtual ICollection<Job> WarrantyCompanyJobs { get; set; } = default!;
    }
#nullable disable
}

我正在使用以下方式引用作业表:

// One warranty company client to a job.
public int? WarrantyCompanyId { get; set; }
public Job? WarrantyCompany { get; set; }

工作表如下:

using JobsLedger.DATA.Entities.Interfaces;
using JobsLedger.DATA.Interfaces;
using System.Collections.Generic;

namespace JobsLedger.DATA.Entities
{
    public class Job : IEntityBase, IAuditedEntityBase
    {
#nullable enable
        public Job()
        {
            Visits = new List<Visit>();
        }

        public int Id { get; set; }

        public int? WarrantyCompanyId { get; set; }

        public Client? WarrantyCompany { get; set; }

        public int ClientId { get; set; } = default!;

        public Client Client { get; set; } = default!;

    }
#nullable disable
}

在此表中,我使用以下方法引用客户端表:

public int? WarrantyCompanyId { get; set; }
public Client? WarrantyCompany { get; set; }

我正在使用 Entity Framework Core Fluent API 来创建关系。

我决定从客户的角度来处理这两种关系:

// Two one to many relationships between client and job.
modelBuilder.Entity<Client>()
    .HasMany(t => t.Jobs)
    .WithOne(g => g.Client)
    .HasForeignKey(g => g.ClientId)
    .OnDelete(DeleteBehavior.Restrict);

// Same table but a client can also be a warranty agent.
modelBuilder.Entity<Client>()
    .HasMany(a => a.Jobs)
    .WithOne(b => b.WarrantyCompany)
    .HasForeignKey(b => b.WarrantyCompanyId);

我尝试创建迁移,但出现以下错误:

无法在“Client.Jobs”和 'Job.WarrantyCompany',因为两者之间已经存在关系 'Client.Jobs' 和 'Job.Client'。导航属性只能 参与单一关系。

这是不言而喻的。我还尝试从作业表的角度创建关系:

modelBuilder.Entity<Job>()
    .HasOne(a => a.Client)
    .WithOne(b => b.WarrantyCompany)
    .HasForeignKey<Client>(b => b.WarrantyCompanyId);

我发现当有两个返回同一个客户端表时,我无法将关系可视化。

基本上,简而言之,我有一个客户可以有一对多的工作,但对于每一个工作,我可能有一个可选的保修公司,其中保修公司是客户。客户和工作之间的两种关系。

如何在这两个表之间建立主要关系和次要可选关系,次要保修公司关系是一对一还是一对多?

【问题讨论】:

  • 您正在为两种关系映射客户端作业导航属性。你需要一个新的,比如 WarrantedJobs?
  • 感谢您的评论。想知道如何做到这一点..

标签: c# entity-framework entity-framework-core


【解决方案1】:

为了便于阅读,我简化了表格属性。

客户表:

public class Client 
{
    public int Id { get; set; }

    // One warranty company client to a job.
    public int? WarrantyCompanyId { get; set; }
    public Job? WarrantyCompany { get; set; }

    public ICollection<Job> Jobs { get; set; } = default!;

    public ICollection<Job> WarrantyCompanyJobs { get; set; } = default!;
}

还有工作表:

public class Job
{
    public int Id { get; set; }

    public int? WarrantyCompanyId { get; set; }
    public Client? WarrantyCompany { get; set; }


    public int ClientId { get; set; } = default!;
    public Client Client { get; set; } = default!;

}

对于配置,我创建了一个JobConfig

public class JobConfig : IEntityTypeConfiguration<Job>
    {
        public void Configure(EntityTypeBuilder<Job> builder)
        {
            builder.HasOne(s => s.Client)
            .WithMany(g => g.Jobs)
            .HasForeignKey(f => f.ClientId);

            builder.HasOne(s => s.WarrantyCompany)
                .WithMany(g => g.WarrantyCompanyJobs)
                .HasForeignKey(f => f.WarrantyCompanyId);
        }
    }

Context类中,假设上下文类名是ProjectContext,我重写OnModelCreating方法。

public class ProjectContext: DbContext
{
    public ProjectContext()
    {

    }

    public Client Client { get; set; }
    public Job Job { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfiguration(new JobConfig());
    }
 }

它会生成一个这样的表和关系。

【讨论】:

  • 就像善意的旁注:这里的default! public int ClientId { get; set; } = default!; 是无用的,因为ClientId 是一个值类型。此外,您似乎使用 default! 只是为了使不可为空的引用类型警告消失。我建议你看看SmartAnalyzers.CSharpExtensions.Annotations。和the article explaining how to use this package
  • 我只是将其作为问题中的代码进行,并没有考虑太多。但你是对的,它在这里没有用。
  • 对不起,我也很糟糕。我直接去找你的答案,并没有看到这段代码实际上来自问题
【解决方案2】:

由于两个关系映射到 Client 类中的同一个 Jobs 集合而发生此问题。由于实际上有两个关系,它们不能重用相同的属性。

您的 Client 模型已经有第二个关系的集合。您只需要修复映射。

    // First client/job relationship
    modelBuilder.Entity<Client>()
        .HasMany(t => t.Jobs)
        .WithOne(g => g.Client)
        .HasForeignKey(g => g.ClientId)
        .OnDelete(DeleteBehavior.Restrict);

    // Second client/job relationship (warranty)
    modelBuilder.Entity<Client>()
        .HasMany(a => a.WarrantyCompanyJobs)
        .WithOne(b => b.WarrantyCompany)
        .HasForeignKey(b => b.WarrantyCompanyId);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-30
    相关资源
    最近更新 更多