web-dev-qa-db-fra.com

Meilleures pratiques pour mapper un objet sur un autre

Ma question est, quelle est la meilleure façon de mapper un objet à un autre de la manière la plus maintenable. Je ne peux pas changer la façon dont l'objet Dto que nous obtenons est configuré pour être plus normalisé, je dois donc créer un moyen de mapper cela à notre implémentation de leur objet.

Voici un exemple de code pour montrer ce que je dois faire:

class Program
{
    static void Main(string[] args)
    {
        var dto = new Dto();

        dto.Items = new object[] { 1.00m, true, "Three" };
        dto.ItemsNames = new[] { "One", "Two", "Three" };            

        var model = GetModel(dto);

        Console.WriteLine("One: {0}", model.One);
        Console.WriteLine("Two: {0}", model.Two);
        Console.WriteLine("Three: {0}", model.Three);
        Console.ReadLine();
    }

    private static Model GetModel(Dto dto)
    {
        var result = new Model();

        result.One = Convert.ToDecimal(dto.Items[Array.IndexOf(dto.ItemsNames, "One")]);
        result.Two = Convert.ToBoolean(dto.Items[Array.IndexOf(dto.ItemsNames, "Two")]);
        result.Three = dto.Items[Array.IndexOf(dto.ItemsNames, "Three")].ToString();

        return result;
    }
}

class Dto
{
    public object[] Items { get; set; }
    public string[] ItemsNames { get; set; }
}

class Model
{
    public decimal One { get; set; }
    public bool Two { get; set; }
    public string Three { get; set; }
}

Je pense que ce serait génial si j'avais une sorte de classe de mappeur qui prendrait la propriété des objets du modèle propertyInfo, le type que je veux convertir et le "nom de l'élément" que je veux retirer. Quelqu'un a-t-il des suggestions pour rendre ce nettoyant?

Merci!

32
Alex

J'opterais pour AutoMapper , une bibliothèque de cartographie open source et gratuite qui permet de mapper un type dans un autre, sur la base de conventions (c'est-à-dire de mapper des propriétés publiques avec les mêmes noms et les mêmes types/dérivés/convertibles, ainsi que avec beaucoup d'autres intelligents ). Très facile à utiliser, vous permettra de réaliser quelque chose comme ceci:

Model model = Mapper.Map<Model>(dto);

Vous n'êtes pas sûr de vos besoins spécifiques, mais AutoMapper prend également en charge résolveurs de valeurs personnalisées , ce qui devrait vous aider à écrire une implémentation générique unique de votre mappeur particulier.

22
Efran Cobisi

Il s'agit d'une implémentation générique possible utilisant un peu de réflexion (pseudo-code, ne pas avoir VS maintenant):

public class DtoMapper<DtoType>
{
    Dictionary<string,PropertyInfo> properties;

    public DtoMapper()
    {
        // Cache property infos
        var t = typeof(DtoType);
        properties = t.GetProperties().ToDictionary(p => p.Name, p => p);
     }

    public DtoType Map(Dto dto)
    {
        var instance = Activator.CreateInstance(typeOf(DtoType));

        foreach(var p in properties)
        {
            p.SetProperty(
                instance, 
                Convert.Type(
                    p.PropertyType, 
                    dto.Items[Array.IndexOf(dto.ItemsNames, p.Name)]);

            return instance;
        }
    }

Usage:

var mapper = new DtoMapper<Model>();
var modelInstance = mapper.Map(dto);

Ce sera lent lorsque vous créez l'instance de mappeur mais beaucoup plus rapide plus tard.

6
Stefano Altieri
/// <summary>
/// map properties
/// </summary>
/// <param name="sourceObj"></param>
/// <param name="targetObj"></param>
private void MapProp(object sourceObj, object targetObj)
{
    Type T1 = sourceObj.GetType();
    Type T2 = targetObj.GetType();

    PropertyInfo[] sourceProprties = T1.GetProperties(BindingFlags.Instance | BindingFlags.Public);
    PropertyInfo[] targetProprties = T2.GetProperties(BindingFlags.Instance | BindingFlags.Public);

   foreach (var sourceProp in sourceProprties)
   {
       object osourceVal = sourceProp.GetValue(sourceObj, null);
       int entIndex = Array.IndexOf(targetProprties, sourceProp);
       if (entIndex >= 0)
       {
           var targetProp = targetProprties[entIndex];
           targetProp.SetValue(targetObj, osourceVal);
       }
   }
}
3
DKM