How to map collection to collection container with Automapper? - c#

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));

Related

Why doesn't Automapper inherit ForPath-ignores?

We are mapping domain hierarchy to the Dto hierarchy. Some of the properties of the domain base class get flattened. We are using ReverseMap to simplify the mapping back to domain from dto.
We've mapped Contained.Id do ContainedId of Dto and ignored the path for the reverse map.
What we were expecting, that mapping back to domain object shouldn't create new instances for Contained-property. But that was not the case.
After some investigations we've found out, that it was due to not-inheriting the .ForPath(, opt => opt.Ignore()) for the ReverseMap.
We are using 9.0.0 version of Automapper.
Here is the code:
public class Contained {
public Guid Id { get; set; }
}
public class Base {
public Contained Contained { get; set;}
}
public class Derived: Base { }
public abstract class BaseDto {
public Guid? ContainedId { get; set; }
}
public class DerivedDto: BaseDto { }
[Test]
public void ForPathInheritanceWorks()
{
var configuration = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Base, BaseDto>()
.IncludeAllDerived()
.ReverseMap()
.ForPath(d => d.Contained.Id, opt => opt.Ignore());
cfg.CreateMap<Derived, DerivedDto>()
.ReverseMap();
});
var mapper = configuration.CreateMapper();
var derived = mapper.Map<Derived>(new DerivedDto());
Assert.That(derived.Contained, Is.Null); // fails, because derived.Contained is not null.
}
To workaround an issue we had to add
.ForPath(d => d.Contained.Id, opt => opt.Ignore())
after ReverseMap of the derived class.
For me it looks like a bug in Automapper.

Convert List of bytes to List of objects using automapper

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));

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));

Automapping double nested IEnumerable

I have a rather complex situation.
I have an order:
public partial class Order
{
public string orderNum { get; set;}
public ICollection<Shipment> Shipments { get; set; }
...
}
I have a view BIGVIEW:
public partial class BIGVIEWVM
{
public string orderNum { get; set;}
public OrderDetailsVM Order { get; set; }
public ShipmentsTableVM Shipments { get; set; }
}
The subview, ShipmentsTableVM is like so:
public partial class ShipmentsTableVM
{
public string somethingelse {get;set;}
public IEnumerable<Shipment_Table_Row_VM> Shipments{get;set;}
}
It's actually more complex than this, but this is the issue in its simplest form.
I have an Order with lots of Shipments. I want to make a ProjectTo call using AutoMapper from the Order to the BIGVIEW. The entire of the Order is mapped into the OrderDetailsVM with no problem, however, What I do not know how to do is pass the ICollection to my ShipmentsTableVM and THEN map it into the IEnumerable.
cfg.CreateMap<Order, BIGVIEW>()
.ForMember(d => d.order, opt => opt.MapFrom(s => s))
...?
I thought I could take care of the mapping from the ICollection<Shipments> to my ShipmentsTableVM:
cfg.CreateMap<IEnumerable<Shipment>, ShipmentsTableVM>()
.ForMember(d => d.Shipments, opt => opt.MapFrom(s => s))
;
But this doesn't work - I get an error about Linq not being able to map things that are not an IEnumerable... but I am using an IEnumerable so I don't understand. I have tried a variety of other configurations too but I'm getting nowhere fast.
I am looking for an Automapper solution for this, as I do not want to manually do the projection in my controller.
Thanks
There are few ways to achieve this, here is one of them.
cfg.CreateMap<Shipments, Shipment_Table_Row_VM>();
cfg.CreateMap<ICollection<Shipments>, ShipmentsTableVM>()
.ConstructUsing(obj => new ShipmentsTableVM())
.ForMember(d => d.Shipments, opt => opt.MapFrom(s => s));
cfg.CreateMap<Order, BIGVIEWVM>();
You need to pass proper model to subview. try :
Order.Shipments
or
Model.Shipments

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));

Categories