简体   繁体   中英

EF Core 3.1 Fluent API

I have two entities:

The Audit class :

public class Audit
{
  public string AuditId{get;set;}
  public int EmployeeId{get;set;}
  public virtual ModEmployee{get;set;}
}

The Employee class :

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

While loading Set<Audit> , I want the Employee property of the Audit class to be populated. This is clearly a case of One to Many relation, where one Audit will have one Employee but an Employee can be in several Audits.

My Fluent API looks like below:

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

}

I have been searching for a solution for sometime and took some inputs from this answer but its for One-To-One. Then this resource shows a way but that will require me to have a collection of Audits in the Employee class.


I cant change the existing models or add annotations as they are already being used by some other entity framework version (not core).


Presently I am getting the following error:

System.InvalidOperationException: 'Unable to determine the relationship represented by navigation property 'Employee.ModEmployee' of type 'Employee'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.'

If I add a ignore method on the builder of Audit the error goes away, like so: .Ignore(a=>a.ModEmployee) but I won't get the entity object inside Audit. Any help will be appreciated. I am on EFCore 3.1.10. Thanks for reading. Any help will be appreciated.


Try changing your type from int to Employee in the Audit class. Should work fine because that's the correct way to relation a foreign key in the fluent API.

PS: the FK attribution string shall match the exact same inside the Employee , what's already happening.

I recently had the same issue. This worked for me:

Method 1: Define your Employee class with the reference to the Audit collection:

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

Then in your model builder define the relation this way:

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

You always need to define the collection in every class.

EDIT!!

Method 2:

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

I know this will need you to edit your class but trust me it will make the things a lot easier.

If you cannot modify your model classes.

You can simply Add in your context a method to retrieve audits.

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

    }
}

If you need populate ModEmployee, you can use the Include method.

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

Check my example in https://github.com/JomaStackOverflowAnswers/EFCoreFluentApi1 This project is <TargetFramework>netcoreapp3.1</TargetFramework>

Check IsRequired explicit https://docs.microsoft.com/en-us/ef/core/modeling/entity-properties?tabs=fluent-api%2Cwithout-nrt#explicit-configuration

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM