Convert List of bytes to List of objects using automapper - c#

I have a source object which contains a list of bytes property('Roles')
Source object:
public class SourceObjectModel
{
public int Id { get; set; }
public List<byte> Roles { get; set; }
}
And the destination object contains a list of objects property ('Roles')
Destination object:
public class DestinationObjectModel
{
public int Id { get; set; }
public List<MyObject> Roles { get; set; }
}
MyObject object:
public class MyObject
{
public byte Id { get; set; }
}
I would like to map source object to destination object.
My automapper configuration:
o.CreateMap<SourceObjectModel, DestinationObjectModel>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.Roles, opt => opt.ResolveUsing(src => new MyResolver()))
I created a custom converter class to convery list of bytes to list of objects.
MyResolver class:
public class MyResolver: ITypeConverter<List<byte>, List<MyObject>>
{
public List<MyObject> Convert(List<byte> source, List<MyObject> destination, ResolutionContext context)
{
return new List<MyObject>();
}
}
My app is crashing after running AssertConfigurationIsValid(), but I do not get a specific detailed error. Can anyone tell me what is wrong with my custom resolver class?

Finally I have managed to fix the error. A added a new mapper inside my mapper configuration:
o.CreateMap<List<byte>, List<MyObject>>()
.ConvertUsing<MyResolver>();
And I removed the property mapping from the base source object to destination object mapping configuration:
o.CreateMap<SourceObjectModel, DestinationObjectModel>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id));

Related

AutoMapper mapping between enum and its integer values fails with ReverseMap

The application is built with DDD approach, with a separate set of persistence models. I called database object, or dbo:
public class ParentDbo
{
public int ParentId { get; set; }
public int TypeId { get; set; }
}
public class ChildDbo
{
public int ChildId { get; set; }
public ParentDbo Parent { get; set; }
public int RetryNumber { get; set; }
}
We have a simple model to look at: a parent and a child relationship. The RetryNumber presents the enum value in the database.
On retrieving data, it uses Dapper to first query the database, and use its splitOn feature to map data into them. This part is irrelevant but I will show it anyway for completeness:
const string sql = "SELECT * FROM XXX ....";
using (var cnt = _dbConnectionFactory.CreateConnection())
{
var childDbos = await cnt.QueryAsync<ChildDbo, ParentDbo, ChildDbo>(
sql: sql,
map: (childDbo, parentDbo) =>
{
childDbo.Parent = parentDbo;
return childDbo;
},
splitOn: "ParentId"
);
}
Dapper has limitation that it couldn't map data to private complex objects. That's mainly the reason why I have to have 2 sets of models. I would like to encapsulate the data and logic within domain models, with private setters and other techniques.
Here are my domain models:
public class Parent
{
public int Id { get; private set; }
public int TypeId { get; private set; }
public Parent(int parentId, int typeId)
{
// Validations
this.Id = parentId;
this.TypeId = typeId;
}
}
public class Child
{
public int Id { get; private set; }
public Parent Parent { get; private set; }
public Attempt Attempt { get; private set; }
public Child(int childId, Parent parent, Attempt attempt)
{
// Validations
this.Id = childId;
this.Parent = parent;
this.Attempt = attempt;
}
}
For domain models, I don't want public setters, and parameter-less constructors.
The Attempt is the enum with integer backing values:
public enum Attempt
{
Original = 1,
FirstRetry = 2,
SecondRetry = 3,
LastRetry = 4
}
Lastly, I want to use AutoMapper to map between Dbos and the domain models. Here is the mapping:
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Child, ChildDbo>()
.ForMember(dest => dest.ChildId, opts => opts.MapFrom(src => src.Id))
.ForMember(dest => dest.RetryNumber, opts => opts.MapFrom(src => (int)src.Attempt))
.ReverseMap();
CreateMap<Parent, ParentDbo>()
.ForMember(dest => dest.ParentId, opts => opts.MapFrom(src => src.Id))
.ReverseMap();
}
}
I want to have two-ways mappings so I use ReverseMap().
.Net Fiddle demo: https://dotnetfiddle.net/saEHWd
It maps domain models to dbos without problem:
But its reverse, mapping from dbos to domain models, is throwing exceptions:
Unhandled exception. System.ArgumentException: Program+Child needs to have a constructor with 0 args or only optional args. (Parameter 'type')
at lambda_method18(Closure , Object , Child , ResolutionContext )
at AutoMapper.Mapper.MapCore[TSource,TDestination](TSource source, TDestination destination, ResolutionContext context, Type sourceType, Type destinationType, IMemberMap memberMap)
at AutoMapper.Mapper.Map[TSource,TDestination](TSource source, TDestination destination)
at AutoMapper.Mapper.Map[TDestination](Object source)
at Program.Main()
I've tried to remove the enum property and everything worked so I'm pretty sure it's the enum mapping that's having issues.
As far as I can see in your fiddle you are trying to map from ChildDbo to Parent and there is no mapping setup for it. Change the mapping code to:
var child2 = mapper.Map<Child>(childDbo);
And since there is mismatch in third Child's ctor param and source property names change map to:
CreateMap<Child, ChildDbo>()
.ForMember(dest => dest.ChildId, opts => opts.MapFrom(src => src.Id))
.ForMember(dest => dest.RetryNumber, opts => opts.MapFrom(src => (int)src.Attempt))
.ReverseMap()
.ConstructUsing((dbo, ctx) => new Child(dbo.ChildId, ctx.Mapper.Map<Parent>(dbo.Parent), (Attempt)dbo.RetryNumber));
See here
Or rename third Child's ctor parameter to retryNumber:
public Child(int childId, Parent parent, Attempt retryNumber)
see here.
or use ForCtorParam:
CreateMap<Child, ChildDbo>()
.ForMember(dest => dest.ChildId, opts => opts.MapFrom(src => src.Id))
.ForMember(dest => dest.RetryNumber, opts => opts.MapFrom(src => (int)src.Attempt))
.ReverseMap()
.ForCtorParam("attempt", opt => opt.MapFrom(dbo => dbo.RetryNumber))
Here.

