[C#] Mappage d'un objet au dictionnaire et vice versa


Answers

Je recommande fortement Castle DictionaryAdapter , facilement l'un des secrets les mieux gardés de ce projet. Vous devez seulement définir une interface avec les propriétés que vous voulez, et dans une ligne de code l'adaptateur va générer une implémentation, l'instancier et synchroniser ses valeurs avec un dictionnaire que vous passez. Je l'utilise pour taper fortement mes AppSettings dans un projet web:

var appSettings =
  new DictionaryAdapterFactory().GetAdapter<IAppSettings>(ConfigurationManager.AppSettings);

Notez que je n'ai pas eu besoin de créer une classe qui implémente IAppSettings - l'adaptateur le fait à la volée. En outre, bien que dans ce cas je ne fais que lire, en théorie si je fixais des valeurs de propriété sur appSettings, l'adaptateur garderait le dictionnaire sous-jacent en phase avec ces changements. Voici un bon tutoriel:

http://blog.andreloker.de/post/2008/06/10/Castle-DictionaryAdapter.aspx

Question

Existe-t-il un moyen rapide et élégant de mapper un objet à un dictionnaire et vice versa?

Exemple:

IDictionary<string,object> a = new Dictionary<string,object>();
a["Id"]=1;
a["Name"]="Ahmad";
// .....

devient

SomeClass b = new SomeClass();
b.Id=1;
b.Name="Ahmad";
// ..........



Sur la base de la réponse de Matías Fidemraizer, voici une version qui prend en charge la liaison aux propriétés d'objet autres que les chaînes.

using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace WebOpsApi.Shared.Helpers
{
    public static class MappingExtension
    {
        public static T ToObject<T>(this IDictionary<string, object> source)
            where T : class, new()
        {
            var someObject = new T();
            var someObjectType = someObject.GetType();

            foreach (var item in source)
            {
                var key = char.ToUpper(item.Key[0]) + item.Key.Substring(1);
                var targetProperty = someObjectType.GetProperty(key);


                if (targetProperty.PropertyType == typeof (string))
                {
                    targetProperty.SetValue(someObject, item.Value);
                }
                else
                {

                    var parseMethod = targetProperty.PropertyType.GetMethod("TryParse",
                        BindingFlags.Public | BindingFlags.Static, null,
                        new[] {typeof (string), targetProperty.PropertyType.MakeByRefType()}, null);

                    if (parseMethod != null)
                    {
                        var parameters = new[] { item.Value, null };
                        var success = (bool)parseMethod.Invoke(null, parameters);
                        if (success)
                        {
                            targetProperty.SetValue(someObject, parameters[1]);
                        }

                    }
                }
            }

            return someObject;
        }

        public static IDictionary<string, object> AsDictionary(this object source, BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
        {
            return source.GetType().GetProperties(bindingAttr).ToDictionary
            (
                propInfo => propInfo.Name,
                propInfo => propInfo.GetValue(source, null)
            );
        }
    }
}



public class SimpleObjectDictionaryMapper<TObject>
{
    public static TObject GetObject(IDictionary<string, object> d)
    {
        PropertyInfo[] props = typeof(TObject).GetProperties();
        TObject res = Activator.CreateInstance<TObject>();
        for (int i = 0; i < props.Length; i++)
        {
            if (props[i].CanWrite && d.ContainsKey(props[i].Name))
            {
                props[i].SetValue(res, d[props[i].Name], null);
            }
        }
        return res;
    }

    public static IDictionary<string, object> GetDictionary(TObject o)
    {
        IDictionary<string, object> res = new Dictionary<string, object>();
        PropertyInfo[] props = typeof(TObject).GetProperties();
        for (int i = 0; i < props.Length; i++)
        {
            if (props[i].CanRead)
            {
                res.Add(props[i].Name, props[i].GetValue(o, null));
            }
        }
        return res;
    }
}



La réflexion peut vous mener d'un objet à un dictionnaire en itérant sur les propriétés.

Pour aller dans l'autre sens, vous devrez utiliser un ExpandoObject dynamique (qui, en fait, hérite déjà de IDictionary, et donc l'a fait pour vous) en C #, sauf si vous pouvez déduire le type de la collection d'entrées dans le dictionnaire en quelque sorte.

Donc, si vous êtes en .NET 4.0, utilisez un ExpandoObject, sinon vous avez beaucoup de travail à faire ...