如何整合Expression& Func& lt;& gt;& gt;清理我的Linq-to-Entity查询? [英] How do I integrate Expression<Func<>> to clean up my Linq-to-Entity queries?

查看:110
本文介绍了如何整合Expression& Func& lt;& gt;& gt;清理我的Linq-to-Entity查询?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用Expression<Func<>>方法清理我的DTO Linq-to-Query Selects,以免使它们增长到无法容纳的程度.关于如何将它们集成到我自己的项目中,我仍然有些困惑.

I want to use the Expression<Func<>> method to clean up my DTO Linq-to-Query Selects so as not to make them grow anymore than what they already have. I'm still a little confused as to how to integrate them into my own project.

一个例子:

public IQueryable<ExampleDTO> SelectDTO()
{
  var repository = new ExampleUDCRepository();

  return db.Example
           .Select(v => new ExampleDTO
           {
             ExampleID     = v.ExampleID,
             MasterGroupID = v.MasterGroupID,
             //...etc

             ExampleUDCs = db.ExampleUDCs
                             .Where(vudc => vudc.ExampleID == v.ExampleID)
                             .AsEnumerable()
                             .Select(vudc => new ExampleDCDTO
                             {
                               ExampleID    = vudc.ExampleID,
                               UDCHeadingID = vudc.UDCHeadingID,
                               UDCValue     = vudc.UDCValue
                             })
           });

我的其他DTO设置和返回方法中的一些甚至更大,更草率.

Some of my other DTO setup and return methods are even bigger and sloppier.

我真正想做的是这样的:

What I really want to do is something like this:

public IQueryable<ExampleDTO> SelectDTO()
{
  var repository = new ExampleUDCRepository();

  return db.Example
           .Select(v => new ExampleDTO
           {
             ExampleID     = v.ExampleID,
             MasterGroupID = v.MasterGroupID,
             //...etc

             ExampleUDCs = new ExampleUDCsRepository().SelectDTO(v);
             // SelectDTO(Example v) in that repository would call
             // any other SelectDTO it might need and so forth
           });

问题是,Linq不知道如何将类似的方法转换为SQL语句,而这正是Expression<Func<>>方法为解决此类问题而发挥作用的地方.

Problem is, Linq doesn't know how to convert methods like that into an SQL statement, which is where the Expression<Func<>> method has come into play for passing around this sort of thing.

我对Expression<Func<>>的理解是有限的,而且我还没有找到任何文档可以使我想做的事情更加清楚.

My understanding of Expression<Func<>> is limited and I haven't found any documentation out there yet that makes what I'm trying to do much clearer.

像这样将Expression<Func<>>集成到我的DTO中的最佳方法是什么?

What is the best way of integrating Expression<Func<>> into my DTO like this?

推荐答案

因此,首先我们需要一些帮助方法.我们将从这个简单的类开始,将一个表达式的所有实例替换为另一个:

So first off we'll need a few helper methods. We'll start off with this simple class to replace all instances of one expression with another:

internal class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}

接下来,我们将创建一个扩展方法来使用它:

Next we'll create an extension method to use it:

public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}

最后,我们将创建一个将两个表达式组合在一起的Combine方法.将使用一个表达式根据一个值计算中间结果,然后使用另一个表达式,该表达式同时使用第一个值和中间结果来确定最终结果.

Finally, we'll create a Combine method that will combine two expressions together. It will take one expression that computes an intermediate result from a value, and then another that uses both the first value and the intermediate result to determine the final result.

public static Expression<Func<TFirstParam, TResult>>
    Combine<TFirstParam, TIntermediate, TResult>(
    this Expression<Func<TFirstParam, TIntermediate>> first,
    Expression<Func<TFirstParam, TIntermediate, TResult>> second)
{
    var param = Expression.Parameter(typeof(TFirstParam), "param");

    var newFirst = first.Body.Replace(first.Parameters[0], param);
    var newSecond = second.Body.Replace(second.Parameters[0], param)
        .Replace(second.Parameters[1], newFirst);

    return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}

接下来,在给定示例对象的情况下,我们可以定义计算ExampleDCDTO对象的方法.这将是您上面内容的直接摘录,不同的是,除了返回IEnumerable<ExampleDCDTO>而不是返回一个将Example转换为这样的序列的表达式之外,还可以:

Next we can define the method that computes the ExampleDCDTO objects given an example object. It will be a straight extraction of what you had above, with the exception that instead of returning an IEnumerable<ExampleDCDTO> it'll need to return an expression that turns an Example into such a sequence:

public Expression<Func<Example, IEnumerable<ExampleDCDTO>>> SelectDTO()
{
    return v => db.ExampleUDCs.Where(vudc => vudc.ExampleID == v.ExampleID)
        .AsEnumerable()
        .Select(vudc => new ExampleDCDTO
        {
            ExampleID = vudc.ExampleID,
            UDCHeadingID = vudc.UDCHeadingID,
            UDCValue = vudc.UDCValue
        });
}

现在将它们整合在一起,我们可以调用此SelectDTO方法来生成用于计算中间值的表达式,并使用另一个使用它的表达式Combine来生成该中间值:

Now to bring it all together we can call this SelectDTO method to generate the expression that computes the intermediate value and Combine it with another expression that uses it:

public IQueryable<ExampleDTO> SelectDTO()
{
    ExampleUDCRepository repository = new ExampleUDCRepository();
    return db.Example
            .Select(repository.SelectDTO().Combine((v, exampleUDCs) =>
                new ExampleDTO()
                {
                    ExampleID = v.ExampleID,
                    MasterGroupID = v.MasterGroupID,
                    ExampleUDCs = exampleUDCs,
                }));
}

对于使用 LINQKit 的用户,另一种选择是使用AsExpandable而不是我所有的辅助方法.使用这种方法仍然需要创建返回Expression<Func<Example, IEnumerable<ExampleDCDTO>>>SelectDTO方法,但是您可以这样组合结果:

Another option, for those using LINQKit, is to use AsExpandable instead of all of my helper methods. Using this approach would still require creating the SelectDTO method that return an Expression<Func<Example, IEnumerable<ExampleDCDTO>>>, but you would instead combine the result like so:

public IQueryable<ExampleDTO> SelectDTO()
{
    ExampleUDCRepository repository = new ExampleUDCRepository();
    var generateUDCExpression = repository.SelectDTO();
    return db.Example
        .AsExpandable()
        .Select(v =>
            new ExampleDTO()
            {
                ExampleID = v.ExampleID,
                MasterGroupID = v.MasterGroupID,
                ExampleUDCs = generateUDCExpression.Invoke(v),
            });
}

这篇关于如何整合Expression&amp; Func&amp; lt;&amp; gt;&amp; gt;清理我的Linq-to-Entity查询?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