LINQ查询组合2个结果集

时间:2015-02-04 22:03:51

标签: c# .net

我有一个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中执行此操作?

3 个答案:

答案 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)

这种行为有两种方法

  • Union,它结合了两个结果集,同时剔除重复项
  • Concat,它简单地将两个结果集拼凑在一起

您选择哪一个取决于所需的行为。请注意,如果您的查询实际上是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