我还很陌生,Linq而且EF对于如何将Linq中的两个列表链接到实体,我深感困惑。
Linq
EF
我正在使用数据库优先,并且我有两个表:
Person与柱Id 和 Ability与列Id,PersonId并Value
Person
Id
Ability
PersonId
Value
因此,Personclass具有一个ICollection<Ability>,称为AllAbilities。
ICollection<Ability>
AllAbilities
在某些View的ViewModel中,我返回了一个int列表,该列表表示用户为所输入的文本框值Ability.Value(称为)AbilitiesInput。我的需求很简单,在Controller中,我必须调用一个查询,该查询将执行以下操作:
Ability.Value
AbilitiesInput
GetAll(person => for(i = 0; i < AbilitiesCount; i++) { person.AllAbilities[i] > AbilitiesInput[i] } )
凡GetAll方法看起来像在我的通用回购:
GetAll
public virtual async Task<List<TEntity>> GetAll( Expression<Func<TEntity, bool>> wherePredicate = null { ... }
要恢复,我只需要一个布尔值就可以检查每个值是否都AllAbilities[i]高于AbilitiesInput[i],但是我尝试过的任何方法都没有起作用。 我尝试更改AbilitiesInput为List<KeyValuePair>或,List<Tuple>但出现错误说No mapping exists,尝试使用A Select创建一个新对象,也尝试使用IndexOf或FindIndex获取没有foreach的索引…
AllAbilities[i]
AbilitiesInput[i]
List<KeyValuePair>
List<Tuple>
No mapping exists
Select
IndexOf
FindIndex
如果有人能解释我如何实现这一简单目标,我将非常高兴。 非常感谢。
我是很新,Linq和EF
您很不走运,因为“简单的事情”在LINQ to Objects中相对容易,但是在LINQ to Entities中却相当困难(几乎不可能)。
为了以某种方式解决它,您需要手动构建LINQ to Entitiescompatible Expression。
Expression
首先,您需要一些帮助程序来构建表达谓词。PredicateBuilder是一个流行的选择,但它不会产生EF兼容表情和要求LinqKit,并AsExpandable在仓库里。因此,我使用以下类似的辅助方法,但会产生最终的兼容表达式:
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); } } }
其次,在控制器中为这样的单个条件定义一个辅助方法
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);
使用的技术绝对不是新手,但我认为没有简单的方法可以解决该特定问题。