How to configure AutoMapper with common types

I am trying to implement property tracking in my entities. Using TrackedProperty for my monitored 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 save a TrackedFoo object, and I can get the data in 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 save as many things as possible. Therefore, I tried to use AutoMapper, but I was not able to configure it in a general way, which I do not need to configure the display of all TrackedTypeX on TypeX .

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

Can you help me find a solution to this problem?

I can reduce the number of variable types (this is what you call stuff between <>?, Anyway) to single use

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

I like to write an AutoMapper configuration for these three types.

+6
source share
3 answers

In the end, I managed to create a common 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 line displays PropertyVersion.Value.

The second line of the display assumes that I only need the latest version in TrackedProperty and retrieves it.

The third line combines everything.

I could probably combine everything into one and have one CreateMap line and one converter, but this is trivial.

+3
source

The whole idea of ​​automapper is to map a specific object to each other. When we worked with generics, we created a helper function:

 public static class AutoMaps { public static void Initialize() { Mapper.Initialize(cfg => { CreateGenericMapping<CatModel>(cfg); }); } public static void CreateCatMapping<TCatType>(IMapperConfigurationExpression cfg) { cfg.CreateMap<TCatType, Cat>(); cfg.CreateMap<Cat, TCatType>(); } } 
0
source

When trying to map Foo to TrackedFoo , since they have the same property names, everything will be fine at first. The problem occurs when AutoMapper tries to convert the Name property ( string ) of your Foo to the aa property Name ( TrackedProperty<string,DateTime> ) of your TrackedFoo .

Since AutoMapper does not know how to convert natevely between string and TrackedProperty<,> , it will not work.

Although, you can teach AutoMapper how to convert between these types. To do this, you will need Custom Type Converter :

 public class TrackedPropertyConverter<TValue, TVersion> : ITypeConverter<TrackedProperty<TValue, TVersion>, TValue> { public TValue Convert(TrackedProperty<TValue, TVersion> source, TValue destination, ResolutionContext context) { return source.First().Value; } } 

Then you configure it like this:

  Mapper.Initialize( cfg => { cfg.CreateMap<TrackedFoo, Foo>(); cfg.CreateMap<TrackedProperty<string, DateTime>, string>().ConvertUsing<TrackedPropertyConverter<string,DateTime>>(); cfg.CreateMap<TrackedProperty<int, DateTime>, int>().ConvertUsing<TrackedPropertyConverter<int, DateTime>>(); cfg.CreateMap<TrackedProperty<double, DateTime>, double>().ConvertUsing<TrackedPropertyConverter<double, DateTime>>(); } ); Mapper.AssertConfigurationIsValid(); 

And here is a usage example:

 var tracked = new TrackedFoo { Id = "SomeGuid", Name = new TrackedProperty<string, DateTime> { new PropertyVersion<string, DateTime> { Value = "FooBar", Version = new DateTime(2017, 2, 28) } }, Value = new TrackedProperty<int, DateTime> { new PropertyVersion<int, DateTime> { Value = 456, Version = new DateTime(2017, 2, 28) } } }; var foo = Mapper.Map<Foo>(tracked); Console.WriteLine("Id: {0} | Name: {1} | Value: {2}", foo.Id, foo.Name, foo.Value); Console.ReadLine(); 

This is a common way for AutoMapper to learn how to convert between types, regardless of which objects with these types are converted (for example, Foo to TrackedFoo / Bar to TrackedBar / Person to TrackedPerson).

You must explicitly configure each type (string, int, double), as an example on your page, but you just do it once, you do not need to do this for all possible classes using tracked properties.

In addition, your repository is responsible for accepting the version value as a parameter (e.g. DateTime ) and getting TrackedProperty with the only value you requested for this version, since you cannot have multiple values ​​for the same version.

0
source

Source: https://habr.com/ru/post/1015349/


All Articles