operador instrucciones funciones expresiones delegados anonimas c# linq entity-framework linq-to-entities expression-trees

funciones - instrucciones lambda c#



Cómo crear LINQ Expression Tree para seleccionar un tipo anónimo (9)

Creo que la mayoría de las cosas ya están respondidas, como dijo Slace, necesitas alguna clase que sea devuelta por el método Select . Una vez que tenga la clase, puede usar el método System.Linq.Expressions.NewExpression para crear la expresión.

Si realmente quieres hacer esto, también puedes generar clases en tiempo de ejecución. Es un poco más trabajo, porque no se puede hacer utilizando árboles LINQ Expression, pero es posible. Puede usar el System.Reflection.Emit nombres System.Reflection.Emit para hacer eso - Acabo de hacer una búsqueda rápida y aquí hay un artículo que explica esto:

Me gustaría generar la siguiente instrucción select dinámicamente usando árboles de expresiones:

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

He resuelto cómo generar

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

pero parece que no puedo encontrar un constructor / sobrecarga que me permita especificar varias propiedades en mi lambda de selección.


Esto compila, no sé si funciona, sin embargo ...

myEnumerable.Select((p) => { return new { Name = p.Name, Description = p.Description }; });

Suponiendo que p se transforma, y ​​la instrucción select devuelve un tipo anon, usando la declaración de función de lambda.

Editar: Tampoco sé cómo generarías esto dinámicamente. Pero al menos te muestra cómo usar la lambda de selección para devolver un tipo de anon con múltiples valores

Edit2:

También debería tener en mente que el compilador de c # genera clases estáticas del tipo anon. Entonces el tipo anon realmente tiene un tipo después del tiempo de compilación. Entonces, si genera estas consultas en tiempo de ejecución (que supongo que es), puede que tenga que construir un tipo usando los diversos métodos de reflexión (creo que puede usarlos para crear tipos sobre la marcha) cargue los tipos creados en el contexto de ejecución y Úselos en su salida generada.


Esto se puede hacer, como se mencionó, con la ayuda de Reflection Emit y una clase de ayuda que he incluido a continuación. El siguiente código es un trabajo en progreso, así que tómalo por lo que vale ... "funciona en mi caja". La clase de método SelectDynamic debe arrojarse en una clase de método de extensión estática.

Como era de esperar, no obtendrás ningún Intellisense ya que el tipo no se crea hasta el tiempo de ejecución. Funciona bien en los controles de datos de destino tardío.

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


La respuesta aceptada es muy útil, pero necesitaba algo un poco más cercano a un tipo real anónimo.

Un tipo anónimo real tiene propiedades de solo lectura, un constructor para completar todos los valores, una implementación de Equals / GetHashCode para comparar los valores de cada propiedad y una implementación ToString que incluye el nombre / valor de cada propiedad. (Consulte https://msdn.microsoft.com/en-us/library/bb397696.aspx para obtener una descripción completa de los tipos anónimos).

En función de esa definición de clases anónimas, puse una clase que genera tipos dinámicos anónimos en github en https://github.com/dotlattice/LatticeUtils/blob/master/LatticeUtils/AnonymousTypeUtils.cs . El proyecto también contiene algunas pruebas unitarias para asegurarse de que los tipos anónimos falsos se comporten como los reales.

Aquí hay un ejemplo muy básico de cómo usarlo:

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

Además, otra nota: descubrí que cuando se utiliza un tipo anónimo dinámico con Entity Framework, se debe llamar al constructor con el conjunto de parámetros "miembros". Por ejemplo:

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

Si utilizó una de las versiones de Expression.New que no incluye el parámetro "miembros", Entity Framework no lo reconocería como el constructor de un tipo anónimo. Así que supongo que eso significa que la expresión del constructor de un tipo anónimo real incluiría esa información de "miembros".


No creo que puedas lograr esto. Aunque cuando select new { c.Name, c.Population } parece que no estás creando una clase que realmente eres. Si echas un vistazo a la salida compilada en Reflector o IL sin procesar, podrás ver esto.

Tendrás una clase que se vería así:

[CompilerGenerated] private class <>c__Class { public string Name { get; set; } public int Population { get; set; } }

(Ok, lo get_Name() un toque, ya que una propiedad es realmente solo un conjunto de get_Name() y set_Name(name) )

Lo que estás tratando de hacer es crear una clase dinámica adecuada, algo que no estará disponible hasta que aparezca .NET 4.0 (y aun así no estoy seguro de si será capaz de lograr lo que quieres).

La mejor solución sería definir las diferentes clases anónimas y luego tener algún tipo de comprobación lógica para determinar cuál crear, y para crearlo puede usar el objeto System.Linq.Expressions.NewExpression .

Pero, puede ser (en teoría al menos) posible hacerlo, si se está volviendo realmente duro sobre el proveedor de LINQ subyacente. Si está escribiendo su propio proveedor LINQ, puede detectar si la expresión analizada actualmente es un Seleccionar, luego determina la clase CompilerGenerated , refleja su constructor y crea.

Definitivamente no es una tarea simple, pero sería como lo hacen LINQ to SQL, LINQ to XML, etc.


Podría usar la API de Expresión Dinámica que le permite construir dinámicamente su declaración de selección así:

Select("new(<property1>,<property2>,...)");

Para que esto funcione, necesita el archivo Dynamics.cs del LINQ y muestras de lenguaje para Visual Studio, ambos están vinculados al final de esta página . También puede ver un ejemplo de trabajo que muestra esto en acción en la misma URL.


Podría usar una clase de parámetro en lugar de trabajar con un tipo anónimo. En su ejemplo, puede crear una clase de parámetros como esta:

public struct ParamClass { public string Name { get; set; }; public int Population { get; set; }; }

... y póngalo en su selección de esta manera:

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

Lo que obtienes es algo del tipo IQueryable<ParamClass> .


Puede usar IQueryable-Extensions aquí, que es una implementación de la solución descrita por "Ethan J. Brown":

https://github.com/thiscode/DynamicSelectExtensions

La extensión crea dinámicamente un tipo anónimo.

Entonces puedes hacer esto:

var YourDynamicListOfFields = new List<string>( "field1", "field2", [...] ) var query = query.SelectPartially(YourDynamicListOfFields);


Tal vez un poco tarde, pero puede ayudar a alguien.

Puede generar una selección dinámica por llamada a DynamicSelectGenerator en select from a entity.

public static Func<T, T> DynamicSelectGenerator<T>() { // get Properties of the T var fields = typeof(T).GetProperties().Select(propertyInfo => propertyInfo.Name).ToArray(); // input parameter "o" var xParameter = Expression.Parameter(typeof(T), "o"); // new statement "new Data()" var xNew = Expression.New(typeof(T)); // create initializers var bindings = fields.Select(o => o.Trim()) .Select(o => { // property "Field1" var mi = typeof(T).GetProperty(o); // original value "o.Field1" var xOriginal = Expression.Property(xParameter, mi); // set value "Field1 = o.Field1" return Expression.Bind(mi, xOriginal); } ); // initialization "new Data { Field1 = o.Field1, Field2 = o.Field2 }" var xInit = Expression.MemberInit(xNew, bindings); // expression "o => new Data { Field1 = o.Field1, Field2 = o.Field2 }" var lambda = Expression.Lambda<Func<T, T>>(xInit, xParameter); // compile to Func<Data, Data> return lambda.Compile(); }

Y usar por este código:

var result = dbContextInstancs.EntityClass.Select(DynamicSelectGenerator<EntityClass>());