【问题标题】:Building a query (LINQ) with a sub-query使用子查询构建查询 (LINQ)
【发布时间】:2012-10-23 10:13:36
【问题描述】:

为简单起见,假设我有以下两个类:

public class ComplexClass
{
    public List<SubClass> SubClasses { get; set; }
    public string Name { get; set; }
}

public class SubClass
{
    public string Name { get; set; }
}

我有一个List&lt;ComplexClass&gt;,我需要根据一些参数构建一个查询。

如果我需要做的只是使用ComplexClassName 属性,这将是一项简单的任务。这是一个例子:

    static IQueryable<ComplexClass> GetQuery(string someParameter, string someOtherParameter)
    {
        var query = list.AsQueryable();
        if (!String.IsNullOrEmpty(someParameter))
            query = query.Where(c => c.Name.StartsWith(someParameter));
        if (!String.IsNullOrEmpty(someOtherParameter))
            query = query.Where(c => c.Name.EndsWith(someOtherParameter));
        return query;
    }

根据我拥有的参数,我可以添加更多查询元素。当然上面的例子很简单,但是实际的问题包含更多的参数,而且这个数量可以增长。

如果我想找到那些具有SubClass 实例且符合基于参数的条件的ComplexClass 实例,事情就没有那么简单了:

    static IQueryable<ComplexClass> GetSubQuery(string someParameter, string someOtherParameter)
    {
        var query = list.AsQueryable();
        if (!String.IsNullOrEmpty(someParameter))
            if (!String.IsNullOrEmpty(someOtherParameter))
                return query.Where(c => c.SubClasses.Where(sc => sc.Name.StartsWith(someParameter) && sc.Name.EndsWith(someOtherParameter)).Any());
            else
                return query.Where(c => c.SubClasses.Where(sc => sc.Name.StartsWith(someParameter)).Any());
        else
            if (!String.IsNullOrEmpty(someOtherParameter))
                return query.Where(c => c.SubClasses.Where(sc => sc.Name.EndsWith(someOtherParameter)).Any());
            else
                return null;
    }

我不能再根据每个参数添加查询的位,我现在需要一次编写整个查询,这意味着我需要检查每个参数组合,这并不理想。

我怀疑关键是构建一个Expression 类并从中创建一个 lambda 表达式,但我不确定如何解决这个问题。

有什么建议吗? :)

编辑:

我最初的想法是这样的:

    static IQueryable<ComplexClass> GetSubQuery(string someParameter, string someOtherParameter)
    {
        var query = list.AsQueryable();
        query = query.Where(c =>
        {
            var subQuery = c.SubClasses.AsQueryable();
            if (!String.IsNullOrEmpty(someParameter))
                subQuery = subQuery.Where(sc => sc.Name.StartsWith(someParameter));
            if (!String.IsNullOrEmpty(someOtherParameter))
                subQuery = subQuery.Where(sc => sc.Name.EndsWith(someOtherParameter));
            return subQuery.Any();
        });
        return query;
    }

这适用于我的小型控制台测试应用程序,因为它使用 LINQ to Objects。不幸的是,我需要使用 Entity Framework 和 LINQ to Entities,这会导致类似于上面的实现抛出 A lambda expression with a statement body cannot be converted to an expression tree 错误消息。

【问题讨论】:

  • 您使用“someParameter”和“someOtherParameter”只是为了检查它们是否为空,实际上并未在where条件下使用,那么它们的作用是什么?看起来它们与大小写有关。 ..解释更多...
  • 您可以将.Where(predicate).Any() 替换为.Any(predicate)
  • Amol:参数可以为空,是的,但在实际的最终代码中用于查询。事实上,这些例子可能看起来有点傻。我将编辑示例。丹尼尔:我知道,但这不仅仅是问题。

标签: linq c#-4.0 lambda


【解决方案1】:

我假设在您的实际代码中 SubClasses 属性是 IQueryable&lt;SubClass&gt; 而不是 List&lt;SubClass&gt;

如果是这样,那么您的查询构建就变得容易了:

