我有一个C#LINQ查询,它有一个主查询,然后是另外两个查询,具体取决于变量是否设置为0。
查询正在运行,但我需要组合结果集并返回该结果。
我希望最终的结果集包含两个子查询的结果。有点像你在SQL查询中:
SELECT * FROM myTable WHERE column1 = 'abc' OR column2 = 'xyz'
现在,我认为它使用的是AND而不是OR
var GeoLocations = rows.Select(r => new ElementSearchGeoLocation(r))
.Where(t => (t.GeoLocationType == "State" && t.CanViewState(t.GeoLocationState, user)) ||
(t.GeoLocationType == "City" && t.CanViewCity(t.GeoLocationCity, user)));
if(SystemList != 0)
{
GeoLocations = GeoLocations.Where(t => (dto.SystemList.Contains(t.SystemID)));
}
if (groupList != 0)
{
GeoLocations = GeoLocations.Where(t => (dto.groupList.Contains(t.PoliticalID)));
}
return Ok(GeoLocations);
有没有办法在LINQ中执行此操作?
答案 0 :(得分:3)
以下是一个PredicateBuilder
的实现,它能够Or
将两个不同的表达式组合在一起:
public static class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
public static Expression<Func<T, bool>> Or<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(
expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(
expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
}
}
依赖于以下代码,能够将一个表达式的所有实例替换为另一个:
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
internal static class ExpressionExtensions
{
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
}
使用此功能,您现在可以写:
var GeoLocations = rows.Select(r => new ElementSearchGeoLocation(r))
.Where(t => (t.GeoLocationType == "State" && t.CanViewState(t.GeoLocationState, user)) ||
(t.GeoLocationType == "City" && t.CanViewCity(t.GeoLocationCity, user)));
var predicate = PredicateBuilder.False();
if(SystemList != 0)
{
predicate = predicate.Or(t => dto.SystemList.Contains(t.SystemID));
}
if (groupList != 0)
{
predicate = predicate.Or(t => dto.groupList.Contains(t.PoliticalID));
}
return Ok(GeoLocations.Where(predicate));
答案 1 :(得分:1)
这种行为有两种方法
您选择哪一个取决于所需的行为。请注意,如果您的查询实际上是IQueryable
并且正在从数据库运行(通过linq-to-sql或Entity Framework等),则其中任何一个都可能有效,也可能无效。
正如已经提到的,不要忘记LINQ
结果被延迟评估,并且可以安全地保存和重新查询此部分查询。
答案 2 :(得分:1)
使用Concat
添加其他行。为了使代码超级最小,我将存储初始的`Select first:
var AllLocations = rows.Select(r => new ElementSearchGeoLocation(r));
var mainQuery = AllLocations.Where(t => (t.GeoLocationType == "State" && t.CanViewState(t.GeoLocationState, user)) ||
(t.GeoLocationType == "City" && t.CanViewCity(t.GeoLocationCity, user)));
然后:
IEnumerable<GeoLocation> subQuery;
if (SystemList != 0)
subQuery = AllLocations.Where(...);
else
subQuery = AllLocations.Where(...);
var GeoLocations = mainQuery.Concat(subQuery);
如果您关心重复项,可以在最后一步使用Union
代替Concat
。