您如何在实体框架的数据库级别执行复杂的“或”过滤器?

Posted

技术标签:

【中文标题】您如何在实体框架的数据库级别执行复杂的“或”过滤器?【英文标题】:How do you do complex "OR" filters at the database level in Entity Framework? 【发布时间】:2022-01-15 12:52:46 【问题描述】:

执行以下 SQL 查询:

select * from model where (Foo = 'a' and Bar = 'b') or (Foo = 'b' and Bar = 'b')

如果您不知道应用的过滤器数量,您如何将其转换为在数据库级别工作的实体框架表达式?

我制作了以下程序来演示我在说什么以及我已经尝试过什么。如果不首先从数据库中取回所有内容而不使用表达式树,我就无法找到一种应用过滤器的方法,这似乎有点矫枉过正。

using Microsoft.EntityFrameworkCore;

var contextOptions = new DbContextOptionsBuilder<TestContext>()
    .UseInMemoryDatabase("testdb")
    .Options;

using (var context = new TestContext(contextOptions))

    context.Database.EnsureCreated();

    var models = new Model[]
    
        new Model
        
            Foo = "a",
            Bar = "a"
        ,
        new Model
        
            Foo = "a",
            Bar = "b"
        ,
        new Model
        
            Foo = "b",
            Bar = "a"
        ,
        new Model
        
            Foo = "b",
            Bar = "b"
        ,
    ;
    await context.AddRangeAsync(models);
    await context.SaveChangesAsync();

    var filters = new Filter[]
    
        new Filter
        
            Foo = "a",
            Bar = "b"
        ,
        new Filter
        
            Foo = "b",
            Bar = "b"
        
    ;

    Console.WriteLine("Complex object:");
    try
    
        var objectFilteredModels = await context.Models
            .Where(m => filters.Any(f => f.Foo == m.Foo && f.Bar == m.Bar))
            .ToListAsync();
    
    catch (InvalidOperationException ex)
    
        Console.WriteLine(ex.Message);
    

    Console.WriteLine("\nDictionary:");
    var filterDictionary = filters.ToDictionary(f => f.Foo, f => f.Bar);
    try
    
        var dictionaryFilteredModels = await context.Models
            .Where(m => filterDictionary.Keys.Any(k => k == m.Foo && filterDictionary[k] == m.Bar))
            .ToListAsync();
    
    catch (InvalidOperationException ex)
    
        Console.WriteLine(ex.Message);
    


    Console.WriteLine("\nSeparate arrays:");
    var foos = filters.Select(f => f.Foo).ToList();
    var bars = filters.Select(f => f.Bar).ToList();
    try
    
        var arraysFilteredModels = await context.Models
            .Where(m => foos.Any(f => f == m.Foo && bars.ElementAt(foos.IndexOf(f)) == m.Bar))
            .ToListAsync();
    
    catch (InvalidOperationException ex)
    
        Console.WriteLine(ex.Message);
    

    Console.WriteLine("\nNon-DB query:");
    var allModels = await context.Models.ToListAsync();
    var filteredModels = allModels.Where(m => filters.Any(f => f.Foo == m.Foo && f.Bar == m.Bar)).ToList();
    Console.WriteLine($"no error, filtered model count: filteredModels.Count");



public class TestContext : DbContext

    public TestContext()  
    public TestContext(DbContextOptions<TestContext> options)
        : base(options)  

    public DbSet<Model> Models => Set<Model>();


public class Model

    public int Id  get; set; 
    public string Foo  get; set;  = null!;
    public string? Bar  get; set; 
    

public class Filter

    public string Foo  get; set;  = null!;
    public string? Bar  get; set; 

【问题讨论】:

您将需要使用类似LINQKit 的东西来构建Expression 树或类似Dynamic LINQ 的东西来将filters 数组的字符串表示形式转换为Expresson 树。真的没有办法解决它。我会推荐 LINQKit 或滚动您自己的简化版本。 ***.com/q/14621450/861716 【参考方案1】:

直译

select * from model where (Foo = 'a' and Bar = 'b') or (Foo = 'b' and Bar = 'b')

from m in db.Models 
where (m.Foo == 'a' && m.Bar == 'b') || (m.Foo == 'b' && m.Bar == 'b')
select m

【讨论】:

是的,但是如果您不知道filters 对象的内容/长度怎么办?我将编辑我的问题以使其更清楚。

以上是关于您如何在实体框架的数据库级别执行复杂的“或”过滤器?的主要内容,如果未能解决你的问题,请参考以下文章

实体框架6过滤您访问的数据

如果不支持包含,您如何在 LINQ to Entities(实体框架)中执行 SQL 样式的“IN”语句?

在 MVC 5 中使用实体框架

如何使用实体框架或 MVC 项目绑定 HTML 选择元素?

如何在实体框架中创建只读实体?

C#,Winform绑定实体框架(Entity Framework)的实体,如何去掉或隐藏导航属性?