How do I integrate Expression> to clean up my Linq-to-Entity queries?

后端 未结 1 979
遥遥无期
遥遥无期 2021-01-23 06:18

I want to use the Expression> method to clean up my DTO Linq-to-Query Selects so as not to make them grow anymore than what they already have. I\

相关标签:
1条回答
  • 2021-01-23 06:59

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

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

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

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

    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),
                });
    }
    
    0 讨论(0)
提交回复
热议问题