我有一节课T:
class T {
int Prop1Id;
int Prop2Id;
}
在运行时,在内存中,我有:
var L1 = new List<T>();
L1包含以下T对象:
Prop1 Prop2
12 5
6 7
8 9
10 12
我无法执行以下操作:
attributes.Where(x=>L1.Any(y=>y.Prop1ID == x.Prop1ID && y.Prop2ID == x.Prop2ID))
attributes
是EF类型(即:在DB中)
这不起作用,因为它无法创建T的常量值类型 - 它只需要原语和枚举。
有没有人想出一个解决方法,除了带回所有属性,所以它们在内存中是IEnumerable而不是IQueryable?对我来说,这不是一个真正的选择: - (
答案 0 :(得分:2)
假设您提供的内容,2个属性为 整数 类型。所以你可以尝试这种解决方法。我们需要将您的列表转换为另一个字符串键列表,如下所示:
var stringKeys = L1.Select(e => e.Prop1 + "_" + e.Prop2);
//then use stringKeys normally
attributes.Where(x=> stringKeys.Contains(x.Prop1ID + "_" + x.Prop2ID));
如果不是这种情况(不限于整数属性),我们可能必须通过基于列表L1
构建Where条件,以更复杂的方式执行此操作:
var result = attributes;
foreach(var e in L1){
var k1 = e.Prop1;
var k2 = e.Prop2;
var sub = attributes.Where(x => k1 == x.Prop1ID && k2 == x.Prop2ID);
result = result.Concat(sub);
}
//then access result instead of attributes
上述方法效率可能稍低。在连接(L1.Count
)所有结果之前,它将对数据库表执行WHERE
次UNION ALL
次。但是我相信如果它在关键列上执行,性能仍然很好。
最后一个解决方案是尝试使用表达式树(表示在Where
中传递的谓词):
//suppose your attributes is a set of Attribute
var p = Expression.Parameter(typeof(Attribute));
var p1 = Expression.Property(p, "Prop1ID");
var p2 = Expression.Property(p, "Prop2ID");
var initBool = Expression.Constant(false) as Expression;
//build the BodyExpression for the predicate
var body = L1.Aggregate(initBool, (c,e) => {
var ep1 = Expression.Constant(e.Prop1);
var ep2 = Expression.Constant(e.Prop2);
var and = Expression.And(Expression.Equal(ep1, p1), Expression.Equal(ep2,p2));
return Expression.Or(c, and);
});
attributes.Where(Expression.Lambda<Func<Attribute, bool>>(body, p));
请注意,x.Prop1ID
的类型应与L1.Prop1
的每个元素的类型完全匹配(例如,应该同时为int
或float
,...)。相同的条件适用于所有相应的属性。否则,您需要修改代码以构建表达式树(这里的原因是Expression.Equal
要求2个操作数表达式具有相同的数据类型)。我没有使用Expression树测试最后一个代码,但是这个想法很清楚。
涉及Expression(内部)的最后一个选项是尝试使用LinqKit
。我甚至没有使用过这个库,但它可以帮助你在许多情况下免于构建表达式树。如果它可以帮助您运行原始查询而不会出现您提到的异常,请试一试。