2010-08-13 12 views
6

Sto creando/aggiornando EntityFramework EntityObject su runtime. Voglio impostare le proprietà della classe di entità, i nomi di proprietà e i valori provengono da un'altra fonte.Come impostare dinamicamente una proprietà di una classe senza utilizzare reflection (con dynamic) in C# 4 quando il nome della proprietà proviene da un'altra fonte

Così sto facendo questo;

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

Così, come posso liberarmi di "t.GetProperty (valori [j] .EntityPropertyName) .SetValue (localeEntity, valore, null);" parte, c'è un modo dinamico per farlo?

Qualcosa come;

dynamicCastedLocaleEntity.GetProperty (valori [j] .EntityPropertyName) = valore;

Grazie.

+0

se c'è davvero una tale espressione 'dynamicCastedLocaleEntity.GetProperty (valori [j] .EntityPropertyName) = valore;', come fa il runtime sapere quali istanza vuoi dare il valore? –

risposta

1

Ho paura di no. Qualsiasi utilizzo di un oggetto dynamic viene elaborato al momento della compilazione. Qualsiasi chiamata che potrebbe variare in fase di esecuzione deve essere eseguita utilizzando la riflessione.

2

forse non con EntityObject, ma se hai avuto un ExpandoObject di quanto si può fare

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

Risposta lunga fino a venire. La riflessione è ottima in molte situazioni, orribile in alcuni, ma in quasi tutti i casi è lenta.

Esistono almeno 4 modi diversi per impostare una proprietà in .NET senza dover utilizzare la riflessione.

Ho pensato di dimostrarne uno: utilizzare alberi di espressione compilati. Si noti che la creazione di espressioni è piuttosto costosa, quindi è molto importante memorizzare nella cache il delegato con un build in un dizionario (ad esempio):

Expression Trees è stato introdotto in .NET35 ed è utilizzato per molte cose. Qui li uso per costruire un'espressione setter di proprietà e quindi compilarlo in un delegato.

L'esempio dimostra tempistica diversa per i diversi casi, ma qui sono i miei numeri: caso di controllo (hard coded): 0.02s Riflessione: 1.78s Espressione Albero: 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

Questi sono i 4 modi diversi (che io sappia) come impostare una proprietà senza riflessione: 1. Delegate.create 2. DynamicMethod 3. Expression Trees 4. CallSite <> Crea – FuleSnabel

+0

ho aggiunto un albero di espressione più veloce. snippet di codice in basso. – Loathing

2

L'open source framework ImpromptuInterface ha metodi per invocare in base a una stringa che utilizza la DLR anziché il reflection e viene eseguito più rapidamente del reflection.

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

Per la risposta di FuleSnabel, è possibile velocizzare molto (a volte due volte più veloce nei miei test). In alcuni test, è stato altrettanto veloce come la soluzione di controllo:

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

Dove si passa il valore per la proprietà! Sono nuovo alle espressioni Non riesco proprio a trovare la parte per vedere il valore come nell'esempio precedente che sta passando il valore attraverso la variabile "val" –

+0

'Azione callSetName = GenerateSetPropertyActionWithLinqExpression2 (typeof (Person)," FirstName ");' then 'Person p = new Person(); callSetName (p, "Bob"); ' – Loathing

Problemi correlati