如何处理匿名类型 的转换委托,如何将T的委托用于IEnumerable ]的Where ()方法中的使用[[

问题描述 投票:6回答:2

让我在开头说,我是菜鸟,我不知道我在做什么。因此,如果有更好的方法可以做到,那么我将不胜感激。

[当前,我正在从事一个项目,我需要将其数据源强制转换为List<T>,其中T是匿名类型,并使用lambda表达式对其进行过滤,或者即时创建lambda表达式。 ,将它们保存到数据库。我已经为System.Linq.Dynamic.Core创建了一个名为RunTimeType的静态包装器类,该类具有允许我从某些数据源创建匿名类型,然后创建该匿名类型的List<>的方法。创建anontypeList<anontype>后,我正在使用existing fluent interface创建Expression<Func<T, bool>>。建立Expression并进行编译后,我或者想要执行它,或者想要将其转换为字符串并将其保存到数据库,xml文件等,以备后用。

案例1:

当编译然后立即执行表达式时,直到这一行为止,我都很高兴:

var testList = anonList.Where(castedExp).ToList();

我收到以下错误:

错误CS1973 C#没有名为'Where'的适用方法,但似乎具有该名称的扩展方法。扩展方法不能动态调度。考虑强制转换动态参数或在不使用扩展方法语法的情况下调用扩展方法。

这很有意义,因为filter被声明为dynamic,我被迫这样做,否则编译器会抱怨以下内容:

错误CS1061'对象'不包含'By'的定义,并且没有可访问的扩展方法'By'可以找到接受'object'类型的第一个参数(您是否缺少using指令或程序集引用?)

Case2:

关于构建表达式,将其转换为字符串,然后编译为有效的Func<T,TResult>的情况,直到这一行为止,我都很好:

var castedExp = (Func<dynamic, bool>)compileExp;

我收到以下错误:

错误System.InvalidCastException'System.Func2[<>f__AnonymousType02 [System.String,System.String],System.Boolean]'键入'System.Func`2 [System.Object,System.Boolean]'。]] >

但是,我知道如果我不明确转换为Func<dynamic, bool>,编译器将抱怨以下内容:

错误CS1503 Argument 2: cannot convert from 'System.Delegate' to 'System.Func<dynamic, bool>'

所以,我的问题是,如何在仍保持使用匿名类型的能力的情况下解决这两种情况?再次说明一下,我被迫创建一个匿名类型,因为我不知道我将在运行时获取哪些数据集,因为这些数据集是完全动态的。

我想重申,只要符合项目的约束条件,我愿意以不同的方式进行此操作。坦白地说,我已经为此工作了一段时间,我没有想法,需要一些指导。

下面是所有相关代码。

测试代码:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using ExpressionBuilder.Generics;
using ExpressionBuilder.Common;
using System.Linq;
using System.Linq.Dynamic;
using System.Linq.Dynamic.Core;
using ExpressionBuilterTest.TestImplementations;

namespace ExpressionBuilterTest
{
    class Program
    {


        static void Main(string[] args)
        {   

            //test Data source
            object[,] arrayTest = new object[3, 2];

            arrayTest[0, 0] = "Field1";
            arrayTest[1, 0] = "X1";
            arrayTest[2, 0] = "Y1";

            arrayTest[0, 1] = "Field2";
            arrayTest[1, 1] = "X2";
            arrayTest[2, 1] = "Y2";

            var anonType = RunTimeType.Create(arrayTest);

            var anonList = RunTimeType.CreateGenericList(anonType, arrayTest); 

            //Creation of List<anonymous> type
            var anonList = CreateGenericList(anonType, arrayTest);

            //Creation of List<anonymous> type
            Type genericFilter = typeof(Filter<>);

            Type constructedClass = genericFilter.MakeGenericType(anonType);


            //*************************Case 1*************************
            /*
            use dynamic otherwise compiler complains about accessing
            methods on the instance of the filter object
            */ 
            dynamic filter = Activator.CreateInstance(constructedClass);

            filter.By("Field1", Operation.Contains, " X1 ")
                  .Or.By("Field2", Operation.Contains, " X2 ");

            //returns Expression<Func<T, bool>>
            var lamda = filter.GetExpression();

            //Error CS1973 
            IEnumerable<dynamic> testList = anonList.Where(castedExp).ToList();

            Console.WriteLine(testList.Count().ToString());
            Console.WriteLine("\n");



            //*************************Case 2*************************
            //convert to string
            string expString = lamda.Body.ToString().Replace("AndAlso", "&&").Replace("OrElse", "||");

            // simulation of compiling an  expression from a string which would be returned from a database
            var param = Expression.Parameter(anonType, ExpressionParameterName.Parent);

            var exp = System.Linq.Dynamic.DynamicExpression.ParseLambda(new ParameterExpression[] { param }, typeof(bool), expString);

            var compiledExp = exp.Compile();

            //*******************************************************
            //Error CS1973
            'System.Func`2[<>f__AnonymousType0`2[System.String,System.String],System.Boolean]' to type 'System.Func`2[System.Object,System.Boolean]'.'
            var castedExp = (Func<dynamic, bool>)compileExp;
            //*******************************************************

            var testList2 = anonList.Where(castedExp).ToList(); 

            Console.WriteLine(testList2.Count().ToString());
            Console.ReadKey();

        }

    }   

}

RunTimeType类:

(为简洁起见,我省略了CreateCreateGenericList方法的重载)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Runtime.CompilerServices;

namespace ExpressionBuilterTest.TestImplementations
{
    public static class  RunTimeType
    {

        /// <summary>
        /// Creates an anonymous type from a 2d array that includes headers
        /// </summary>
        public static Type Create<T>(T[,] fieldNameAndValues)
        {
            IList<System.Linq.Dynamic.Core.DynamicProperty> properties = new List<System.Linq.Dynamic.Core.DynamicProperty>();

            int columnCount = fieldNameAndValues.GetLength(1); 

            for (int jj = 0; jj < columnCount; jj++)
                properties.Add(new System.Linq.Dynamic.Core.DynamicProperty(fieldNameAndValues[0, jj].ToString(), fieldNameAndValues[1, jj].GetType()));

            return DynamicClassFactory.CreateType(properties);

        }

        /// <summary>
        /// Creates an IEnumerable<dynamic>, where dynamic is an anonymous type, from a 2d array
        /// </summary>
        /// <param name="type">Anonymous type</param>
        /// <param name="data">2 dimensional array of data</param>
        public static IEnumerable<dynamic> CreateGenericList<T>(Type anonType, T[,] data)
        {
            ThrowIfNotAnonymousType(anonType); 

            dynamic dynoObject = Activator.CreateInstance(anonType);

            var fieldNames = dynoObject.GetDynamicMemberNames();

            Type genericListType = typeof(List<>);

            Type constructedClass = genericListType.MakeGenericType(anonType);

            dynamic list = (IEnumerable<dynamic>)Activator.CreateInstance(constructedClass);

            int rowCount = data.GetLength(0);
            int jj;

            for (int ii = 1; ii < rowCount; ii++)   //skip first row
            {

                jj = 0;

                foreach (var field in fieldNames)
                    anonType.GetProperty(field).SetValue(dynoObject, data[ii, jj], null);
                jj++;

                list.Add(dynoObject);

            }

            return list;

        }

        private static void ThrowIfNotAnonymousType(Type type)
        {
            if (!IsAnonymousType(type))
                throw new Exception("'anonType' must be an anonymous type");

        }

        //https://stackoverflow.com/questions/1650681/determining-whether-a-type-is-an-anonymous-type
        private static Boolean IsAnonymousType(Type type)
        {
            Boolean hasCompilerGeneratedAttribute = type.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Count() > 0;
            Boolean nameContainsAnonymousType = type.FullName.Contains("AnonymousType");
            Boolean isAnonymousType = hasCompilerGeneratedAttribute && nameContainsAnonymousType;

            return isAnonymousType;
        }


    } 

} 

更新:

我结合了@CSharpie的答案,并对其进行了调整以适合我的实现。一切都能编译,但是,我没有得到正确的输出(请参见代码体中的注释)。

    static void Main(string[] args)
    {

        object[,] arrayTest = new object[3, 2];

        arrayTest[0, 0] = "Field1";
        arrayTest[1, 0] = "X1";
        arrayTest[2, 0] = "Y1";

        arrayTest[0, 1] = "Field2";
        arrayTest[1, 1] = "X2";
        arrayTest[2, 1] = "Y2";

        var anonType = RunTimeType.Create(arrayTest);

        var anonList = RunTimeType.CreateGenericList(anonType, arrayTest);


        Type targetType = anonType;

        Type genericFilter = typeof(Filter<>);

        Type constructedClass = genericFilter.MakeGenericType(targetType);

        dynamic filter = Activator.CreateInstance(constructedClass);

        //Dynamically build expression
        filter.By("Field1", Operation.Contains, "X")
            .Or.By("Field2", Operation.Contains, "2");

        //Returns Expression<Func<anonType, bool>>
        var lamda = filter.GetExpression();

        string expString = lamda.Body.ToString();
        expString = expString.Replace("AndAlso", "&&").Replace("OrElse", "||");

        /*
        Prints: (((x.Field1 != null) && x.Field1.Trim().ToLower().Contains("X".Trim().ToLower())) || ((x.Field2 != null) && 
                    x.Field2.Trim().ToLower().Contains("2".Trim().ToLower())))
        */
        Console.WriteLine(expString);

        ParameterExpression param = Expression.Parameter(targetType, ExpressionParameterName.Parent);

        LambdaExpression exp = System.Linq.Dynamic.DynamicExpression.ParseLambda(new ParameterExpression[] { param }, typeof(bool), expString);

        Delegate compileExp = exp.Compile(); 


        MethodInfo whereMethod = typeof(Enumerable).GetMethods().Single(m =>
        {
            if (m.Name != "Where" || !m.IsStatic)
                return false;
            ParameterInfo[] parameters = m.GetParameters();
            return parameters.Length == 2 && parameters[1].ParameterType.GetGenericArguments().Length == 2;
        });

        MethodInfo finalMethod = whereMethod.MakeGenericMethod(anonType);

        IEnumerable resultList = (IEnumerable)finalMethod.Invoke(null, new object[] { anonList, compileExp });

        /*
         Prints Nothing but should print the following:
         X1 X2
        */
        foreach (dynamic val in resultList)
        {
            Console.WriteLine(val.Field1 + "/t" + val.Field2);
        }

        Console.ReadKey();


    }

最终更新:

对于那些感兴趣的人,我终于开始工作了。我发现CreateGenericList方法仅返回匿名类型的第一个实例的列表。说CreateGenericList应该变成:

    /// <summary>
    /// Creates an IEnumerable<dynamic>, where dynamic is an anonymous type, from a 2d array
    /// </summary>
    /// <param name="type">Anonymous type</param>
    /// <param name="data">2 dimensional array of data</param>
    public static IEnumerable<dynamic> CreateGenericList<T>(Type anonType, T[,] data)
    {
        ThrowIfNotAnonymousType(anonType);

        Type genericListType = typeof(List<>);

        Type constructedClass = genericListType.MakeGenericType(anonType);

        dynamic list = (IEnumerable<dynamic>)Activator.CreateInstance(constructedClass);

        //first instance
        dynamic dynoObject = Activator.CreateInstance(anonType);

        var fieldNames = dynoObject.GetDynamicMemberNames(); 

        int rowCount = data.GetLength(0);
        int jj;

        for (int ii = 1; ii < rowCount; ii++)   //skip first row
        {


            jj = 0;

            foreach (var field in fieldNames)
            {
                dynoObject.SetDynamicPropertyValue(field,data[ii, jj]);
                jj++;

            }
            list.Add(dynoObject);

            //create a new instance for each iteration of the loop
            dynoObject = Activator.CreateInstance(anonType);

        }

        return list;

    }

然后Main变为:

    static void Main(string[] args)
    {

        object[,] arrayTest = new object[3, 2];

        arrayTest[0, 0] = "Field1";
        arrayTest[1, 0] = "X1";
        arrayTest[2, 0] = "blah";

        arrayTest[0, 1] = "Field2";
        arrayTest[1, 1] = "Y1";
        arrayTest[2, 1] = "Y2";

        var anonType = RunTimeType.Create(arrayTest);

        var anonList = RunTimeType.CreateGenericList(anonType, arrayTest);

        Type genericFilter = typeof(Filter<>);

        Type constructedClass = genericFilter.MakeGenericType(anonType);

        dynamic filter = Activator.CreateInstance(constructedClass);


        //Dynamically build expression
        filter.By("Field1", Operation.Contains, "blah")
            .Or.By("Field2", Operation.Contains, "2");


        //Returns Expression<Func<anonType, bool>>
        var lamda = filter.GetExpression();

        //Prints: System.Func`2[<>f__AnonymousType0`2[System.String,System.String],System.Boolean]
        Console.WriteLine(lamda.Compile().ToString());
        Console.WriteLine("\n");

        string expBodyString = lamda.Body.ToString();

        /*
        Prints: (((x.Field1 != null) AndAlso x.Field1.Trim().ToLower().Contains("blah".Trim().ToLower())) 
                    OrElse ((x.Field2 != null) AndAlso x.Field2.Trim().ToLower().Contains("2".Trim().ToLower())))
        */ 
        Console.WriteLine(expBodyString);
        Console.WriteLine("\n");

        expBodyString = expBodyString.Replace("AndAlso", "&&").Replace("OrElse", "||");

        /*
        Prints: (((x.Field1 != null) && x.Field1.Trim().ToLower().Contains("blah".Trim().ToLower())) || ((x.Field2 != null) 
                    && x.Field2.Trim().ToLower().Contains("2".Trim().ToLower())))
        */
        Console.WriteLine(expBodyString);
        Console.WriteLine("\n");


        ParameterExpression param = Expression.Parameter(anonType, ExpressionParameterName.Parent);


        LambdaExpression exp = System.Linq.Dynamic.Core.DynamicExpressionParser.ParseLambda(new ParameterExpression[] { param }, typeof(bool), expBodyString);


        /*
        Prints: (((x.Field1 != null) AndAlso x.Field1.Trim().ToLower().Contains("blah".Trim().ToLower())) 
                    OrElse ((x.Field2 != null) AndAlso x.Field2.Trim().ToLower().Contains("2".Trim().ToLower())))
        */
        Console.WriteLine(exp.Body.ToString());
        Console.WriteLine("\n");


        Delegate compileExp = exp.Compile();

        //Prints: System.Func`2[<>f__AnonymousType0`2[System.String,System.String],System.Boolean]
        Console.WriteLine(compileExp.ToString());
        Console.WriteLine("\n");

        MethodInfo whereMethod = typeof(Enumerable).GetMethods().Single(m =>
        {
            if (m.Name != "Where" || !m.IsStatic)
                return false;
            ParameterInfo[] parameters = m.GetParameters();
            return parameters.Length == 2 && parameters[1].ParameterType.GetGenericArguments().Length == 2;
        });

        MethodInfo finalMethod = whereMethod.MakeGenericMethod(anonType);

        IEnumerable resultList = (IEnumerable)finalMethod.Invoke(null, new object[] { anonList, compileExp });


        //Prints: blah    Y2
        foreach (dynamic val in resultList)
        {
            Console.WriteLine(val.Field1 + "\t" + val.Field2);
        }

        Console.ReadKey();

}

让我在开头说,我是菜鸟,我不知道我在做什么。因此,如果有更好的方法可以做到,那么我将不胜感激。目前,我正在从事需要能够......>

c# linq lambda dsl anonymous-types
2个回答
1
投票

这里是一个简单的示例,没有任何其他调用Enumerable.Where方法的nuget程序包。我不知道您使用的是什么软件包,因此您必须要根据自己的要求进行调整。

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

public class Program
{
    static void Main(string[] args)
    {
        var test = new {Foo = "bar"};
        var test2 = new {Foo = "derp"};

        // get the annonymous type
        Type anonType = test.GetType();


        // create a list of that annonymous type
        IList genericList = (IList) Activator.CreateInstance(typeof(List<>).MakeGenericType(anonType));

        genericList.Add(test);
        genericList.Add(test2);




        // Find the correct Enumerable.Where method
        MethodInfo whereMethod = typeof(Enumerable).GetMethods().Single(m =>
        {
            if (m.Name != "Where" || !m.IsStatic)
                return false;
            ParameterInfo[] parameters = m.GetParameters();
            return parameters.Length == 2 && parameters[1].ParameterType.GetGenericArguments().Length == 2;
        });

        // construct the finalmethod using generic type
        MethodInfo finalMethod = whereMethod.MakeGenericMethod(anonType);

        // define the Type of the Filter Func<anontype,bool>
        Type filterType = typeof(Func<,>).MakeGenericType(anonType, typeof(bool));


        // Build a simple filter expression
        // this is mostly to subsitute for the missing packages you are using to create that filter func
        ParameterExpression parameter = Expression.Parameter(anonType, "item");
        MemberExpression member = Expression.Property(parameter, "Foo");
        BinaryExpression euqalExpression = Expression.Equal(member, Expression.Constant("derp"));

        LambdaExpression filterExpression = Expression.Lambda(filterType, euqalExpression, parameter);
        Delegate filter = filterExpression.Compile();

        Console.WriteLine("This is the Filter: {0}", filterExpression);





        // Finally invoke and see it in action
        IEnumerable result = (IEnumerable) finalMethod.Invoke(null, new object[] {genericList, filter});

        foreach (dynamic o in result)
        {
            Console.WriteLine(o.Foo);
        }

        Console.ReadKey();
    }
}

1
投票

这里是您似乎想做的最少的工作代码:

© www.soinside.com 2019 - 2024. All rights reserved.