Combine several similar SELECT-expressions into a single expression

后端 未结 3 1425
予麋鹿
予麋鹿 2020-12-09 12:23

How to combine several similar SELECT-expressions into a single expression?

   private static Expression> CombineSelector         


        
3条回答
  •  醉话见心
    2020-12-09 12:44

    In case anyone else stumbles upon this with a similar use case as mine (my selects targeted different classes based on the level of detail needed):

    Simplified scenario:

    public class BlogSummaryViewModel
    {
        public string Name { get; set; }
    
        public static Expression> Map()
        {
            return (i => new BlogSummaryViewModel
            {
                Name = i.Name
            });
        }
    }
    
    public class BlogViewModel : BlogSummaryViewModel
    {
        public int PostCount { get; set; }
    
        public static Expression> Map()
        {
            return (i => new BlogViewModel
            {
                Name = i.Name,
                PostCount = i.Posts.Count()
            });
        }
    }
    

    I adapted the solution provided by @Marc Gravell like so:

    public static class ExpressionMapExtensions
    {
        public static Expression> Concat(
            this Expression> mapA, Expression> mapB)
            where TTargetB : TTargetA
        {
            var param = Expression.Parameter(typeof(TSource), "i");
    
            return Expression.Lambda>(
                Expression.MemberInit(
                    ((MemberInitExpression)mapB.Body).NewExpression,
                    (new LambdaExpression[] { mapA, mapB }).SelectMany(e =>
                    {
                        var bindings = ((MemberInitExpression)e.Body).Bindings.OfType();
                        return bindings.Select(b =>
                        {
                            var paramReplacedExp = new ParameterReplaceVisitor(e.Parameters[0], param).VisitAndConvert(b.Expression, "Combine");
                            return Expression.Bind(b.Member, paramReplacedExp);
                        });
                    })),
                param);
        }
    
        private class ParameterReplaceVisitor : ExpressionVisitor
        {
            private readonly ParameterExpression original;
            private readonly ParameterExpression updated;
    
            public ParameterReplaceVisitor(ParameterExpression original, ParameterExpression updated)
            {
                this.original = original;
                this.updated = updated;
            }
    
            protected override Expression VisitParameter(ParameterExpression node) => node == original ? updated : base.VisitParameter(node);
        }
    }
    

    The Map method of the extended class then becomes:

        public static Expression> Map()
        {
            return BlogSummaryViewModel.Map().Concat(i => new BlogViewModel
            {
                PostCount = i.Posts.Count()
            });
        }
    

提交回复
热议问题