2010-06-24 16 views
0

La création d'une collection de (type générée dynamiquement) à afficher dans une grille Silverlight et l'un des processus implique la création d'un type d'importation (type généré dynamiquement) puis le mappage des propriétés sur le type d'importation. la collection de (type généré dynamiquement), les deux types partagent une propriété ID qui identifie le produit (que ce soit sur la grille ou dans l'importation)Mappage de l'objet à l'objet dans les collections

-à-dire le type lié à la grille

int Id {get; set}  
string Foo {get;set;} 
string FooFoo {get;set;} 

et importer le type

int Id {get; set} 
string Foo {get;set} 

où ids correspond je veux copier foos.

Qu'est-ce qu'un moyen rapide de mapper des propriétés d'un type à un autre dans une collection?

EDIT

Heres la mise en œuvre de Typemapper finale avec grâce à Stephan, en tant que caractéristique ne carte les deux types lorsque les keymembers sont égaux, les correspondances définies par une chaîne de chaîne dictionnaire représentant les noms de membres, travaux en silverlight.

public class TypeMapper 
{ 
    private readonly DynamicMethod _mapper; 


    public static DynamicMethod BuildMapper(Type fromType, 
              Type toType, 
              KeyValuePair<string, string> keyMemberMap, 
              Dictionary<string, string> memberMappings) 
    { 

     var method = new DynamicMethod("Map", typeof(bool), new[] { fromType, toType }); 

     // Preparing Reflection instances 
     MethodInfo getFromKeyMethod = fromType.GetMethod(
      string.Format("get_{0}", keyMemberMap.Key), 
      BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 

     MethodInfo getToKeyMethod = toType.GetMethod(
      string.Format("get_{0}", keyMemberMap.Value), 
      BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 

     ILGenerator gen = method.GetILGenerator(); 

     // Preparing locals 
     gen.DeclareLocal(typeof(Boolean)); 
     // Preparing labels 
     Label labelNoMatch = gen.DefineLabel(); 
     // Writing body 
     gen.Emit(OpCodes.Ldarg_0); 
     gen.Emit(OpCodes.Callvirt, getFromKeyMethod); 
     gen.Emit(OpCodes.Ldarg_1); 
     gen.Emit(OpCodes.Callvirt, getToKeyMethod); 
     gen.Emit(OpCodes.Ceq); 
     gen.Emit(OpCodes.Stloc_0); 
     gen.Emit(OpCodes.Ldloc_0); 
     gen.Emit(OpCodes.Brfalse_S, labelNoMatch); 
     gen.Emit(OpCodes.Ldarg_1); 
     gen.Emit(OpCodes.Ldarg_0); 


     foreach (var mapping in memberMappings) 
     { 
      var getFromValueMethod = fromType.GetMethod(
       string.Format("get_{0}", mapping.Key), 
       BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 

      var setToValueMethod = toType.GetMethod(
       string.Format("set_{0}", mapping.Value), 
       BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 

      gen.Emit(OpCodes.Callvirt, getFromValueMethod); 
      gen.Emit(OpCodes.Callvirt, setToValueMethod); 
     } 

     gen.MarkLabel(labelNoMatch); 
     gen.Emit(OpCodes.Ldloc_0); 
     gen.Emit(OpCodes.Ret); 


     return method; 
    } 

    public void Map (object fromInstance, object toInstance) 
    { 
     _mapper.Invoke(null, new[] { fromInstance, toInstance }); 
    } 


    public TypeMapper(Type fromType, Type toType, 
     KeyValuePair<string, string> keyMemberMap, 
     Dictionary<string, string> memberMappings) 
    { 
     _mapper = BuildMapper(fromType, toType, keyMemberMap, memberMappings); 
    } 

} 

Répondre

1
bound.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList() 
    .ForEach(s => 
     { 
      var prop = import.GetType().GetProperty(s.Name,BindingFlags.Public | BindingFlags.Instance); 
      if(prop != null) 
      { 
       prop.SetValue(import,s.GetValue(bound,null),null); 
      } 
     }); 

qui tracera les propriétés d'un élément à l'autre. Si vous voulez le faire dans une collection, faites-en une méthode et faites myCollection.Select(o => MapProperties(o,mapType));.

Remarque: La méthode utilise actuellement un objet existant et y effectue des copies. Vous pouvez avoir votre méthode à l'exception d'un type, puis appeler Activator.CreateInstance(type) et définir la valeur de l'importation pour mon extrait.

Modifier

Dynamic Methods

Cet article a un bon exemple de générer un DynamicMethod pour faire la copie en profondeur des objets dynamiques. Il aura un temps d'installation beaucoup plus long que la solution de réflexion, mais chaque appel suivant sera aussi rapide que s'il était compilé.

Modifier

Actual Exemple:

DynamicMethod GetMapper(Type type1, Type type2) 
{ 
DynamicMethod method = new DynamicMethod("junk", type2, 
new Type[] { type1 }); 

ILGenerator il = method.GetILGenerator(); 

LocalBuilder obj0 = il.DeclareLocal(type2); //target 

// create object and store in local 0 
ConstructorInfo ctor = type2.GetConstructor(
    new Type[] { }); 
il.Emit(OpCodes.Newobj, ctor); 
il.Emit(OpCodes.Stloc_0); 


PropertyInfo[] properties = type1.GetProperties(BindingFlags.Instance 
| BindingFlags.Public | BindingFlags.FlattenHierarchy); 
foreach (PropertyInfo prop in properties) 
{ 
// local constructed object 
il.Emit(OpCodes.Ldloc_0); 

// load source argument 
il.Emit(OpCodes.Ldarg_0); 

// get property value 
il.EmitCall(OpCodes.Callvirt, type1.GetMethod(
    "get_" + prop.Name), null); 
il.EmitCall(OpCodes.Callvirt, type2.GetMethod(
    "set_" + prop.Name), null); 
} 

il.Emit(OpCodes.Ldloc_0); 
il.Emit(OpCodes.Ret); 
return method; 
} 

Vous devriez être en mesure de le faire GetMapper(sourceType,destinationType).Invoke(null,new [] { myObject});

+0

je l'ai fait quelque chose de tout à fait similaire, il se révèle être lente (par réflexion) lorsque nous sommes compte tenu de quelques milliers. encore +1 –

+0

Si les types sont générés dynamiquement, c'est probablement ce que vous ferez de mieux. Vous faites en sorte de l'accélérer un peu en utilisant 'Reflection.Emit' pour générer un délégué d'usine et en l'exécutant, mais je ne suis pas sûr du chemin exact pour le faire. Vous pouvez jeter un oeil au MSDN pour 'Reflection.Emit' car il pourrait donner une bonne direction. – Stephan

+0

J'ai oublié que c'était une question Silverlight. Ma deuxième solution ne fonctionnera probablement pas pour Silverlight. Il serait possible de le faire comme une opération côté serveur (comme avec RIA), mais comme un côté purement client, il ne fonctionnera probablement pas. – Stephan