存储库中 Entity Framework 中 where 子句中的复杂表达式

Complex expression in where clause in Entity Framework in repository

我得到了以下适用于模型数据的表达式 - 特此不使用 Entity Framework:

 public static Expression<Func<Resource, bool>> FilterResourcesByUserCriteria(IEnumerable<FilterValue> filterValuesForUser)
    {
      Expression<Func<Resource, bool>> filter = (resource) =>
      // Get filter values for the current resource in the loop

     resource.ResourceFilterValues

     // Group filter values for user
    .GroupBy(filterValue => filterValue.FilterValue.FilterGroup.Id)

    // Each group must fulfill the following logic
    .All(filterGroup =>

    // For each filter group, only select the user values from the same group
    filterValuesForUser
    .Where(filterValueForUser => filterValueForUser.FilterGroup.Id == filterGroup.Key)
    .Select(filterValueForUser => filterValueForUser.FilterValue1)

     // Each group must at least one value in the sublist of filter values of the current user
    .Any(filterValueForUser => filterGroup
      .Select(resourceFilterValue => resourceFilterValue.FilterValue.FilterValue1)
      .Any(x => x == filterValueForUser))
 );

}

但是,当我尝试将此表达式插入存储库方法的 where 子句时(使用 Entity Framework)出现了这个著名的异常:

Unable to create a constant value of type. Only primitive types or enumeration types are supported in this context.

我怀疑这与一个名为 filterValuesForUser 的参数有关,它是一个复杂(即自定义)类型的集合。

在我执行与 Entity Framework 不直接相关的子查询的 Entity Framework 中,这种行为甚至可能吗?我在这里想要实现的是为查询中的每个组查询自定义列表的子集。

这个或其他解决方法的任何解决方案?我想尽量减少数据库调用的数量,最好将其限制为一次。

使用 LinqToEF 无法实现您要求的确切查询(由于 SQL 的限制)。但不要害怕。稍作调整即可解决您的问题。

public static Expression<Func<Resource, bool>> FilterResourcesByUserCriteria(FilterValue filterValueForUser)
{
    //I assume you can write this part yourself.
}

public IQueryable<Resource> GetResources()
{
    IQueryable<Resource> resources = _context.Resources;
    IEnumerable<FilterValue> filterValuesForUser  = GetFilterValues();

    IEnumerable<IQueryable<Resource>> queries = from filter in filterValuesForUser
                                                let filterExp = FilterResourcesByUserCriteria(filter)
                                                select resources.Where(filterExp);
    return Enumerable.Aggregate(queries, (l, r) => Queryable.Concat(l, r));

}

为清楚起见,扩展了类型和扩展方法。

除了 Aron 的回答之外,我还使用了 LinqKit 程序集中的 PredicateBuilder 实用程序来生成 1 个表达式,而不是多个单独的表达式。这也避免了进行多次数据库调用。

以下是实现此目的的方法(伪代码):

public IQueryable<Resource> GetResources()
{      
       MyContext ctx = new MyContext ();
       IEnumerable<Expression<Func<Resource, bool>>> queries =
            filterValuesForUser.GroupBy(x => x.FilterGroup)
            .Select(filter => SecurityFilters.FilterResourcesByUserCriteriaEF(filter.Select(y => y.FilterValue1)))
            .Select(filterExpression => { return filterExpression; });

   Expression<Func<Resource, bool>> query = PredicateBuilder.True<Resource>();
        foreach (Expression<Func<Resource, bool>> filter in queries)
        {
            query = query.And(filter);
        }

        return ctx.Resources.AsExpandable().Where(query);
}      


public static Expression<Func<Resource, bool>> FilterResourcesByUserCriteriaEF(IEnumerable<string> filterValuesForUser)
{
        // From the resource's filter values, check if there are any present in the user's filter values
        return (x) => x.ResourceFilterValues.Any(y => filterValuesForUser.Contains(y.FilterValue.FilterValue1));
}

我仍然无法在我的存储库中使用它,但这与阻止 AsExpandable() 正常工作有关。