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

标签 c# linq entity-framework linq-to-entities expression-trees

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

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

但我似乎找不到可以让我在选择的 lambda 中指定多个属性的构造函数/重载。

最佳答案

如前所述,这可以借助 Reflection Emit 和我在下面包含的辅助类来完成。下面的代码是一项正在进行的工作,所以请按它的值(value)来使用它……“它在我的盒子上工作”。 SelectDynamic 方法类应该扔在静态扩展方法类中。

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

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

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

    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<string, Type> builtTypes = new Dictionary<string, Type>();

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

    private static string GetTypeKey(Dictionary<string, Type> 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<string, Type> 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<PropertyInfo> fields)
    {
        return GetTypeKey(fields.ToDictionary(f => f.Name, f => f.PropertyType));
    }

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

关于c# - 如何创建 LINQ 表达式树以选择匿名类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/606104/

相关文章:

c# - 是否有更简单的方法使用 Microsoft.Extensions.Configuration 将数组作为命令行参数传递?

c# - Linq 连接查询 - 如何在 View MVC C# 中显示新表

c# - 为什么我没有收到任何错误,但数据库没有插入记录?

c# - 停用事件时隐藏表单

c# - 如何使用 bool 变量切换 EntityFramework Tracker

c# - 简单的Linqpad Linq join语句,选择所有字段

entity-framework - Entity Framework 4中的唯一键

c# - 升级到 EF 4.1 后出现 DbEntityValidationException

c# - EF 代码优先 - {"CREATE DATABASE permission denied in database ' master'。”}

c# - 添加之前检查对象是否已存在于列表中