Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to configure AutoMapper with generic types

I am trying to implement propertywise version tracking in my entities. Using the schema of TrackedProperty for my trackable properties;

public class PropertyVersion<TValue, TVersion>
{
    public TVersion Version { get; set; }
    public TValue Value { get; set; }
}


public class TrackedProperty<TValue, TVersion> : List<PropertyVersion<TValue, TVersion>>
{

}

For example, In my repository, I will keep the TrackedFoo object and I will be able to retrieve the data at a specific version (in this case the version is described as a Time object) in Foo from.

public class TrackedFoo
{
    public string Id { get; set; }
    public TrackedProperty<string, DateTime> Name { get; set; }
}

public class Foo
{
    public string Id { get; set; }
    public string Name { get; set; }
}

I would like to keep things generic as much as possible. So I tried to use AutoMapper but I wasn't able to configure it in a generic way which I won't need to configure mapping of every single TrackedTypeX to TypeX.

I need to get the last PropertyVersion item in TrackedProperty and map its Value property to a TValue type

Can you please help me find a solution for this problem?

I can reduce the number of variable types (is that what you call the stuff between <> ?, anyway) to single using

DateTrackedProperty<TValue> : TrackedProperty<TValue,DateTime>
IntegerTrackedProperty<TValue> : TrackedProperty<TValue, int>
StringTrackedProperty<TValue> : TrackedProperty<TValue, string>

I am ok with having to write AutoMapper configuration for these three types.

like image 342
Hasan Avatar asked Feb 28 '17 19:02

Hasan


People also ask

When should you not use AutoMapper?

If you have to do complex mapping behavior, it might be better to avoid using AutoMapper for that scenario. Reverse mapping can get very complicated very quickly, and unless it's very simple, you can have business logic showing up in mapping configuration.

Does AutoMapper map both ways?

Yes, or you can call CreateMap<ModelClass, ViewModelClass>(). ReverseMap() .


1 Answers

At the end I managed to create a generic mapping profile (only one way) by placing the following in my MappingProfile

CreateMap(typeof(PropertyVersion<,>), typeof(object)).ConvertUsing(typeof(PropertyVersionToValueConverter<,>));
CreateMap(typeof(TrackedProperty<,>), typeof(PropertyVersion<,>)).ConvertUsing(typeof(TrackedPropertyToPropertyVersionConverter<,>));
CreateMap(typeof(TrackedProperty<,>), typeof(object)).ConvertUsing(typeof(TrackedPropertyToValueConverter<,>));

where

public class PropertyVersionToValueConverter<TValue, TVersion> : ITypeConverter<PropertyVersion<TValue, TVersion>, TValue>
{
    public TValue Convert(PropertyVersion<TValue, TVersion> source, TValue destination, ResolutionContext context)
    {
        if (source != null)
            return source.Value;
        return default(TValue);
    }
}

public class TrackedPropertyToPropertyVersionConverter<TValue, TVersion> : ITypeConverter<TrackedProperty<TValue, TVersion>, PropertyVersion<TValue, TVersion>>
{
    public PropertyVersion<TValue, TVersion> Convert(TrackedProperty<TValue, TVersion> source, PropertyVersion<TValue, TVersion> destination, ResolutionContext context)
    {
        if (source != null && source.Count > 0)
            return source.Last();

        else return default(PropertyVersion<TValue, TVersion>);
    }
}

public class TrackedPropertyToValueConverter<TValue, TVersion> : ITypeConverter<TrackedProperty<TValue, TVersion>, TValue>
{
    public TValue Convert(TrackedProperty<TValue, TVersion> source, TValue destination, ResolutionContext context)
    {
        var vers = context.Mapper.Map(source, typeof(TrackedProperty<TValue, TVersion>), typeof(PropertyVersion<TValue,TVersion>));
        return (TValue)context.Mapper.Map(vers, typeof(PropertyVersion<TValue, TVersion>), typeof(TValue));
    }
}

The first mapping line extracts PropertyVersion.Value.

The second mapping line assumes I need only the last version in TrackedProperty and extracts that one.

The third line brings everything together.

I could probably combine everything into one and have one CreateMap line and one Converter but that's trivial.

like image 195
Hasan Avatar answered Oct 16 '22 18:10

Hasan