Мне сложно создать функцию построения выражений, чтобы передать запрос для репозитория NHibernate.
В основном, что я хочу сделать, это построить запрос из различных свойств объекта (строк) и сравнить их значение со значениями в массиве. Также сможете добавлять другие предложения предложений.
Что-то вроде
public static Expression<Func<TElement, bool>> BuildOrExpression<TElement, TValue>
(Dictionary<Expression<Func<TElement, TValue>>, IEnumerable<TValue>> expressionSet)
{
List<Expression> newExpressions = new List<Expression>();
foreach (var item in expressionSet)
{
if (null == item.Key) throw new ArgumentNullException("valueSelector");
if (null == item.Value) throw new ArgumentNullException("values");
ParameterExpression p = item.Key.Parameters.Single();
if (!item.Value.Any()) return e => false;
IEnumerable<Expression> expressions = item.Value
.Select(value => (Expression)Expression.Equal(item.Key.Body, Expression.Constant(value, typeof(TValue))));
Expression compExpression = expressions
.Aggregate<Expression>((accumulate, equal) => Expression.OrElse(accumulate, equal));
newExpressions.Add(Expression.Lambda<Func<TElement, bool>>(compExpression, p));
}
Expression accExpression = newExpressions
.Aggregate<Expression>((accumulate, equal) => Expression.OrElse(accumulate, equal));
return Expression.Lambda<Func<TElement, bool>>(accExpression);
}
Но вместо выполнения Expression.Equal
каким-то образом String.Contains
.
Любая помощь приветствуется. Кстати, код выше не проверен.
Мне удалось преодолеть проблему с сочетанием выражений, но я не уверен, что это лучшее решение. Любой, кто использовал решение, предоставленное Владимиром и PredicateBuilder Джо Альбахари (писатель C # 4). Это то, что я придумал.
public static Expression<Func<TElement, bool>> BuildOrExpression<TElement, TValue>(Expression<Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
{
if (null == valueSelector) throw new ArgumentNullException("valueSelector");
if (null == values) throw new ArgumentNullException("values");
ParameterExpression p = valueSelector.Parameters.Single();
if (!values.Any()) return e => false;
Expression<Func<string, string, bool>> expFunc = (name, value) => name.Contains(value);
IEnumerable<Expression> equals = values.Select(value => (Expression)Expression.Call(valueSelector.Body, typeof(String).GetMethod("Contains"), Expression.Constant(value, typeof(TValue))));
Expression body = equals.Aggregate<Expression>((accumulate, equal) => Expression.OrElse(accumulate, equal));
return Expression.Lambda<Func<TElement, bool>>(body, p);
}
public static Expression<Func<T, bool>> CombineOr<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> CombineAnd<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
[TestMethod()]
public void OfferComplexSearchTest()
{
using (var lifetime = container.BeginLifetimeScope())
{
IOfferRepository offerRepository = lifetime.Resolve<IOfferRepository>();
List<Offer> offers = new List<Offer>();
List<Expression<Func<Offer, bool>>> expressions = new List<Expression<Func<Offer, bool>>>();
Expression<Func<Offer, bool>> finalExp = null;
string[] orQuery = new string[2] { "director".ToUpper(), "jefe".ToUpper() };
expressions.Add(LinqTools.BuildOrExpression<Offer, string>(c => c.Description.ToUpper(), orQuery));
expressions.Add(LinqTools.BuildOrExpression<Offer, string>(c => c.Title.ToUpper(), orQuery));
expressions.Add(LinqTools.BuildOrExpression<Offer, string>(c => c.Keywords.ToUpper(), orQuery));
finalExp = expressions.Aggregate<Expression<Func<Offer, bool>>>((accomulate, equal) => LinqTools.CombineOr<Offer>(accomulate, equal));
offers.AddRange(offerRepository.GetMany(LinqTools.CombineAnd<Offer>(finalExp, (x) => x.Publish)));
Assert.IsTrue(offers.Count > 0, "Error: No Offers found.");
}
}
Вместо Expression.Equal
вам нужно использовать Expression.Call
и передать Contains экземпляр MethodInfo. Что-то вроде этого
IEnumerable<Expression> expressions = item.Value.Select(value =>
(Expression)Expression.Call(item.Key.Body,
typeof(String).GetMethod("Contains"),
Expression.Constant(value, typeof(TValue))));