Question

Je veux remplir sans utiliser la réflexion d'une manière les propriétés d'un objet similaire à la DynamicBuilder sur CodeProject . L'exemple CodeProject est conçu pour renseigner les entités utilisant un DataReader ou dataRecord. Je l'utiliser dans plusieurs lignes d'accès à bon escient. Maintenant, je veux le modifier pour utiliser un dictionnaire ou d'autres données objet agnostique afin que je puisse l'utiliser dans le code non DAL --places J'utilise actuellement la réflexion. Je ne sais presque rien OpCodes et IL. Je sais juste que cela fonctionne bien et est plus rapide que la réflexion.

J'ai essayé de modifier l'exemple CodeProject et à cause de mon ignorance avec l'IL, j'ai obtenu coincé sur deux lignes.

  • L'un d'eux traite dbnulls et je suis assez sûr que je peux perdre, mais je ne sais pas si les lignes qui précèdent et suivent ce sont liés et qui d'entre eux aussi besoin d'aller.
  • L'autre, je pense, est celui qui a tiré la valeur de la dataRecord avant et doit maintenant tirer sur le dictionnaire. Je pense que je peux remplacer le « getValueMethod » avec mon « property.Value » mais je ne suis pas sûr.

Je suis ouvert à d'autres / de meilleures façons de dépecer ce chat aussi.

Voici le code à ce jour (les lignes commentées sont celles que je suis bloqué sur):

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;

public class Populator<T>
{
    private delegate T Load(Dictionary<string, object> properties);
    private Load _handler;
    private Populator() { }
    public T Build(Dictionary<string, object> properties)
    {
        return _handler(properties);
    }
    public static Populator<T> CreateBuilder(Dictionary<string, object> properties)
    {
        //private static readonly MethodInfo getValueMethod = typeof(IDataRecord).GetMethod("get_Item", new [] { typeof(int) });
        //private static readonly MethodInfo isDBNullMethod = typeof(IDataRecord).GetMethod("IsDBNull", new [] { typeof(int) });
        Populator<T> dynamicBuilder = new Populator<T>();
        DynamicMethod method = new DynamicMethod("Create", typeof(T), new[] { typeof(Dictionary<string, object>) }, typeof(T), true);
        ILGenerator generator = method.GetILGenerator();
        LocalBuilder result = generator.DeclareLocal(typeof(T));
        generator.Emit(OpCodes.Newobj, typeof(T).GetConstructor(Type.EmptyTypes));
        generator.Emit(OpCodes.Stloc, result);
        int i = 0;
        foreach (var property in properties)
        {
            PropertyInfo propertyInfo = typeof(T).GetProperty(property.Key, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy | BindingFlags.Default);
            Label endIfLabel = generator.DefineLabel();

            if (propertyInfo != null && propertyInfo.GetSetMethod() != null)
            {
                generator.Emit(OpCodes.Ldarg_0);
                generator.Emit(OpCodes.Ldc_I4, i);
                //generator.Emit(OpCodes.Callvirt, isDBNullMethod);
                generator.Emit(OpCodes.Brtrue, endIfLabel);

                generator.Emit(OpCodes.Ldloc, result);
                generator.Emit(OpCodes.Ldarg_0);
                generator.Emit(OpCodes.Ldc_I4, i);
                //generator.Emit(OpCodes.Callvirt, getValueMethod);

                generator.Emit(OpCodes.Unbox_Any, property.Value.GetType());
                generator.Emit(OpCodes.Callvirt, propertyInfo.GetSetMethod());
                generator.MarkLabel(endIfLabel);
            }
            i++;
        }

        generator.Emit(OpCodes.Ldloc, result);
        generator.Emit(OpCodes.Ret);
        dynamicBuilder._handler = (Load)method.CreateDelegate(typeof(Load));
        return dynamicBuilder;
    }
}

EDIT:

Utilisation de la mise en œuvre de PropertyDescriptor de Marc Gravell (avec HyperDescriptor) le code est simplifié centuple. J'ai maintenant le test suivant:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using Hyper.ComponentModel;

namespace Test
{
    class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    class Program
    {
        static void Main()
        {
            HyperTypeDescriptionProvider.Add(typeof(Person));
            var properties = new Dictionary<string, object> { { "Id", 10 }, { "Name", "Fred Flintstone" } };
            Person person = new Person();
            DynamicUpdate(person, properties);
            Console.WriteLine("Id: {0}; Name: {1}", person.Id, person.Name);
            Console.ReadKey();
        }

        public static void DynamicUpdate<T>(T entity, Dictionary<string, object> properties)
        {
            foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(typeof(T)))
                if (properties.ContainsKey(propertyDescriptor.Name))
                    propertyDescriptor.SetValue(entity, properties[propertyDescriptor.Name]);
        }
    }
}

Les commentaires sur des considérations de performance pour les TypeDescriptor.GetProperties () et PropertyDescriptor.SetValue () sont les bienvenus ...

Était-ce utile?

La solution

Edit: tout cela est essentiellement ce pimpant fait - mais pimpant est beaucoup plus optimisé. Si je rédigeais cette réponse aujourd'hui, il suffit de lire. « Utiliser pimpant »


Si vous n'êtes pas énormement « up » sur IL, il existe des alternatives que vous obtenez la vitesse de l'IL et la commodité de la réflexion.

Premier exemple:

HyperDescriptor - utilise un modèle PropertyDescriptor personnalisé qui traite de l'IL pour vous, tout ce que vous avez est code comme (plus la pour permettre HyperDescriptor un liner):

public static IEnumerable<T> Read<T>(IDataReader reader) where T : class, new() 
{
    PropertyDescriptorCollection props =
        TypeDescriptor.GetProperties(typeof(T));

    PropertyDescriptor[] propArray = new PropertyDescriptor[reader.FieldCount];
    for (int i = 0; i < propArray.Length; i++)
    {
        propArray[i] = props[reader.GetName(i)];
    }
    while(reader.Read()) {
        T item = new T();
        for (int i = 0; i < propArray.Length; i++)
        {
            object value = reader.IsDBNull(i) ? null : reader[i];
            propArray[i].SetValue(item, value);
        }
        yield return item;
    }
}

Deuxième exemple:

expressions LINQ - assez longues, mais j'ai discuté (et ci-dessus, il se trouve) sur usenet - voir cette archive .

Autres conseils

Oui, vous pouvez utiliser le code comme ceci:

 for (int i = 0; i < dataRecord.FieldCount; i++)
                {

                    PropertyInfo propertyInfo = t.GetProperty(dataRecord.GetName(i));
                    LocalBuilder il_P = generator.DeclareLocal(typeof(PropertyInfo));

                    Label endIfLabel = generator.DefineLabel();

.... ...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top