如何将Select方法中的表达式与Linq合并

时间:2017-01-04 19:50:30

标签: c# .net asp.net-mvc linq expression

我正在尝试在Selectors组件上使用泛型。它应该提供一个默认模式结果,该结果基于我们所谓的SelectorViewModel类型,其中包含IdDescriptionCode。今天我们有一个使用以下查询来完成它的方法:

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方法的表达式上。这可能吗?

谢谢。

2 个答案:

答案 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")

,最后

  • 代码不太可读
  • no / low支持调试
  • 长代码行(长度)

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

现在假设您的方法收到与上述相同的idSelectordescriptionSelectorcodeSelector参数,其用法为:

var result = Queryable.Where(x => .... )
                      .OrderBy(x => ... )
                      .ToSelectorViewModel(idSelector, descriptionSelector, codeSelector) 
                      .ToList();