【问题标题】:Complex query with filters in Entity Framework CoreEntity Framework Core 中带有过滤器的复杂查询
【发布时间】:2018-05-18 11:36:16
【问题描述】:

我的模型只有 3 个类:User、Filter 和 FilterEntry。

class Filter 
{
    public List<FilterEntry> Inclusions { get; set; } 
    public List<FilterEntry> Exclusions { get; set; } 
}

public class FilterEntry
{
    public string Name { get; set; }
    public int? Age { get; set; }
}

public class User 
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Filter 是持久化到数据库的过滤器,您可以将其视为过滤器的定义。它包含一个或多个定义过滤器的FilterEntries。包含和排除是适用于过滤器的限制。

一个例子:

var filter = new Filter 
    { 
       Inclusions = new[] { new FilterEntry { Age = 33 } }, 
       Exclusions = new[] { new FilterEntry { Name = "John" }, new FilterEntry { Name = "Peter" } }, 
    };

这定义了一个Filter,它将代表 33 岁的人,除了被称为“约翰”或“彼得”的人。因此,当将此过滤器应用于用户时,它应该返回所有 33 岁的用户,除了 Johns 和 Peters。

现在的问题。如何使用实体框架创建一个查询,给定一个过滤器,根据它返回用户?

我什至不知道如何开始!我只有这个:

Filter filter = dbContext.Filters.First(x => x.FilterId == filterId);
var filteredUsers = from u in dbContext.Users
     where ... // user is any of the in filter.Inclusions
     where ... // user is not in any of the filter.Exclusions
     select u;

注意,Filter 和 FilterEntry 保持 1-N 关系。我省略了键以简化代码。

【问题讨论】:

  • 过滤器是否总是名称/年龄,或者您是否排除了其他过滤器要求?
  • 是的,为了简单起见,我排除了一些过滤器,但例如,FilterEntry 可以包含一个导航属性,例如 Country,如果它被指定,它将指定一个包含/排除的 Country。 FilterEntry 的任何属性中的空值意味着未设置给定的过滤器字段。例如,所有属性都为 null 的 FilterEntry,但使用 Country 意味着只有 Country 将适用。这就是 Age 可以为空的原因。 Null 确定何时对成员应用过滤器。

标签: c# entity-framework filter entity-framework-core


【解决方案1】:

你想建立一个动态的 where 子句,所以我推荐PredicateBuilder。我还没有测试过下面的代码,但你会想要...

Filter filter = dbContext.Filters.First(x => x.FilterId == filterId);

var pb = new PredicateBuilder<User>();

foreach(var inclusion in filter.Inclusions)
{
    if(inclusion.Age.HasValue) 
    {
        pb = pb.And(p => p.Age == inclusion.Age.Value);
    }
    if(!string.IsNullOrWhiteSpace(inclusion.Name))
    {
        pb = pb.And(p => p.Name == inclusion.Name);
    }
}

foreach(var exclusion in filter.Exclusions)
{
    if(exclusion.Age.HasValue) 
    {
        pb = pb.And(p => p.Age != exclusion.Age.Value);
    }
    if(!string.IsNullOrWhiteSpace(exclusion.Name))
    {
        pb = pb.And(p => p.Name != exclusion.Name);
    }
}

var filteredUsers = dbContext.Users.AsExpandable().Where(pb);

【讨论】:

  • 感谢@Eric Lease。如果我们在 FilterEntry 中包含一个成员 Country 成员以包含/排除一个 Country,这将如何改变?这将是解决方案仍然有效吗?如果有像 Couple.CountryOfBirth 这样的嵌套过滤器?
  • Eric,我应该安装哪个包才能在 ASP.NET Core 2 中使用 PredicateBuilder。
  • 我找到了。它是 LinqKit.Microsoft.EntityFrameworkCore
【解决方案2】:

我遇到了类似的情况,我希望能够指定这样的动态过滤器。我最终使用了System.Linq.Dynamic.Core NuGet 包来提供帮助 (https://github.com/StefH/System.Linq.Dynamic.Core)。这允许您使用字符串输入运行 Linq 查询。然后,您不必从 Filter 构建 Linq 表达式树,只需将 where 子句构造为字符串。

public class Filter
{
    public List<FilterEntry> Inclusions { get; set; }
    public List<FilterEntry> Exclusions { get; set; }

    public IQueryable<User> ApplyTo<User>(IQueryable<User> queryable)
    {
        // TODO: Dynamically build this string instead of hard-coding it.
        //       You would probably iterate through Inclusions and Exclusions
        //       appending to the where clause as you go.
        var whereClause = "Age == @0 && Name != @1 && Name != @2";
        var params = new object[] { 33, "John", "Peter" };

        return queryable.Where(whereClause, params);
    }
}

我意识到这不是一个可行的解决方案,但希望它至少可以为您提供一个起点。

【讨论】:

    【解决方案3】:

    我用于我的项目:

      public async Task<SocialListViewModel> GetPagedListAsyncResultSearch(SocialSearchRequestResultSearch request, List<string> fields)
        {
    
            //Begin Conditions
    
            var predicate = PredicateBuilder.New<Social>();
    
    
            if (request.Id_parvandeh != 0)
            {
                if (request.logic_Id_parvandeh.Equals("And")) predicate = predicate.And(a => a.Id_parvandeh == request.Id_parvandeh);
                else predicate = predicate.Or(a => a.Id_parvandeh == request.Id_parvandeh);
            }
    }
    

    【讨论】:

      猜你喜欢
      • 2021-08-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-03
      • 1970-01-01
      • 1970-01-01
      • 2021-09-02
      相关资源
      最近更新 更多