EF Core 3.1 Fluent API

3
我有两个实体: Audit类
public class Audit
{
  public string AuditId{get;set;}
  public int EmployeeId{get;set;}
  public virtual ModEmployee{get;set;}
}

员工类:

public class Employee
{
  public int EmployeeId{get;set}
}

当加载 Set<Audit> 时,我希望填充 Audit 类的 Employee 属性。 这显然是一种一对多关系,一个 Audit 会有一个 Employee,但一个 Employee 可以在多个 Audits 中出现。

我的 Fluent API 如下:

protected override OnModelCreating(ModelBuilder modelBuilder)
{
  modelBuilder.Entity<Audit>()
 .HasOne(a=>a.ModEmployee)
 .WithMany()
 .HasForeignKey("EmployeeId");//a=>a.EmployeeId wont work.

}

我已经寻找解决方案有一段时间了,并从这个答案中获取了一些输入,但它仅适用于单向一对一关系。然后这个资源展示了另一种方法,但需要在Employee类中拥有一个Audits集合。


由于其他实体框架版本(不是核心版本)已经在使用,我无法更改现有的模型或添加注释。


目前我遇到了以下错误:

System.InvalidOperationException:“无法确定导航属性'Employee.ModEmployee'所表示的关系类型'Employee'。请手动配置该关系,或使用'[NotMapped]'属性或在'OnModelCreating'中使用'EntityTypeBuilder.Ignore'忽略此属性。”

如果我在Audit的构建器上添加一个忽略方法,就可以消除错误,如下所示:.Ignore(a=>a.ModEmployee) 但我将无法在Audit中获得实体对象。任何帮助将不胜感激。我使用的是EFCore 3.1.10。谢谢阅读,期待您的回复。



错误信息说“Employee.ModEmployee”而不是“Audit.ModEmployee”,这很尴尬。你尝试过删除“.HasForeignKey(“EmployeeId”)”吗? - heringer
@heringer 我之前没有这样做,因为我认为它只是加载EmployeeId,并不会对ModEmplyee做任何事情。但我看到你有一点道理,我会试试的。谢谢评论! - James
我测试了你的示例,在EF Core 3.1.10和EF Core 5中都可以正常运行。它可以在有或者没有OnModelCreating配置下工作。问题一定是出在其他地方。 - Marcos Dimitrio
只是提供信息,下一个设置 modelBuilder.Entity<Audit>().HasOne(a => a.ModEmployee).WithMany().HasForeignKey(a => a.EmployeeId); 对我来说很好用。 - Guru Stron
3个回答

0

最近我遇到了同样的问题。下面是解决方法:

方法1: 在定义Employee类时,加入对Audit集合的引用:

public class Employee
{
  public int EmployeeId { get; set }
  public virtual ICollection<Audit> Audits { get; set; }
}

然后在您的模型构建器中以这种方式定义关系:

builder.Entity<Audit>().HasOne(e => e.ModEmployee).WithMany(ae => ae.Audits).HasForeignKey(k => k.EmployeeId);

每个类都需要定义集合。

编辑!!

方法2:

public class Audit
{
  public string AuditId{get;set;}
  public Employee Employee { get; set; }
}

我知道这需要你编辑你的类,但相信我,这会让事情变得更容易。


谢谢你的回答,但是像我说的那样,我不能改变模型或添加注释,因为这些类在EF6中运行得很好。我正在为EFCore迁移编写并行流程,因此必须使用相同的模型。 - James
很抱歉,您不能这样做,因为ICollection不会改变数据库中的任何内容,但EFCore需要定义在哪里“本地”存储检索到的数据库行。我在同一项目中使用了EF6和EFCore,但是我没有遇到任何与此ICollection接口相关的问题。 - bera
但是EF6中,使用IsRequired(...).WithMany().HasForeignKey(...)方法,这个现有的设置是如何工作的呢? - James
你可以直接使用EFCore的自动绑定。定义一个带有对Employee对象的对象引用的审计类,这应该会自动在你的数据库上添加一个外键。我将编辑我的答案,以便您可以正确查看它。 - bera

0

如果您无法修改模型类。

您可以在上下文中简单地添加一个检索审计的方法。