static IQueryable<ComplexClass> GetSubQuery(
    string someParameter, string someOtherParameter)
{
    var query = list.AsQueryable();
    if (!String.IsNullOrEmpty(someParameter))
        query = query.Where(c => c.SubClasses
            .Where(sc => sc.Name.StartsWith(someParameter)).Any());
    if (!String.IsNullOrEmpty(someOtherParameter))
        query = query.Where(c => c.SubClasses
            .Where(sc => sc.Name.StartsWith(someOtherParameter)).Any());
    return query;
}

使用AsQueryable() 混合IEnumerable&lt;T&gt;IQueryable&lt;T&gt; 绝不是一个好主意。

【讨论】:

  • 哦,天哪...一直都很简单...>_
  • Wait... 这种方法(假设所有参数都不为空)不会导致查询需要很长时间才能执行吗?我只是想使用SelectMany 创建一个子查询,像我在第一个示例中的查询一样构建它,然后执行query.Where(cc=&gt;cc.SubClasses.Where(sc=&gt;subQuery.Contains(sc)).Any())... 这会起作用,但我不确定哪个是更快(如果有区别的话)。
  • @Shaamaan - 如果我是你,我不确定我是否会担心速度,直到你确定你有性能问题。即使它重复子查询,缓存也会极大地加快第二个子查询的速度。
  • 好点。 :) 我想这是 KISS 的一个完美例子。 >_
【解决方案2】:

我在一个简单的控制台项目中实现了我的解决方案:

internal class Program
{
    #region Constants and Fields

    private static readonly List<ComplexClass> list = new List<ComplexClass>
                                                          {
                                                              new ComplexClass
                                                                  {
                                                                      Name = "complex", 
                                                                      SubClasses = new List<SubClass>
                                                                                       {
                                                                                           new SubClass
                                                                                               {
                                                                                                   SubName = "foobar"
                                                                                               }
                                                                                       }
                                                                  }, 
                                                              new ComplexClass
                                                                  {
                                                                      Name = "complex", 
                                                                      SubClasses = new List<SubClass>
                                                                                       {
                                                                                           new SubClass
                                                                                               {
                                                                                                   SubName = "notfoobar"
                                                                                               }
                                                                                       }
                                                                  }
                                                          };

    #endregion

    #region Public Methods

    public static void Main(string[] args)
    {
        Console.WriteLine("foo / bar :");
        GetSubQuery("foo", "bar");
        Console.WriteLine();

        Console.WriteLine("foo / null :");
        GetSubQuery("foo", null);
        Console.WriteLine();

        Console.WriteLine("string.Empty / bar :");
        GetSubQuery(string.Empty, "bar");
        Console.WriteLine();

        Console.WriteLine("maeh / bar :");
        GetSubQuery("maeh", "bar");

        Console.ReadKey();
    }

    #endregion

    #region Methods

    private static void GetSubQuery(string startsWith, 
        string endsWith)
    {
        var query = from item in list
                    let StartIsNull = string.IsNullOrEmpty(startsWith)
                    let EndIsNull = string.IsNullOrEmpty(endsWith)
                    where
                        (StartIsNull || item.SubClasses.Any(sc => sc.SubName.StartsWith(startsWith)))
                        && (EndIsNull || item.SubClasses.Any(sc => sc.SubName.EndsWith(endsWith)))
                    select item;

        foreach (var complexClass in query)
        {
            Console.WriteLine(complexClass.SubClasses.First().SubName);
        }
    }

    #endregion

    public class ComplexClass
    {
        #region Public Properties

        public string Name { get; set; }
        public List<SubClass> SubClasses { get; set; }

        #endregion
    }

    public class SubClass
    {
        #region Public Properties

        public string SubName { get; set; }

        #endregion
    }
}

控制台输出是:

foo / bar :
foobar

foo / null :
foobar

string.Empty / bar :
foobar
notfoobar

maeh / bar :

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-28
    • 2021-03-03
    • 1970-01-01
    相关资源
    最近更新 更多