C#,Linq2Sql:是不是可以将两个可查询对象合并为一个?
Posted
技术标签:
【中文标题】C#,Linq2Sql:是不是可以将两个可查询对象合并为一个?【英文标题】:C#, Linq2Sql: Is it possible to concatenate two queryables into one?C#,Linq2Sql:是否可以将两个可查询对象合并为一个? 【发布时间】:2010-10-08 20:28:50 【问题描述】:我有一个可查询的,我使用了各种 Where
和 WhereBetween
语句将集合缩小到某个集合。现在我需要添加一种Where || WhereBetween
。换句话说,我不能像到目前为止那样将它们链接在一起,因为这将作为 And 起作用。那么,我该怎么做呢?
我看到了两种可能性:
-
从我拥有的一个中创建两个可查询项,一个使用
Where
,一个使用WhereBetween
。 然后将它们连接起来。不知道这是否可能?此外,虽然不是在我的特定情况下,但您很可能最终会出现重复...
以某种方式将Where
表达式和WhereBetween
中创建的表达式与某种或合并。
第一个,如上所述,我不确定是否可能。如果是这样,我不太确定这是一个好方法。
第二个,我可以将其视为一个选项,但不能完全确定所有细节。下面是我另一个问题中的WhereBetween
方法,我现在使用它并且效果很好:
public static IQueryable<TSource> WhereBetween<TSource, TValue>(
this IQueryable<TSource> source,
Expression<Func<TSource, TValue>> selector,
IEnumerable<Range<TValue>> ranges)
var param = Expression.Parameter(typeof(TSource), "x");
var member = Expression.Invoke(selector, param);
Expression body = null;
foreach (var range in ranges)
var filter = Expression.AndAlso(
Expression.GreaterThanOrEqual(member,
Expression.Constant(range.A, typeof(TValue))),
Expression.LessThanOrEqual(member,
Expression.Constant(range.B, typeof(TValue))));
body = body == null ? filter : Expression.OrElse(body, filter);
return body == null ? source : source.Where(
Expression.Lambda<Func<TSource, bool>>(body, param));
我在想我可以将它的表达式构建部分提取到一个新方法中。可能是这样的:
public static IQueryable<TSource> WhereBetween<TSource, TValue>(
this IQueryable<TSource> source,
Expression<Func<TSource, TValue>> selector,
IEnumerable<Range<TValue>> ranges)
return source.Where(WhereBetween(selector, ranges));
public static Expression<Func<TSource, bool>> WhereBetween<TSource, TValue>(
Expression<Func<TSource, TValue>> selector,
IEnumerable<Range<TValue>> ranges)
var param = Expression.Parameter(typeof(TSource), "x");
var member = Expression.Invoke(selector, param);
Expression body = null;
foreach (var range in ranges)
var filter = Expression.AndAlso(
Expression.GreaterThanOrEqual(member,
Expression.Constant(range.A, typeof(TValue))),
Expression.LessThanOrEqual(member,
Expression.Constant(range.B, typeof(TValue))));
body = body == null ? filter : Expression.OrElse(body, filter);
return body == null
? ø => true
: Expression.Lambda<Func<TSource, bool>>(body, param);
然后我可以使用这个新方法来获取表达式而不是可查询的。所以,假设我有WhereBetween(ø => ø.Id, someRange)
,例如ø => ø.SomeValue == null
。我怎样才能将这两者与 Or 结合起来?我正在查看WhereBetween
方法中使用的Expression.OrElse
,我认为这可能是我需要的,或者这可能是Expression.Or
。但是我对这个表达的东西很不稳定,所以我不确定在这里选择什么,或者即使我在正确的轨道上:p
有人可以在这里给我一些指示吗?
【问题讨论】:
【参考方案1】:这里有两个选项 - Queryable.Union
,或表达式组合。我通常倾向于后者,通过OrElse
- (至少使用 LINQ-to-SQL)你可以使用 2 个表达式(见下文) - 但无论哪种情况,它都应该组合起来:
using(var ctx = new DataClasses1DataContext())
ctx.Log = Console.Out;
Expression<Func<Customer, bool>> lhs =
x => x.Country == "UK";
Expression<Func<Customer, bool>> rhs =
x => x.ContactName.StartsWith("A");
var arr1 = ctx.Customers.Where(
lhs.OrElse(rhs)).ToArray();
var arr2 = ctx.Customers.Where(lhs)
.Union(ctx.Customers.Where(rhs)).ToArray();
arr1
和 arr2
都只执行 1 次数据库命中(尽管 TSQL 不同;第一个在 WHERE
子句中有一个 OR
;第二个有两个单独的查询 UNION
)。
这是我使用的扩展方法:
static Expression<Func<T, bool>> OrElse<T>(
this Expression<Func<T, bool>> lhs,
Expression<Func<T, bool>> rhs)
var row = Expression.Parameter(typeof(T), "row");
var body = Expression.OrElse(
Expression.Invoke(lhs, row),
Expression.Invoke(rhs, row));
return Expression.Lambda<Func<T, bool>>(body, row);
【讨论】:
那排的东西可能是对的吗? (当然是任何字符串) 考虑 sql 性能等,在WHERE
clause 中使用UNION
或OR
是否更有效?
非常好。这个问题不断出现(变化形式),而您的 OrElse 可能是解决所有问题的灵丹妙药。
是的,它可以是“row”、“x”或“Fred”。重新性能:您需要对其进行分析...不同的查询我的行为不同。 一般 OR 会更有效,但在某些情况下 UNION 会更好;以及两个单独的查询更好的情况。
不错。表达的东西慢慢但稳定地开始有意义了,呵呵。看起来它会起作用,虽然我要到星期一才能完全测试它。可能会坚持使用 OR,因为这在 general 中更有效 =) 再次感谢您!以上是关于C#,Linq2Sql:是不是可以将两个可查询对象合并为一个?的主要内容,如果未能解决你的问题,请参考以下文章
Linq2Sql - 存储复杂的Linq查询以便将来动态执行 - 原始文本 - 可能吗?
C# 在 Linq 查询 WHERE 语句中返回两个纬度/经度坐标之间的计算距离