如何在 C# 中使用 Func / Expressions 创建查询对象?
How to create a query object with Func / Expressions in C#?
我想为我使用 ElasticSearch 的项目创建一个可重用的查询对象。我已经在使用 LongLe 的类似 QueryObject from the Generic Unit of Work/Repositories 来使用 Entity Framework.
对数据库进行查询
我似乎无法完全理解如何执行此操作 - 我不确定如何 "chain" 将 lambda 表达式的各个部分组合在一起。我试过使用 Expression/Func 但这对我来说是一个我不完全理解的新领域。我感觉好像我只是在黑暗中刺伤。
因为我什至不知道如何准确表达我的问题,下面是我目前正在做的事情、我正在尝试做的事情以及我目前的进展的一个例子:
我目前要做的事情:
ISearchResponse<DemoIndexModel> result = client.Search<DemoIndexModel>(s => s.Query(
q => q.Term(t => t.FirstName, firstName)
&& q.Term(t => t.LastName, lastName)));
我想做的事情:
var query = new DemoIndexQuery();
query = query.ByFirstName(firstName);
query = query.ByLastName(lastName);
result = client.Search<DemoIndexModel>(s => s.Query(query.Compile()));
目前代码:
public abstract class ElasticQueryObject<T> where T : class
{
private Func<QueryDescriptor<T>, QueryContainer> _query;
// tried using Expression, still completely lost
private Expression<Func<QueryDescriptor<T>, QueryContainer>> _expression;
public Func<QueryDescriptor<T>, QueryContainer> Compile()
{
return _query;
}
public Func<QueryDescriptor<T>, QueryContainer> And(Func<QueryDescriptor<T>, QueryContainer> query)
{
if (_query == null)
{
_query = query;
}
else
{
// how do I chain the query??? I only can figure out how to set it.
}
return null;
}
}
public class DemoIndexQuery : ElasticQueryObject<DemoIndexModel>
{
public DemoIndexQuery ByFirstName(string firstName)
{
And(p => p.Term(term => term.FirstName, firstName));
return this;
}
public DemoIndexQuery ByLastName(string lastName)
{
And(p => p.Term(term => term.LastName, lastName));
return this;
}
}
您违反了 LINQ 合同。查询应该是不可变的——所有在查询上操作的方法都应该 return 一个新查询而不是修改旧查询。由于查询的构建方式,这本质上是可组合的,所以不用
public DemoIndexQuery ByFirstName(string firstName)
{
And(p => p.Term(term => term.FirstName, firstName));
return this;
}
你可以只用这个:
public DemoIndexQuery ByFirstName(string firstName)
{
return Where(p => p.Term(term => term.FirstName, firstName));
}
如果由于某种原因无法做到这一点,您需要自己构建表达式树,然后再传递它。最简单的方法是这样的:
Expression<...> oldQuery = ...;
var newCondition = (Expression<...>)(p => p.Term(...));
return Expression.And(oldQuery, newCondition);
如果您的查询提供程序不支持此功能,您需要做更多的工作 - 您可以单独构建整个 where 谓词,然后确保修复 lambda 和 lambda 参数。
我想为我使用 ElasticSearch 的项目创建一个可重用的查询对象。我已经在使用 LongLe 的类似 QueryObject from the Generic Unit of Work/Repositories 来使用 Entity Framework.
对数据库进行查询我似乎无法完全理解如何执行此操作 - 我不确定如何 "chain" 将 lambda 表达式的各个部分组合在一起。我试过使用 Expression/Func 但这对我来说是一个我不完全理解的新领域。我感觉好像我只是在黑暗中刺伤。
因为我什至不知道如何准确表达我的问题,下面是我目前正在做的事情、我正在尝试做的事情以及我目前的进展的一个例子:
我目前要做的事情:
ISearchResponse<DemoIndexModel> result = client.Search<DemoIndexModel>(s => s.Query(
q => q.Term(t => t.FirstName, firstName)
&& q.Term(t => t.LastName, lastName)));
我想做的事情:
var query = new DemoIndexQuery();
query = query.ByFirstName(firstName);
query = query.ByLastName(lastName);
result = client.Search<DemoIndexModel>(s => s.Query(query.Compile()));
目前代码:
public abstract class ElasticQueryObject<T> where T : class
{
private Func<QueryDescriptor<T>, QueryContainer> _query;
// tried using Expression, still completely lost
private Expression<Func<QueryDescriptor<T>, QueryContainer>> _expression;
public Func<QueryDescriptor<T>, QueryContainer> Compile()
{
return _query;
}
public Func<QueryDescriptor<T>, QueryContainer> And(Func<QueryDescriptor<T>, QueryContainer> query)
{
if (_query == null)
{
_query = query;
}
else
{
// how do I chain the query??? I only can figure out how to set it.
}
return null;
}
}
public class DemoIndexQuery : ElasticQueryObject<DemoIndexModel>
{
public DemoIndexQuery ByFirstName(string firstName)
{
And(p => p.Term(term => term.FirstName, firstName));
return this;
}
public DemoIndexQuery ByLastName(string lastName)
{
And(p => p.Term(term => term.LastName, lastName));
return this;
}
}
您违反了 LINQ 合同。查询应该是不可变的——所有在查询上操作的方法都应该 return 一个新查询而不是修改旧查询。由于查询的构建方式,这本质上是可组合的,所以不用
public DemoIndexQuery ByFirstName(string firstName)
{
And(p => p.Term(term => term.FirstName, firstName));
return this;
}
你可以只用这个:
public DemoIndexQuery ByFirstName(string firstName)
{
return Where(p => p.Term(term => term.FirstName, firstName));
}
如果由于某种原因无法做到这一点,您需要自己构建表达式树,然后再传递它。最简单的方法是这样的:
Expression<...> oldQuery = ...;
var newCondition = (Expression<...>)(p => p.Term(...));
return Expression.And(oldQuery, newCondition);
如果您的查询提供程序不支持此功能,您需要做更多的工作 - 您可以单独构建整个 where 谓词,然后确保修复 lambda 和 lambda 参数。