Entity Framework 核心 table 过滤器(其中 table 有不止一种用途)

Entity Framework Core table filters (where a table has more than one use)

我继承了一个 SQL 服务器数据库,其中原始开发人员使用单个 table 来表示所有 "lookup" 类型,而不是为每个类型指定一个 table。 ..

create table Lookup (
    LookupID int,
    LookupType int,
    LookupKey int,
    LookupValue varchar(50)
)

然后使用 table(例如)根据 LookupType 提供不同的列表,因此您会看到诸如...

之类的数据
ID     Type     Key     Value
1      1        1       Mr
2      1        2       Mrs
3      1        3       Miss
4      2        1       Dog
5      2        2       Cat
6      2        3       Hamster

我需要将此 table 与 Entity Framework 核心一起使用,因为我希望能够在查询数据时撤回查找值。以下面的table为例...

create table Customer (
    CustomerID int,
    CustomerTitleID int, <- LookupType = 1
    PetTypeID int -- LookupType = 2
)

数据看起来像...

ID     TitleID     PetTypeID
1      1           1

我可以定义一个"Lookup"class...

public class Lookup {
    public int LookupID {get; set;}
    public int LookupTypeID {get; set;}
    public int LookupKey {get; set;}
    public string LookupValue {get; set;}
} 

我可以定义一个"Customer" class...

public class Customer {
    public int CustomerID {get; set;}
    public Lookup CustomerTitle {get; set;}
    public int CustomerTitleID {get; set;}
    public Lookup PetType {get; set;}
    public int PetTypeID {get; set;}
}

问题是虽然(在 DbContext.OnModelCreating 中)我可以为 Customer/Lookup 关系指定一个 "Principle Key"...

entity<Customer>().HasOne<Lookup>(c => c.CustomerTitle).WithMany().WithForeignKey(c => c.CustomerTitleID).WithPrincipleKey(l => l.LookupKey);
entity<Customer>().HasOne<Lookup>(c => c.PetType).WithMany().WithForeignKey(c => c.PetTypeID).WithPrincipleKey(l => l.LookupKey);

我找不到任何方法来为每个 "Lookup" class 设置过滤器以将其限制为 "LookupTypeID"。

我已经尝试创建自定义 "PetType" class 并将其与 "Lookup" 以及过滤器(在 DbContext.OnModelCreating 中)相关联...

entity.HasQueryFilter(lookup => lookup.LookupType == 2);

但是 EF 不喜欢多个实体类型与 table 关联,除非实体类型也相关("PetType" 必须继承自 "Lookup")。

然后我从 "Lookup" 继承了自定义 "PetType" class 并尝试了相同的过滤器...

entity.HasQueryFilter(petType=> petType.LookupType == 2);

但是 EF 只允许在根级别使用这样的过滤器。

我也尝试过使用视图,但是虽然 DbSet 实体可以是 DbQuery 实体的子实体 属性,但它似乎不能反过来工作。

我是不是错过了另一种方式?我希望能够实现的最终结果是..

from customer in dbContext.Customers
    .Select new
    {
        customer.CustomerID,
        Title = customer.CustomerTitle.LookupValue,
        PetType = customer.PetType.LookupValue
    }

并让 EF 自动对每个查找(显然由我指定)应用过滤器,以便选择正确的行。

您实际上可以通过在 EF Core 中创建 TPH 继承层次结构来解决这个问题 "design"。例如:

using Microsoft.EntityFrameworkCore;
using System;
using System.Data;
using System.Linq;

namespace EfCoreTest
{

    public abstract class Lookup
    {
        public int LookupID { get; set; }
        public int LookupTypeID { get; set; }
        public int Key { get; set; }
        public string Value { get; set; }
    }

    public class CustomerTitle : Lookup
    {

    }
    public class PetType : Lookup
    {

    }
    public class Customer
    {
        public int CustomerID { get; set; }
        public CustomerTitle CustomerTitle { get; set; }
        public int CustomerTitleID { get; set; }
        public PetType PetType { get; set; }
        public int PetTypeID { get; set; }
    }


    public class Db : DbContext
    {
        readonly string connectionString;

        public DbSet<Customer> Customers { get; set; }
        public DbSet<Lookup> Lookup { get; set; }
        public DbSet<CustomerTitle> CustomerTitles { get; set; }
        public DbSet<PetType> PetTypes { get; set; }

        public Db(): this("server=.;database=EfCoreTest;Integrated Security=true")
        {

        }
        public Db(string connectionString)
        {
            this.connectionString = connectionString;
        }

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

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Lookup>()
                .HasAlternateKey(e => new { e.LookupTypeID, e.Key });

            modelBuilder.Entity<Lookup>()
                .HasDiscriminator(e => e.LookupTypeID);

            modelBuilder.Entity<CustomerTitle>()
                .HasDiscriminator()
                .HasValue(1);
            modelBuilder.Entity<PetType>()
                .HasDiscriminator()
                .HasValue(2);

            var fks = from et in modelBuilder.Model.GetEntityTypes()
                      from fk in et.GetForeignKeys()
                      where typeof(Lookup).IsAssignableFrom(fk.PrincipalEntityType.ClrType)
                      select fk;
            foreach (var fk in fks)
            {
                fk.DeleteBehavior = DeleteBehavior.Restrict;
            }

            base.OnModelCreating(modelBuilder);
        }
    }



    class Program
    {
        static void Main(string[] args)
        {


            using (var db = new Db())
            {
                db.Database.EnsureDeleted();
                db.Database.EnsureCreated();

                var Mr = new CustomerTitle() { Key = 1, Value = "Mr" };
                var Mrs = new CustomerTitle() { Key = 2, Value = "Mrs" };
                var Miss = new CustomerTitle() { Key = 3, Value = "Miss" };
                db.CustomerTitles.AddRange( new []{ Mr,Mrs,Miss});

                var Dog = new PetType() { Key = 1, Value = "Dog" };
                var Cat = new PetType() { Key = 2, Value = "Cat" };
                var Hamster = new PetType() { Key = 3, Value = "Hamster" };
                db.PetTypes.AddRange(new[] { Dog,Cat,Hamster });

                db.SaveChanges();
            }

            using (var db = new Db())
            {
                var titles = db.CustomerTitles.ToDictionary(t => t.Value);
                var petTypes = db.PetTypes.ToDictionary(t => t.Value);

                var cust = new Customer();
                cust.CustomerTitle = titles["Mr"];
                cust.PetType = petTypes["Hamster"];

                db.SaveChanges();
            }


            Console.WriteLine("Hit any key to exit");
            Console.ReadKey();


        }
    }
}

或者,您可以为查找创建视图,例如:

create or alter view CustomerTitle
as
select [Key] CustomerTitleId, Value
from Lookup
where LookupTypeID = 1