2010-08-13 11 views
6

Estoy creando/actualizando EntityFramework EntityObject en tiempo de ejecución. Quiero establecer las propiedades de la clase de entidad, los nombres de propiedad y los valores provienen de otra fuente.Cómo establecer dinámicamente una propiedad de una clase sin usar la reflexión (con dinámica) en C# 4 cuando el nombre de propiedad proviene de otra fuente

Así que estoy haciendo esto;

public static EntityCollection<T> UpdateLocaleEntity<T>(EntityCollection<T> entityCollectionToUpdate, params ILocaleControl[] values) where T : EntityObject 
    { 
     foreach (var x in entityCollectionToUpdate) 
     { 
      Type t = typeof(T); 
      dynamic localeEntity = x; 

      string cultureCode = localeEntity.CultureCode; 

      for (int j = 0; j < values.Length; j++) 
      { 
       var value = values[j].GetLocaleValue(cultureCode); 
       t.GetProperty(values[j].EntityPropertyName).SetValue(localeEntity, value, null); 
      } 
     } 

     return entityCollectionToUpdate; 
    } 

Así que, ¿cómo puedo deshacerme de "t.GetProperty (valores [j] .EntityPropertyName) .SetValue (localeEntity, valor, null);" parte, ¿hay una forma dinámica de hacer esto?

Algo así como;

dynamicCastedLocaleEntity.GetProperty (values ​​[j] .EntityPropertyName) = value;

Gracias.

+0

si realmente hay una expresión 'tales dynamicCastedLocaleEntity.GetProperty (valores [j] .EntityPropertyName) = valor;', ¿Cómo sabe el tiempo de ejecución que instancia que desea dar el valor? –

Respuesta

1

Me temo que no. Cualquier uso de un objeto dynamic se cuece al horno en tiempo de compilación. Cualquier llamada que pueda variar en el tiempo de ejecución tiene que hacerse usando la reflexión.

2

posiblemente no con EntityObject, pero si usted ha tenido una ExpandoObject de lo que puede hacer

dynamic entity = new ExpandoObject(); 
(entity as IDictionary<String, Object>)[values[j].EntityPropertyName] = value 
13

Respuesta larga que viene. La reflexión es excelente en muchas situaciones, horrible en algunos, pero en casi todos los casos es lenta.

Hay al menos 4 maneras diferentes de establecer una propiedad en .NET sin tener que usar la reflexión.

Creo que demuestro una de ellas: Usar árboles de expresiones compiladas. Tenga en cuenta que la creación de expresiones también es bastante costosa, por eso es muy importante almacenar en caché el delegado que se compila con él en un diccionario (por ejemplo):

Expression Trees se introdujo en .NET35 y se usa para muchas cosas. Aquí los uso para construir una expresión de establecimiento de propiedades y luego compilarla en un delegado.

El ejemplo demuestra un calendario diferente para los diferentes casos, pero aquí están mis números: caso de control (no modificable): 0.02s Reflexión: 1.78s árbol Expresión: 0.06s

using System; 
using System.Linq.Expressions; 

namespace DifferentPropertSetterStrategies 
{ 
    class TestClass 
    { 
     public string XY 
     { 
     get; 
     set; 
     } 
    } 

    class DelegateFactory 
    { 
     public static Action<object, object> GenerateSetPropertyActionForControl(
     ) 
     { 
     return (inst, val) => ((TestClass) inst).XY = (string) val; 
     } 

     public static Action<object, object> GenerateSetPropertyActionWithReflection(
     Type type, 
     string property 
     ) 
     { 
     var propertyInfo = type.GetProperty(property); 

     return (inst, val) => propertyInfo.SetValue (inst, val, null); 
     } 