Automapper, map by naming convention

I'm using automapper to map my entities. But entities have different structure.
Source:
public class SourceEntity
{
public string Name { get; set; }
public Type Type { get; set; }
public Communication SelectedCommunication { get; set; }
}
public enum Type
{
Type1=1,
Typ2
}
[Flags]
public enum Communication
{
Phone =1,
Email =2,
Post =4
}
Also I have HasFlag() extension method that will return true if flag is selected.
Destination entity:
public class DestinationEntity
{
public string Name { get; set; }
public bool Type1_PhoneSelected { get; set; }
public bool Type1_EmailSelected { get; set; }
public bool Type1_PostSelected { get; set; }
public bool Type2_PhoneSelected { get; set; }
public bool Type2_EmailSelected { get; set; }
public bool Type2_PostSelected { get; set; }
}
My map:
CreateMap<SourceEntity, DestinationEntity>()
.ForMember(v => v.Name, opt => opt.MapFrom(i => i.Name));
But I can't figure out the best way to map Types properties.
Is it possible to map it without typing something like:
.ForMemeber(v=>v.Test1_PhoneSelected, opt=>opt.MapFrom(i=>i.SelectedCommunication.HasFlag(Communication.Phone)))
.ForMemeber(v=>v.Test2_PhoneSelected, opt=>opt.MapFrom(i=>i.SelectedCommunication.HasFlag(Communication.Phone)))
For each of this properties.
Is there any way to map by naming convention?
Or any other ways?
You can use custom value resolvers
Although AutoMapper covers quite a few destination member mapping
scenarios, there are the 1 to 5% of destination values that need a
little help in resolving. Many times, this custom value resolution
logic is domain logic that can go straight on our domain. However, if
this logic pertains only to the mapping operation, it would clutter
our source types with unnecessary behavior. In these cases,
AutoMapper allows for configuring custom value resolvers for
destination members.
Example of custom value resolver:
public class YourCustomResolver
: IMemberValueResolver<object, object, Communication, bool>
{
private Communication _communication;
public YourCustomResolver(
Communication communication)
{
}
public bool Resolve(
object source,
object destination,
Communication sourceMember,
bool destMember,
ResolutionContext context)
{
return _communication == sourceMember;
}
}
Your mapping will look like this:
CreateMap<SourceEntity, DestinationEntity>()
.ForMember(dest => dest.Type1_PhoneSelected, opt => opt.ResolveUsing(new YourCustomResolver(Communication.Phone), src => src.SelectedCommunication))
.ForMember(dest => dest.Type1_EmailSelected, opt => opt.ResolveUsing(new YourCustomResolver(Communication.Email), src => src.SelectedCommunication))
.ForMember(dest => dest.Type1_PostSelected , opt => opt.ResolveUsing(new YourCustomResolver(Communication.Post) , src => src.SelectedCommunication))
.ForMember(dest => dest.Type2_PhoneSelected, opt => opt.ResolveUsing(new YourCustomResolver(Communication.Phone), src => src.SelectedCommunication))
.ForMember(dest => dest.Type2_EmailSelected, opt => opt.ResolveUsing(new YourCustomResolver(Communication.Email), src => src.SelectedCommunication))
.ForMember(dest => dest.Type2_PostSelected , opt => opt.ResolveUsing(new YourCustomResolver(Communication.Post) , src => src.SelectedCommunication));

