c# - 在 linq to entities where 子句中建立两个列表之间的链接

标签 c# entity-framework linq

我对 Linq 还很陌生和 EF我对如何将 Linq 中的两个列表链接到实体感到困惑。

我正在使用数据库优先并且我有两个表:

Person , 列 Id

Ability , 列 Id , PersonIdValue

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

在某些 View 的 ViewModel 中,我得到一个 int 列表,表示用户为 Ability.Value 输入的文本框值, 称为 AbilitiesInput . 我的需求很简单,在 Controller 中我必须调用一个查询来执行以下操作:

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

在哪里GetAll方法在我的通用仓库中看起来像这样:

public virtual async Task<List<TEntity>> GetAll(
        Expression<Func<TEntity, bool>> wherePredicate = null
{ ... }

要恢复,我只需要一个 bool 值,它可以检查是否每个 AllAbilities[i]高于AbilitiesInput[i] ,但我尝试过的都没有用。
我试图改变 AbilitiesInputList<KeyValuePair>List<Tuple>但得到一个错误说 No mapping exists , 尝试使用 Select创建一个新对象,也尝试使用 IndexOfFindIndex在没有 foreach 的情况下获取索引...

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

最佳答案

I'm pretty new to Linq and EF

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

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

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

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);
        }
    }
}

其次,在您的 Controller 中为这样的单个条件定义一个辅助方法

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);
}

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

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

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

关于c# - 在 linq to entities where 子句中建立两个列表之间的链接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36246162/

相关文章:

c# - 使用 MVVMLight 在 ViewModel 之间传递参数

c# - 如何使用 Spreadsheet Gear C# 将 IRange 中所有日期格式的单元格转换为 DateTime?

c# - 使用 LINQ 计算列表中项目的出现次数

c# - 从具有特定类名的元素中选择值

c# - 将图像映射到 asp.net mvc 中的页面

c# - 如何通过委托(delegate)查询方法属性?

c# - 如何将 DispatcherObject (BitmapSource) 复制到不同的线程中?

c# - 如何对具有相同属性名称的多个属性使用 select 方法

c# - Count Query generation in table-per-type inheritance

entity-framework - 在 EF Core 2.0 中使用 OwnsOne 方法映射属性