当前位置:  开发笔记 > 编程语言 > 正文

如何创建LINQ表达式树以选择匿名类型

如何解决《如何创建LINQ表达式树以选择匿名类型》经验,为你挑选了2个好方法。

我想使用表达式树动态生成以下select语句:

var v = from c in Countries
        where c.City == "London"
        select new {c.Name, c.Population};

我已经研究出如何生成

var v = from c in Countries
        where c.City == "London"
        select new {c.Name};

但我似乎无法找到一个构造函数/重载,让我在select lambda中指定多个属性.



1> Ethan J. Bro..:

如上所述,这可以通过Reflection Emit和我在下面包含的帮助类来完成.下面的代码是一项正在进行中的工作,因此请将其视为值得的......"它适用于我的盒子".SelectDynamic方法类应该放在静态扩展方法类中.

正如预期的那样,您不会获得任何Intellisense,因为直到运行时才创建类型.适用于后期数据控件.

public static IQueryable SelectDynamic(this IQueryable source, IEnumerable fieldNames)
{
    Dictionary sourceProperties = fieldNames.ToDictionary(name => name, name => source.ElementType.GetProperty(name));
    Type dynamicType = LinqRuntimeTypeBuilder.GetDynamicType(sourceProperties.Values);

    ParameterExpression sourceItem = Expression.Parameter(source.ElementType, "t");
    IEnumerable bindings = dynamicType.GetFields().Select(p => Expression.Bind(p, Expression.Property(sourceItem, sourceProperties[p.Name]))).OfType();

    Expression selector = Expression.Lambda(Expression.MemberInit(
        Expression.New(dynamicType.GetConstructor(Type.EmptyTypes)), bindings), sourceItem);

    return source.Provider.CreateQuery(Expression.Call(typeof(Queryable), "Select", new Type[] { source.ElementType, dynamicType },
                 Expression.Constant(source), selector));
}



public static class LinqRuntimeTypeBuilder
{
    private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    private static AssemblyName assemblyName = new AssemblyName() { Name = "DynamicLinqTypes" };
    private static ModuleBuilder moduleBuilder = null;
    private static Dictionary builtTypes = new Dictionary();

    static LinqRuntimeTypeBuilder()
    {
        moduleBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run).DefineDynamicModule(assemblyName.Name);
    }

    private static string GetTypeKey(Dictionary fields)
    {
        //TODO: optimize the type caching -- if fields are simply reordered, that doesn't mean that they're actually different types, so this needs to be smarter
        string key = string.Empty;
        foreach (var field in fields)
            key += field.Key + ";" + field.Value.Name + ";";

        return key;
    }

    public static Type GetDynamicType(Dictionary fields)
    {
        if (null == fields)
            throw new ArgumentNullException("fields");
        if (0 == fields.Count)
            throw new ArgumentOutOfRangeException("fields", "fields must have at least 1 field definition");

        try
        {
            Monitor.Enter(builtTypes);
            string className = GetTypeKey(fields);

            if (builtTypes.ContainsKey(className))
                return builtTypes[className];

            TypeBuilder typeBuilder = moduleBuilder.DefineType(className, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Serializable);

            foreach (var field in fields)                    
                typeBuilder.DefineField(field.Key, field.Value, FieldAttributes.Public);

            builtTypes[className] = typeBuilder.CreateType();

            return builtTypes[className];
        }
        catch (Exception ex)
        {
            log.Error(ex);
        }
        finally
        {
            Monitor.Exit(builtTypes);
        }

        return null;
    }


    private static string GetTypeKey(IEnumerable fields)
    {
        return GetTypeKey(fields.ToDictionary(f => f.Name, f => f.PropertyType));
    }

    public static Type GetDynamicType(IEnumerable fields)
    {
        return GetDynamicType(fields.ToDictionary(f => f.Name, f => f.PropertyType));
    }
}


我一直在使用它(好吧,类似)并且已经得到了"无法创建类型的常量值"错误.我通过在最后一行用`source.Expression`替换`Expression.Constant(source)`来解决这个问题.希望这有助于某人:)
您可以将OrderBy放在// TODO中进行优化并完成.

2> 小智..:

接受的答案非常有用,但我需要更接近真实匿名类型的东西.

真正的匿名类型具有只读属性,用于填充所有值的构造函数,用于比较每个属性的值的Equals/GetHashCode的实现,以及包含每个属性的名称/值的实现ToString.(有关匿名类型的完整说明,请参阅https://msdn.microsoft.com/en-us/library/bb397696.aspx.)

根据匿名类的定义,我在https://github.com/dotlattice/LatticeUtils/blob/master/LatticeUtils/AnonymousTypeUtils.cs上放了一个在github上生成动态匿名类型的类.该项目还包含一些单元测试,以确保假匿名类型的行为与真实类型相同.

这是一个如何使用它的一个非常基本的例子:

AnonymousTypeUtils.CreateObject(new Dictionary
{
    { "a", 1 },
    { "b", 2 }
});

另外,另一个注意事项:我发现当使用带有Entity Framework的动态匿名类型时,必须使用"members"参数集调用构造函数.例如:

Expression.New(
    constructor: anonymousType.GetConstructors().Single(), 
    arguments: propertyExpressions,
    members: anonymousType.GetProperties().Cast().ToArray()
); 

如果您使用其中一个不包含"members"参数的Expression.New版本,则Entity Framework不会将其识别为匿名类型的构造函数.所以我认为这意味着真正的匿名类型的构造函数表达式将包含"成员"信息.

推荐阅读
oDavid_仔o_880
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有