我对 Linq
还很陌生和 EF
我对如何将 Linq 中的两个列表链接到实体感到困惑。
我正在使用数据库优先并且我有两个表:
Person
, 列 Id
和
Ability
, 列 Id
, PersonId
和 Value
因此,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]
,但我尝试过的都没有用。
我试图改变 AbilitiesInput
至 List<KeyValuePair>
或 List<Tuple>
但得到一个错误说 No mapping exists
, 尝试使用 Select
创建一个新对象,也尝试使用 IndexOf
或 FindIndex
在没有 foreach 的情况下获取索引...
如果有人能向我解释如何实现这个简单的事情,我会非常非常高兴。
非常感谢。
最佳答案
I'm pretty new to
Linq
andEF
你运气不好,因为“这个简单的事情”在 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);
}
}
}
其次,在您的 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/