2010-08-13 12 views
6

Buduję/aktualizuję EntityFramework EntityObject w czasie wykonywania. Chcę ustawić właściwości klasy encji, nazwy właściwości i wartości pochodzą z innego źródła.Jak dynamicznie ustawić właściwość klasy bez użycia odbicia (z dynamicznym) w C# 4, gdy nazwa właściwości pochodzi z innego źródła

Więc to robię;

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

Tak, jak mogę pozbyć się "t.GetProperty (wartości [j] .EntityPropertyName) .SetValue (localeEntity, wartość, null);" część, czy istnieje dynamiczny sposób robienia tego?

Coś jak;

dynamicCastedLocaleEntity.GetProperty (wartości [j] .EntityPropertyName) = wartość;

Dzięki.

+0

jeśli nie ma naprawdę takie wyrażenie 'dynamicCastedLocaleEntity.GetProperty (wartości [j] .EntityPropertyName) = value;', w jaki sposób środowisko wykonawcze wiedzieć, które instancja, którą chcesz podać wartość? –

Odpowiedz

1

Obawiam się, że nie. Każde użycie obiektu dynamic jest zapakowane w czasie kompilacji. Każde połączenie, które może się różnić w czasie wykonywania, musi być wykonane przy użyciu odbicia.

2

ewentualnie nie z EntityObject, ale jeśli miałeś ExpandoObject niż można zrobić

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

długa odpowiedź wymyślanie. Odbicie jest świetne w wielu sytuacjach, w niektórych straszne, ale w prawie wszystkich przypadkach jest powolne.

Istnieją 4 różne sposoby ustawiania właściwości w .NET bez użycia odbicia.

Pomyślałem, że zademonstruję jedno z nich: Używanie skompilowanych drzewek wyrażeń. Zauważ, że budowanie wyrażeń jest również dość drogie, dlatego bardzo ważne jest, aby buforować delegata, który buduje z nim w słowniku (na przykład):

Wyrażenie drzew zostało wprowadzone w .NET35 i jest używane do wielu rzeczy. Używam ich do budowania wyrażenia ustawiającego właściwości, a następnie kompilowania go do delegata.

Przykład pokazuje różne terminy dla różnych przypadków, ale tutaj są moje numery: przypadek sterowania (dysk zakodowany): 0.02s Odbicie: 1.78s Expression Drzewo: 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

Są to 4 różne sposoby (to wiem), jak ustawić właściwość bez refleksji: 1. Delegate.Create 2. DynamicMethod 3. Expression Trees 4. CallSite <> Tworzenie – FuleSnabel

+0

I dodał szybsze drzewa wyrażenie. fragment kodu poniżej. – Loathing

2

open source framework ImpromptuInterface ma metody wywoływania oparte na łańcuchu przy użyciu DLR zamiast odbicia i działa szybciej niż odbicie.

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

Dla odpowiedzi FuleSnabela można znacznie przyspieszyć (czasami dwa razy szybciej w moich testach). W niektórych badaniach, było tak samo szybko jak roztwór kontrolny:

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

Gdzie podajesz wartość nieruchomości! Jestem nowy w wyrażeniach Po prostu nie mogę znaleźć części do zobaczenia wartości, tak jak w poprzednim przykładzie, że przekazuje wartość przez zmienną "val" –

+0

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

Powiązane problemy