简体   繁体   English

使用子查询构建查询(LINQ)

[英]Building a query (LINQ) with a sub-query

For simplicity sake lets assume I have the following two classes: 为了简单起见,假设我具有以下两个类:

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

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

I have a List<ComplexClass> and I need to build a query based on some parameters. 我有一个List<ComplexClass> ,我需要基于一些参数来构建查询。

It's an easy task if all I need to do is use the Name property of ComplexClass . 如果我需要做的只是使用ComplexClassName属性,这是一个简单的任务。 Here's an example: 这是一个例子:

    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;
    }

Based on the parameters I have I can add more query elements. 根据参数,我可以添加更多查询元素。 Of course the above example is simple, but the actual problem contains more parameters, and that number can grow. 当然,上面的示例很简单,但是实际问题包含更多参数,并且这个数字可能会增加。

Things aren't as simple if I want to find those ComplexClass instances which have SubClass instances which meet criteria based on parameters: 如果要查找具有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;
    }

I can no longer just add bits of the query based on each parameter, I now need to write the whole query in one go, and this means I need to check every combination of parameters, which is hardly ideal. 我不能再仅基于每个参数添加查询的位,现在我需要一次性编写整个查询,这意味着我需要检查参数的每个组合,这并不理想。

I suspect the key is to build an Expression class and create a lambda expression from that, but I'm not sure how to tackle the problem. 我怀疑关键是要构建一个Expression类并从中创建一个lambda表达式,但是我不确定如何解决该问题。

Any suggestions? 有什么建议么? :) :)

EDIT: 编辑:

My initial idea was this: 我最初的想法是这样的:

    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;
    }

This works in my small console test application as it's using LINQ to Objects. 这在我的小型控制台测试应用程序中有效,因为它使用的是LINQ to Objects。 Unfortunately, I need to use Entity Framework and LINQ to Entities, which causes an implementation similar to the one above to throw a A lambda expression with a statement body cannot be converted to an expression tree error message. 不幸的是,我需要使用Entity Framework和LINQ to Entities,这将导致与上述实现类似的实现引发A lambda expression with a statement body cannot be converted to an expression tree错误消息。

I'm assuming that in you real-life code the SubClasses property is IQueryable<SubClass> rather than List<SubClass> ? 我假设在您的真实代码中, SubClasses属性是IQueryable<SubClass>而不是List<SubClass>

If so, then your query building becomes easy: 如果是这样,那么您的查询构建就变得容易了:

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;
}

Mixing IEnumerable<T> and IQueryable<T> using AsQueryable() is never a good idea. 使用AsQueryable()混合IEnumerable<T>IQueryable<T>从来不是一个好主意。

I implemented my solution in a simple Console Project: 我在一个简单的控制台项目中实现了我的解决方案:

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
    }
}

The Console Output is: 控制台输出为:

foo / bar :
foobar

foo / null :
foobar

string.Empty / bar :
foobar
notfoobar

maeh / bar :

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM