在 linq toEntity where 子句中建立两个列表之间的链接

Fla*_*ack 1 c# linq entity-framework

我对如何在 Linq to Entities 中链接两个列表感到Linq很困惑。EF

我首先使用数据库,并且有两个表:

Person,带有列Id

Ability,带有列IdPersonId并且Value

因此,Person类有一个ICollection<Ability>, 称为AllAbilities.

在某些视图的 ViewModel 中,我返回一个 int 列表,表示用户输入的文本框值Ability.Value,称为AbilitiesInput。我的需求很简单,在控制器中我必须调用一个查询来执行以下操作:

GetAll(person =>
    for(i = 0; i < AbilitiesCount; i++) { person.AllAbilities[i] > AbilitiesInput[i] }
)
Run Code Online (Sandbox Code Playgroud)

其中GetAll方法看起来像我的通用存储库中的那样:

public virtual async Task<List<TEntity>> GetAll(
        Expression<Func<TEntity, bool>> wherePredicate = null
{ ... }
Run Code Online (Sandbox Code Playgroud)

要继续,我只需要一个布尔值,它可以检查 every 是否AllAbilities[i]高于AbilitiesInput[i],但我尝试的任何方法都不起作用。
我尝试更改AbilitiesInputList<KeyValuePair>orList<Tuple>但收到错误消息No mapping exists,尝试使用 aSelect创建新对象,还尝试使用IndexOforFindIndex获取不带 foreach 的索引...

如果有人能解释我如何实现这个简单的事情,我会非常非常高兴。
多谢。

Iva*_*oev 5

我对Linq和很陌生EF

你运气不好,因为“这个简单的事情”在 LINQ to Objects 中相对容易,但在 LINQ to Entities 中却相当困难(几乎不可能)。

为了以某种方式解决这个问题,您需要手动构建 LINQ to Entities 兼容的Expression

首先,您需要一些帮助器来构建表达式谓词。PredicateBuilder是一个流行的选择,但它不会生成 EF 兼容的表达式,并且需要存储库内的LinqKit和。AsExpandable因此,我使用以下类似的帮助器,但生成最终兼容的表达式:

public static class PredicateUtils
{
    sealed class Predicate<T>
    {
        public static readonly Expression<Func<T, bool>> True = item => true;
        public static readonly Expression<Func<T, bool>> False = item => false;
    }
    public static Expression<Func<T, bool>> Null<T>() { return null; }
    public static Expression<Func<T, bool>> True<T>() { return Predicate<T>.True; }
    public static Expression<Func<T, bool>> False<T>() { return Predicate<T>.False; }
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
    {
        if (Equals(left, right)) return left;
        if (left == null || Equals(left, True<T>())) return right;
        if (right == null || Equals(right, True<T>())) return left;
        if (Equals(left, False<T>()) || Equals(right, False<T>())) return False<T>();
        var body = Expression.AndAlso(left.Body, right.Body.Replace(right.Parameters[0], left.Parameters[0]));
        return Expression.Lambda<Func<T, bool>>(body, left.Parameters);
    }
    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
    {
        if (Equals(left, right)) return left;
        if (left == null || Equals(left, False<T>())) return right;
        if (right == null || Equals(right, False<T>())) return left;
        if (Equals(left, True<T>()) || Equals(right, True<T>())) return True<T>();
        var body = Expression.OrElse(left.Body, right.Body.Replace(right.Parameters[0], left.Parameters[0]));
        return Expression.Lambda<Func<T, bool>>(body, left.Parameters);
    }

    static Expression Replace(this Expression expression, Expression source, Expression target)
    {
        return new ExpressionReplacer { Source = source, Target = target }.Visit(expression);
    }

    class ExpressionReplacer : ExpressionVisitor
    {
        public Expression Source;
        public Expression Target;
        public override Expression Visit(Expression node)
        {
            return node == Source ? Target : base.Visit(node);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

其次,在控制器中为单个标准定义一个辅助方法,如下所示

static Expression<Func<Person, bool>> AbilityFilter(int index, int value)
{
    return p => p.AllAbilities.OrderBy(a => a.Id).Skip(index).Take(1).Any(a => a.Value > value);
}
Run Code Online (Sandbox Code Playgroud)

最后,构建过滤器并将其传递给GetAll方法:

var filter = PredicateUtils.Null<Person>();
for (int i = 0; i < AbilitiesInput.Count; i++)
    filter = filter.And(AbilityFilter(i, AbilitiesInput[i]));
GetAll(filter);
Run Code Online (Sandbox Code Playgroud)

所使用的技术绝对不适合新手,但我认为没有简单的方法可以解决该特定问题。