我正在尝试在Selectors组件上使用泛型。它应该提供一个默认模式结果,该结果基于我们所谓的SelectorViewModel
类型,其中包含Id
,Description
和Code
。今天我们有一个使用以下查询来完成它的方法:
var result = Queryable.Where(x => .... )
.OrderBy(x => ... )
.Select(x => SelectorViewModel(x.Id,
x.Name,
x.Code))
.ToList();
它工作正常,但我们会有很多这些方法。问题是,如何使Select
方法上定义的字段可以作为参数传递给SelectorViewModel
?样本:
public IEnumerable<SelectorViewModel> Selector<T>(.. idExpression, .. descriptionExpression, .. codeExpression)
{
var result = session.Query<T>
.Where(x => .... )
.OrderBy(x => ... )
.Select(x => SelectorViewModel(idExpression, // id
descriptionExpression, // description
codeExpression /*code*/))
.ToList();
return result;
}
我希望在使用这种方法时有类似的东西:
var result = repository.Selector(x => x.Id, x => x.Name, x => x.Code);
并将这些参数合并到Select
方法的表达式上。这可能吗?
谢谢。
答案 0 :(得分:4)
你需要这样的东西:
public IEnumerable<SelectorViewModel> Selector<T>(
Func<T, int> idExpression,
Func<T, string> descriptionExpression,
Func<T, string> codeExpression)
{
var result = session.Query<T>
.Where(x => .... )
.OrderBy(x => ... )
.Select(x => new SelectorViewModel(idExpression(x), descriptionExpression(x), codeExpression(x)))
.ToList();
return result;
}
但是我喜欢更像是&#34; Factory方法&#34;的东西,所以你传递了一个将T转换为SelectorViewModel的工厂(如下所示):
public class SelectorViewModel
{
public static SelectorViewModel GetSelectorViewModel<T>(T input)
{
//create SelectorViewModel how do you prefer
return new SelectorViewModel(input.Id, input.Description, input.Code);
}
public SelectorViewModel(int id, string description, int code)
{
// TODO
}
}
public IEnumerable<SelectorViewModel> Selector<T>(Func<T, SelectorViewModel> factoryMethod)
{
var result = session.Query<T>
.Where(x => .... )
.OrderBy(x => ... )
.Select(x => factoryMethod(x))
.ToList();
return result;
}
这是因为(个人而言)我不想传递许多功能,可能很复杂,例如:
x => x.property > 2 ? x.name.ToString() : (x.property < 0 ? x.description : x.description + "a")
,最后
[ps:我上面所做的事情可以通过OOP(https://en.wikipedia.org/wiki/Factory_method_pattern)]
完成)答案 1 :(得分:3)
从您的示例中不清楚您定位的是什么查询提供程序(EF或NHibernate),因此以下是一个通用帮助器方法,它使用原型表达式和基于ExpressionVisitor
的小参数替换程序组成选择器表达式:
public static class QueryableExtensions
{
public static IQueryable<SelectorViewModel> ToSelectorViewModel<T>(
this IQueryable<T> source,
Expression<Func<T, long>> idSelector,
Expression<Func<T, string>> descriptionSelector,
Expression<Func<T, string>> codeSelector
)
{
Expression<Func<long, string, string, SelectorViewModel>> prototype =
(id, description, code) => new SelectorViewModel { Id = id, Description = description, Code = code };
var parameter = Expression.Parameter(typeof(T), "e");
var body = prototype.Body
.ReplaceParameter(prototype.Parameters[0], idSelector.Body.ReplaceParameter(idSelector.Parameters[0], parameter))
.ReplaceParameter(prototype.Parameters[1], descriptionSelector.Body.ReplaceParameter(descriptionSelector.Parameters[0], parameter))
.ReplaceParameter(prototype.Parameters[2], codeSelector.Body.ReplaceParameter(codeSelector.Parameters[0], parameter));
var selector = Expression.Lambda<Func<T, SelectorViewModel>>(body, parameter);
return source.Select(selector);
}
}
使用的表达式助手:
public static partial class ExpressionUtils
{
public static Expression ReplaceParameter(this Expression expression, ParameterExpression source, Expression target)
{
return new ParameterReplacer { Source = source, Target = target }.Visit(expression);
}
class ParameterReplacer : ExpressionVisitor
{
public ParameterExpression Source;
public Expression Target;
protected override Expression VisitParameter(ParameterExpression node)
{
return node == Source ? Target : base.VisitParameter(node);
}
}
}
现在假设您的方法收到与上述相同的idSelector
,descriptionSelector
和codeSelector
参数,其用法为:
var result = Queryable.Where(x => .... )
.OrderBy(x => ... )
.ToSelectorViewModel(idSelector, descriptionSelector, codeSelector)
.ToList();