     public static Action<object,object> GenerateSetPropertyActionWithLinqExpression (
     Type type, 
     string property 
     ) 
     { 
     var propertyInfo = type.GetProperty(property); 
     var propertyType = propertyInfo.PropertyType; 

     var instanceParameter = Expression.Parameter(typeof(object), "instance"); 
     var valueParameter = Expression.Parameter(typeof(object), "value"); 

     var lambda = Expression.Lambda<Action<object, object>> (
      Expression.Assign (
       Expression.Property (Expression.Convert (instanceParameter, type), propertyInfo), 
       Expression.Convert(valueParameter, propertyType)), 
      instanceParameter, 
      valueParameter 
      ); 

     return lambda.Compile(); 
     } 
    } 

    static class Program 
    { 
     static void Time (
     string tag, 
     object instance, 
     object value, 
     Action<object, object > action 
     ) 
     { 
     // Cold run 
     action(instance, value); 

     var then = DateTime.Now; 
     const int Count = 2000000; 
     for (var iter = 0; iter < Count; ++iter) 
     { 
      action (instance, value); 
     } 
     var diff = DateTime.Now - then; 
     Console.WriteLine ("{0} {1} times - {2:0.00}s", tag, Count, diff.TotalSeconds); 

     } 

     static void Main(string[] args) 
     { 
     var instance = new TestClass(); 
     var instanceType = instance.GetType(); 

     const string TestProperty = "XY"; 
     const string TestValue = "Test"; 

     // Control case which just uses a hard coded delegate 
     Time(
      "Control", 
      instance, 
      TestValue, 
      DelegateFactory.GenerateSetPropertyActionForControl() 
      ); 

     Time(
      "Reflection", 
      instance, 
      TestValue, 
      DelegateFactory.GenerateSetPropertyActionWithReflection (instanceType, TestProperty) 
      ); 

     Time(
      "Expression Trees", 
      instance, 
      TestValue, 
      DelegateFactory.GenerateSetPropertyActionWithLinqExpression(instanceType, TestProperty) 
      ); 

     Console.ReadKey(); 
     } 

    } 
} 
+0

Estas son las 4 formas diferentes (que yo sepa) cómo establecer una propiedad sin reflexión: 1. Delegate.create 2. DynamicMethod 3. Expresión árboles 4. CallSite <> Crea – FuleSnabel

+0

he añadido un árbol de expresión más rápido. fragmento de código abajo. – Loathing

2

El código abierto framework ImpromptuInterface tiene métodos para invocar basados ​​en una cadena que utiliza el DLR en lugar de la reflexión y se ejecuta más rápido que la reflexión también.

Impromptu.InvokeSet(localeEntity, values[j].EntityPropertyName,value); 
1

Para la respuesta de FuleSnabel, puede acelerar mucho (a veces el doble de rápido en mis pruebas). En algunas pruebas, fue tan rápido como la solución de control:

public static Action<Object,Object> GenerateSetPropertyActionWithLinqExpression2(Type type, String property) { 
    PropertyInfo pi = type.GetProperty(property,BindingFlags.Instance|BindingFlags.Public); 
    MethodInfo mi = pi.GetSetMethod(); 
    Type propertyType = pi.PropertyType; 

    var instance = Expression.Parameter(typeof(Object), "instance"); 
    var value = Expression.Parameter(typeof(Object), "value"); 

    var instance2 = Expression.Convert(instance, type); 
    var value2 = Expression.Convert(value, pi.PropertyType); 
    var callExpr = Expression.Call(instance2, mi, value2); 

    return Expression.Lambda<Action<Object,Object>>(callExpr, instance, value).Compile(); 
} 
+0

¡Dónde pasa el valor de la propiedad! Soy nuevo en las expresiones. Simplemente no puedo encontrar la parte para ver el valor, ya que en el ejemplo anterior está pasando el valor a través de la variable "val" –

+0

'Acción callSetName = GenerateSetPropertyActionWithLinqExpression2 (typeof (Person)," FirstName ");' then 'Persona p = new Person(); callSetName (p, "Bob"); ' – Loathing

Cuestiones relacionadas