[英]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
. 如果我需要做的只是使用
ComplexClass
的Name
属性,这是一个简单的任务。 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.