Object to object mapperi kirjutamine

13.02.2010  |  Gunnar

Hästi keeruline Hiljuti treisin kirjatüki stringide pildumisest ja enne seda kirjutasin sellest, kuidas objektide omadusi kiiresti kopeerida. Nüüd peale mõningast mängimist ja katsetamist kirjutasin kokku rivi klasse, millest siin avaldan selle kõikse kiirema.

Objektide omaduste kopeerimise kanne annab hea ülevaate sellest, mis tehtud ja proovitud sai. Tegin sellesse koodi mõningad kohendused, koostasin klassid ja võrdlesin tulemust AutoMapperiga.

NB! Minu siintoodud koodi suhtu kriitiliselt. Ära käitu nagu keskmine ALT.NET-i fanboy, kes vanaisade jutust huvitavama osa ära kuulab, manitsused kõrvust mööda laseb ja seejärel endale tundmatute vahenditega hea projekti põhja laseb. Mõtle kaks sammu ette. Ja kui ennast kindlalt ei tunne, siis võta AutoMapper – see on palju küpsem ja stabiilsem ja testitum toode, kui minu siinne kritseldus.

Et mapperi implementatsioonide arvu mitte piirata, siis tekitasin abstraktse baasklassi, mis pakub välja põhilise funktsionaalsuse.

public class PropertyMap
{
    public PropertyInfo SourceProperty { get; set; }
    public PropertyInfo TargetProperty { get; set; }
}
 
public abstract class ObjectCopyBase
{
    public abstract void MapTypes<T, TU>();
    public abstract void Copy<T, TU>(T source, TU target);
 
    protected virtual IList<PropertyMap>
        GetMatchingProperties<T, TU>()
    {
        var sourceProperties = typeof(T).GetProperties();
        var targetProperties = typeof(TU).GetProperties();
 
        var properties = (from s in sourceProperties
                          from t in targetProperties
                          where s.Name == t.Name &&
                                s.CanRead &&
                                t.CanWrite &&
                                s.PropertyType == t.PropertyType
                          select new PropertyMap
                          {
                              SourceProperty = s,
                              TargetProperty = t
                          }).ToList();
        return properties;
    }
 
    protected virtual string GetMapKey<T, TU>()
    {
        var className = "Copy_";
        className += typeof(T).FullName.Replace(".", "_");
        className += "_";
        className += typeof(TU).FullName.Replace(".", "_");
 
        return className;
    }
}

Selle peale ehitasin LCG (Lightweigh Code Generation) kasutava implementatsiooni mapperist. Välja näeb see selline.

public class ObjectCopyLcg : ObjectCopyBase
{
    private delegate void CopyPublicPropertiesDelegate<T, TU>
        (T source, TU target);
 
    private readonly Dictionary<string, object> _del =
        new Dictionary<string, object>();
 
    public override void MapTypes<T, TU>()
    {
        var key = GetMapKey<T, TU>();
        if (_del.ContainsKey(key))
            return;
 
        var source = typeof(T);
        var target = typeof(TU);
 
        var args = new[] { source, target };
        var mod = typeof(Program).Module;
 
        var dm = new DynamicMethod(key, null, args, mod);
        var il = dm.GetILGenerator();
        var maps = GetMatchingProperties<T, TU>();
 
        foreach (var map in maps)
        {
            il.Emit(OpCodes.Ldarg_1);
            il.Emit(OpCodes.Ldarg_0);
            il.EmitCall(OpCodes.Callvirt,
                        map.SourceProperty.GetGetMethod(), null);
            il.EmitCall(OpCodes.Callvirt,
                        map.TargetProperty.GetSetMethod(), null);
        }
        il.Emit(OpCodes.Ret);
        var del = dm.CreateDelegate(
                  typeof(CopyPublicPropertiesDelegate<T, TU>));
        _del.Add(key, del);
    }
 
    public override void Copy<T, TU>(T source, TU target)
    {
        var key = GetMapKey<T, TU>();
        var del = (CopyPublicPropertiesDelegate<T, TU>)_del[key];
        del.Invoke(source, target);
    }
}

Koodi polegi kuigi palju ja see muudab klassi kasutamise küllaltki lihtsaks ja hästi kontrollitavaks.

Aga võrdleme tulemusi AutoMapperiga. Minul olid paar objekti, mis polnud küll keerukamate killast, kuid erinevus tuli siiski päris hea.

minu mapper vs AutoMapper

Mu daamid ja härrad – erinevus on ~6 kordne!

Okay, see võrdlus pole kuigi aus, sest AutoMapper teeb veel palju muid häid asju ära, mida minu mapper ei tee ja arvatavasti ei hakkagi tegema. Samas on see siin hea näide sellest, kuidas algne sopakood millekski ilusamaks vormida ja kuidas lihtsad asjad annavad paremaid tulemusi kui keerukad asjad.

P.S. Kui keegi soovib üleelatust ülevaadet alates nullist kuni selle tulemuseni, siis palun lahkesti märku anda. :)

Kommenteeri

sulge
Saada link e-postiga

© DT 2012 | Creative Commons Attribution-Noncommercial 3.0 License | WordPress