using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EFCoreFluentApi1.Context
{
    public class MyContext : DbContext
    {
        public DbSet<Audit> Audit { get; set; }
        public DbSet<Employee> Employee { get; set; }

        public MyContext(DbContextOptions options) : base(options)
        {
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);

        }
        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);

            builder.Entity<Employee>().HasKey(e => e.EmployeeId);
            builder.Entity<Employee>().Property(e => e.EmployeeId);

            builder.Entity<Audit>().HasKey(e => e.AuditId);
            builder.Entity<Audit>().Property(e => e.AuditId);
            builder.Entity<Audit>().HasOne(e => e.ModEmployee).WithMany().HasForeignKey(e => e.EmployeeId);

            builder.Entity<Employee>().HasData(new Employee[]
            {
                new Employee{ EmployeeId = 1 },
                new Employee{ EmployeeId = 2 },
                new Employee{ EmployeeId = 3 },
                new Employee{ EmployeeId = 4 }
            });

            builder.Entity<Audit>().HasData(
                new Audit[]
                {
                    new Audit
                    {
                        AuditId = "Audit1",
                        EmployeeId = 1
                    },
                    new Audit
                    {
                        AuditId = "Audit2",
                        EmployeeId = 1
                    },
                    new Audit
                    {
                        AuditId = "Audit3",
                        EmployeeId = 1
                    },
                    new Audit
                    {
                        AuditId = "Audit4",
                        EmployeeId = 2
                    },
                    new Audit
                    {
                        AuditId = "Audit5",
                        EmployeeId = 3
                    },
                    new Audit
                    {
                        AuditId = "Audit6",
                        EmployeeId = 3
                    },
                    new Audit
                    {
                        AuditId = "Audit7",
                        EmployeeId = 4
                    },
                    new Audit
                    {
                        AuditId = "Audit8",
                        EmployeeId = 4
                    },
                    new Audit
                    {
                        AuditId = "Audit9",
                        EmployeeId = 4
                    },
                    new Audit
                    {
                        AuditId = "Audit10",
                        EmployeeId = 4
                    }

                });
        }

        public ICollection<Audit> GetAudits(int employeeId)
        {
            return Audit.Where(e => e.EmployeeId == employeeId).ToList();
        }

    }
}


如果您需要填充ModEmployee,可以使用Include方法。
using EFCoreFluentApi1.Context;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using System;
using System.IO;
using System.Linq;
using System.Text.Json;

namespace EFCoreFluentApi1
{
    class Program
    {
        static void Main(string[] args)
        {
            IConfiguration configuration = new ConfigurationBuilder().
                    SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json", false, true)
                    .Build();


            DbContextOptions options = new DbContextOptionsBuilder<MyContext>()
                .UseSqlServer(configuration.GetConnectionString("MyConnectionString"))
                .EnableSensitiveDataLogging(true).Options;

            using (var context = new MyContext(options))
            {
                var result = context.Audit.Include(e => e.ModEmployee).ToList(); //ModEmployee is populated 
                //var result = context.Audit.ToList(); //ModEmployee = null
                foreach (var value in result)
                {
                    Console.WriteLine($"█ Audit: {value.AuditId}");
                    Console.WriteLine(JsonSerializer.Serialize(value));
                    Console.WriteLine();
                }
                Console.ReadLine();
            }
        }
    }
}

请查看我的示例 https://github.com/JomaStackOverflowAnswers/EFCoreFluentApi1 该项目的目标框架为 <TargetFramework>netcoreapp3.1</TargetFramework>

请检查是否需要显式设置 https://learn.microsoft.com/en-us/ef/core/modeling/entity-properties?tabs=fluent-api%2Cwithout-nrt#explicit-configuration


谢谢你的回答。这是EFCore 3.1.10而不是EF。我问这个问题是因为据我所知,IsRequired()在EFCore上不起作用。 - James
请检查示例的URI。已更新答案。 - Joma
我的错,应该是HasRequired而不是IsRequired。需要一点时间来理解你的答案。 - James

0

尝试在Audit类中将类型从int更改为Employee。 这应该可以正常工作,因为这是使用流畅API关联外键的正确方法。

附注:FK属性字符串应与Employee内部完全匹配,这已经发生了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接