如何在实体框架中的数据库级别进行复杂的“OR”过滤器?

问题描述 投票:0回答:2

执行以下 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; }
}
c# .net entity-framework linq entity-framework-core
2个回答
0
投票

简单翻译

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

0
投票

您可以使用 LinqKit 来构建查询:

   using LinqKit;

   try
    {
        var predicate = PredicateBuilder.New<Model>(false);

        foreach (var item in filters)
        {
            predicate = predicate.Or(x => x.Foo == item.Foo && x.Bar == item.Bar);
        }

        var result = context.Models.Where(predicate).ToList();

        result.Dump();

    }
    catch (InvalidOperationException ex)
    {
        Console.WriteLine(ex.Message);
    }
© www.soinside.com 2019 - 2024. All rights reserved.