Default mapping configuration for inherited classes in AutoMapper

Is it possible to create a default destination mapping in AutoMapper ?
Source classes:
class SourceA {
public string X { get; set; }
}
class SourceB {
public string Y { get; set; }
}
Destination classes:
class DestBase {
public List<string> Z { get; set; }
}
class DestA : DestBase {
public string X { get; set; }
}
class DestB : DestBase {
public string Y { get; set; }
}
And the mapping configuration contains the following:
cfg.CreateMap<SourceA, DestA>()
.ForMember(dest => dest.Z, src => src.MapFrom(s => null));
cfg.CreateMap<SourceB, DestB>()
.ForMember(dest => dest.Z, src => src.MapFrom(s => null));
Is it possible to create a default mapping for all destination classes inheriting the DestBase to avoid the repeated .ForMember(...) lines ?
eg. something like:
cfg.CreateMap<object, DestBase>
.ForMember(dest => dest.Z, src => src.MapFrom(s => new List<string>()));
In principle yes, with the Include method, but there is a caveat.
If you define a map from source type object, this map would match all types. Maybe you can introduce an interface ISource for the source types that should be affected by this mapping.
So it could look like this:
class SourceA : ISource {
public string X { get; set; }
}
class SourceB : ISource {
public string Y { get; set; }
}
cfg.CreateMap<ISource, DestBase>
.Include<SourceA, DestA>
.Include<SourceB, DestB>
.Include<SourceC, DestC>
.ForMember(dest => dest.Z, , o => o.MapFrom(src => new List<string>()));
cfg.CreateMap<SourceA, DestA>()
.ForMember(dest => dest.X, o => o.MapFrom(src => src.X));
cfg.CreateMap<SourceB, DestB>()
.ForMember(dest => dest.Y, o => o.MapFrom(src => src.Y));
// still need to create a map even if no additional properties are to be mapped
cfg.CreateMap<SourceC, DestC>();
Note that you still need to create maps for all included types, even if there are no additional properties to map.

How can I make Automapper set a reference to the source object in the destination object?

In the application I am busy writing, all my mapping destination objects derive from a base class like this:
public class CatalogObject<TObject>
{
TObject InnerObject { get; set; }
}
public class CatalogTable : CatalogObject<table>
{
public string Name { get; set; }
public int ObjectId { get; set; }
}
Now, after mapping a table object to a CatalogTable object, I want the InnerObject property of that destination to be a reference to the source table object.
You could do it with a Custom Resolver:
Mapper.CreateMap<Table, CatalogTable>()
.ForMember(dest => dest.InnerObject,
opt => opt.ResolveUsing<InnerObjectResolver>());
Where the resolver would look something like:
public class InnerObjectResolver : ValueResolver<Table, Table>
{
protected override Table ResolveCore(Table source)
{
return source;
}
}
Full details can be found in the custom resolver documentation.
You might also be able to do it directly, but I haven't tried that. Something like this maybe:
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.InnerObject, opt => opt.MapFrom(src => src));

How to map collection to collection container with Automapper?

I'm having some trouble trying to map these two classes (Control -> ControlVM)
public class Control
{
public IEnumerable<FieldType> Fields { get; set; }
public class FieldType
{
//Some properties
}
}
public class ControlVM
{
public FieldList Fields { get; set; }
public class FieldList
{
public IEnumerable<FieldType> Items { get; set; }
}
public class FieldType
{
//Properties I'd like to map from the original
}
}
I tried with opt.ResolveUsing(src => new { Items = src.Fields }) but apparently AutoMapper cannot resolve the anonymous type. Also tried extending ValueResolver, but didn't work either.
NOTE: This VM is later used in a WebApi, and JSON.NET needs a wrapper around the collection to properly deserialize it. So removing the wrapper is not a solution.
NOTE2: I'm also doing Mapper.CreateMap<Control.FieldType, ControlVM.FieldType>(), so the problem isn't there.
This works for me:
Mapper.CreateMap<Control.FieldType, ControlVM.FieldType>();
// Map between IEnumerable<Control.FieldType> and ControlVM.FieldList:
Mapper.CreateMap<IEnumerable<Control.FieldType>, ControlVM.FieldList>()
.ForMember(dest => dest.Items, opt => opt.MapFrom(src => src));
Mapper.CreateMap<Control, ControlVM>();
Update: Here's how to map the other way:
Mapper.CreateMap<ControlVM.FieldType, Control.FieldType>();
Mapper.CreateMap<ControlVM, Control>()
.ForMember(dest => dest.Fields, opt => opt.MapFrom(src => src.Fields.Items));

Categories