簡體   English   中英

將強類型表達式轉換為匿名類型返回

[英]Convert strongly typed Expression to anonymous type return

我正在嘗試動態轉換這種類型的表達式:

myDbSet.Select(x => new MyClass
            {
                IsSelected = x.ChildList.Any()
            })

進入 :

myDbSet.Select(x => new 
            {
                Item = x
                IsSelected = x.ChildList.Any()
            })

我會編寫一個擴展方法,該方法將采用強類型版本並轉換為匿名版本,然后創建一個新表達式以執行以下操作:

myDbSet.Select(anonymousTransformedExpression).ToList().
       .Select(newGeneratedExpression)

我希望這個 newGeneratedExpression 是:

myDbSet.Select(x => new 
            {
                x.Item.IsSelected = x.IsSelected
                return x.Item;
            })

所以基本上將返回轉換回強類型但應用了“IsSelected”值。

問題是我真的找不到如何做的起點..

編輯

好吧,我意識到問題不是那么清楚,到目前為止,我已經接近解決一個主要問題的解決方案。 這是我得到的:

public static IEnumerable<TModel> SelectWithUnmapped<TModel> (this IQueryable<TModel> source, Expression<Func<TModel, object>> assigner) where TModel : class, new()
    {
        var anonymousType = typeof(AnonymousType<TModel>);
        var expression = Expression.New(anonymousType);
        var parameter = Expression.Parameter(typeof(TModel), "x");


        //this part is hard coded to only take binding at position 0.. eventually will become dynamic
        var originalBinding = ((MemberAssignment) ((MemberInitExpression) assigner.Body).Bindings[0]);
        var originalExpression = originalBinding.Expression;
        Expression conversion = Expression.Convert(originalExpression, typeof(object));

        var bindings = new[]
        {
            Expression.Bind(anonymousType.GetProperty("Item"), parameter),
            //this is hardcoded test
            Expression.Bind(anonymousType.GetProperty("Property1"), conversion)
        };

        var body = Expression.MemberInit(expression, bindings);
        var lambda = Expression.Lambda<Func<TModel, AnonymousType<TModel>>>(body, parameter);




        var test = source.Select(lambda).ToList();

        return source;
    }

    class AnonymousType<TModel>
    {
        public TModel Item { get; set; }
        public object Property1 { get; set; }
        public object Property2 { get; set; }
        public object Property3 { get; set; }
        public object Property4 { get; set; }
        public object Property5 { get; set; }
        public object Property6 { get; set; }
        public object Property7 { get; set; }
        public object Property8 { get; set; }
        public object Property9 { get; set; }
        public object Property10 { get; set; }
        public object Property11 { get; set; }
        public object Property12 { get; set; }
        public object Property13 { get; set; }
        public object Property14 { get; set; }
        public object Property15 { get; set; }
        public object Property16 { get; set; }
        public object Property17 { get; set; }
        public object Property18 { get; set; }
        public object Property19 { get; set; }
        public object Property20 { get; set; }
    } 
}

當我嘗試分配測試時,出現以下錯誤:在指定的 LINQ to Entities 查詢表達式中未綁定參數“x”。

這是由於我使用原始表達式的綁定表達式進行了第二次綁定。 然而,兩者都使用相同的參數名稱“x”。 我如何確保我的新綁定確實知道 x 是來自新表達式的參數?

所以基本上到目前為止,我正在嘗試采用一個看起來像這樣的表達式:

x => new Test
{
   PropertyTest = x.Blah.Count()
}

進入 :

x => new AnonymousType<Test>(){
   Item = x,
   Property1 = x.Blah.Count()
}

我終於完成了。 我想這只是需要耐心和反復試驗。 幫助任何想做同樣事情的人。 我不得不將自己限制在特定的財產數量上……這很容易添加。

這是代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;


public static class IQueryableExtensions
{
    public static IEnumerable<TModel> SelectAndAssign<TModel>(this IQueryable<TModel> source, Expression<Func<TModel, object>> assigner) where TModel : class, new()
    {
        //get typed body of original expression
        var originalBody = (MemberInitExpression)assigner.Body;

        //list to store the new bindings we're creating for new expression
        var newExpressionBindings = new List<MemberBinding>();
        var newExpressionReturnType = typeof(AnonymousType<TModel>);

        //new param
        var parameter = Expression.Parameter(typeof(TModel), "x");

        //base binding
        newExpressionBindings.Add(Expression.Bind(newExpressionReturnType.GetProperty("Item"), parameter));

        //go through all the original expression's bindings
        for (var i = 0; i < originalBody.Bindings.Count; i++)
        {
            var originalBinding = (MemberAssignment)originalBody.Bindings[i];
            var originalExpression = originalBinding.Expression;

            var memberType = originalBinding.Expression.Type;

            //create delegate based on the member type
            var originalLambdaDelegate = typeof(Func<,>).MakeGenericType(typeof(TModel), memberType);

            //create lambda from that delegate
            var originalLambda = Expression.Lambda(originalLambdaDelegate, originalExpression, assigner.Parameters[0]);

            //create a AnonymousVar<MemberType> from the type of the member ( to please EF unable to assign bool to object directly ) 
            //start with getting the generic type
            var genericMemberType = typeof(AnonymousVar<>).MakeGenericType(memberType);
            //then create teh delegate
            var genericMemberTypeDelegate = typeof(Func<>).MakeGenericType(genericMemberType);
            //Now create an expression with a binding for that object to assign its Property ( strongly typed now from the generic declaration )
            var genericInstantiationExpression = Expression.New(genericMemberType);
            //the binding.. using the original expression expression
            var genericInstantiationBinding = Expression.Bind(genericMemberType.GetProperty("Property"), originalLambda.Body);
            // create the body
            var genericInstantiationBody = Expression.MemberInit(genericInstantiationExpression, genericInstantiationBinding);

            //now we need to recreate a lambda for this
            var newBindingExpression = Expression.Lambda(genericMemberTypeDelegate, genericInstantiationBody);

            //Create the binding and add it to the new expression bindings
            newExpressionBindings.Add(Expression.Bind(newExpressionReturnType.GetProperty("Property" + (i + 1)), newBindingExpression.Body));
        }

        //start creating the new expression
        var expression = Expression.New(newExpressionReturnType);

        //create new expression body with bindings
        var body = Expression.MemberInit(expression, newExpressionBindings);

        //The actual new expression lambda
        var newLambda = Expression.Lambda<Func<TModel, AnonymousType<TModel>>>(body, parameter);

        // replace old lambda param with new one
        var replacer = new ParameterReplacer(assigner.Parameters[0], newLambda.Parameters[0]); // replace old lambda param with new

        //new lambda with fixed params
        newLambda = Expression.Lambda<Func<TModel, AnonymousType<TModel>>>(replacer.Visit(newLambda.Body), newLambda.Parameters[0]);

        //now that we have all we need form the server, we materialize the list
        var materialized = source.Select(newLambda).ToList();

        //typed return parameter
        var typedReturnParameter = Expression.Parameter(typeof(AnonymousType<TModel>), "x");

        //Lets assign all those custom properties back into the original object type
        var expressionLines = new List<Expression>();
        for (var i = 0; i < originalBody.Bindings.Count; i++)
        {
            var originalBinding = (MemberAssignment)originalBody.Bindings[i];
            var itemPropertyExpression = Expression.Property(typedReturnParameter, "Item");
            var bindingPropertyExpression = Expression.Property(itemPropertyExpression, originalBinding.Member.Name);

            var memberType = originalBinding.Expression.Type;
            var valuePropertyExpression = Expression.Convert(Expression.Property(typedReturnParameter, "Property" + (i + 1)), typeof(AnonymousVar<>).MakeGenericType(memberType));
            var memberValuePropertyExpression = Expression.Property(valuePropertyExpression, "Property");

            var equalExpression = Expression.Assign(bindingPropertyExpression, memberValuePropertyExpression);
            expressionLines.Add(equalExpression);
        }
        var returnTarget = Expression.Label(typeof(TModel));
        expressionLines.Add(Expression.Return(returnTarget, Expression.Property(typedReturnParameter, "Item")));
        expressionLines.Add(Expression.Label(returnTarget, Expression.Constant(null, typeof(TModel))));
        var finalExpression = Expression.Block(expressionLines);

        var typedReturnLambda = Expression.Lambda<Func<AnonymousType<TModel>, TModel>>(finalExpression, typedReturnParameter).Compile();

        return materialized.Select(typedReturnLambda);
    }

    class AnonymousVar<TModel>
    {
        public TModel Property { get; set; }
    }

    class AnonymousType<TModel>
    {
        public TModel Item { get; set; }

        public object Property1 { get; set; }
        public object Property2 { get; set; }
        public object Property3 { get; set; }
        public object Property4 { get; set; }
        public object Property5 { get; set; }
        public object Property6 { get; set; }
        public object Property7 { get; set; }
        public object Property8 { get; set; }
        public object Property9 { get; set; }
        public object Property10 { get; set; }
        public object Property11 { get; set; }
        public object Property12 { get; set; }
        public object Property13 { get; set; }
        public object Property14 { get; set; }
        public object Property15 { get; set; }
        public object Property16 { get; set; }
        public object Property17 { get; set; }
        public object Property18 { get; set; }
        public object Property19 { get; set; }
        public object Property20 { get; set; }
    }


    class ParameterReplacer : ExpressionVisitor
    {
        private ParameterExpression from;
        private ParameterExpression to;

        public ParameterReplacer(ParameterExpression from, ParameterExpression to)
        {
            this.from = from;
            this.to = to;
        }
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return base.VisitParameter(node == this.from ? this.to : node);
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM