Automapper "flattening" - automapper

I am trying to "flatten out" the contents of a source List to fields on the Dest object, as follows:
class Source
{
public IList<TypeX> TypeXs {get; set;}
}
class Dest
{
public int IdentifierXValue { get; set;}
public int IdentifierYValue { get; set;}
public int IdentifierZValue { get; set;}
}
class TypeX
{
Identifier Identifier {get; set;}
float Value {get; set;}
}
enum Identifier
{
X,
Y,
Z
}
Mapping from source to dest works fine using the below mapping:
Mapper.CreateMap<Source, Dest>.ForMember( dest => dest.IdentifierXValue, opt => opt.MapForm(src => src.TypeXs.First(f => f.Identifier == Identifier.X).Value));
How do I achieve the reverse mapping? The "Source" reference will already exist and it will have prepopulated references to TypeXs. I just need to search for a TypeX object whose Identifier matches (say X) and for that object I need to replace the value with the IdentifierXValue in the Dest object.

This worked for me
Mapper.CreateMap<Dest, Source>()
.ForMember(d => d.TypeXs, opt => opt.Ignore())
.AfterMap((s, d) =>
{
d.TypeXs.First(tx => tx.Identifier == Identifier.X).Value = s.IdentifierXValue;
d.TypeXs.First(tx => tx.Identifier == Identifier.Y).Value = s.IdentifierYValue;
d.TypeXs.First(tx => tx.Identifier == Identifier.Z).Value = s.IdentifierZValue;
}
);

I think you'll need a custom type converter. That would probably be the easiest way.

Related

AutoMapper different level

Trying to map from Customer to CustomerDto but having issues with that extra layer in the source (I have no control over the source so I cannot align the two unfortunately).
public class Customer
{
public string Name { get; set; }
public AddressSet AddressSet { get; set; }
}
public class AddressSet
{
public AddressSetResults[] AddressSetResults { get; set; }
}
public class AddressSetResults
{
public string Street { get; set; }
public string HouseNumber { get; set; }
}
public class CustomerDto
{
public string Name { get; set; }
public AddressDto AddressDto { get; set; }
}
public class AddressDto
{
public string Street { get; set; }
public string HouseNumber { get; set; }
}
The following does not work for the AddressDto, any idea what I'm missing?
CreateMap<Customer, CustomerDto>()
.ForMember(dest => dest.AddressDto , opt => opt.MapFrom(src => src.AddressSet.AddressSetResults))
Two things:
1) Missing map from AddressSetResults to AddressDto
In order to map inner address you need to create map for these inner types as well:
CreateMap<AddressSetResults, AddressDto>();
2) Map from an element of AddressSetResults array, not from the array itself
This method:
.ForMember(
dest => dest.AddressDto,
opt => opt.MapFrom(src => src.AddressSet.AddressSetResults))
tells AutoMapper to map to AddressDto from AddressSetResults which is an array of AddressSetResults. This is incorrect, as AutoMapper will not know how to map from array of elements to just one element. Unless you create a map for that too, which would not be a good solution.
Assuming that AddressSetResults will contain up to one address you can fix that adding just one more call FirstOrDefault() to the end of mapping expression:
.ForMember(
dest => dest.AddressDto,
opt => opt.MapFrom(src => src.AddressSet.AddressSetResults.FirstOrDefault()))
FirstOrDefault() needs System.Linq namespace.
Why not just First()? If source AddressSetResults array would contain no elements, then mapping would fail resulting in exception as no elements would be found to satisfy the First() method call. Making it resistant to no elements scenario with FirstOrDefault() is more secure solution.
Final, working configuration:
CreateMap<Customer, CustomerDto>()
.ForMember(
dest => dest.AddressDto,
opt => opt.MapFrom(src => src.AddressSet.AddressSetResults.FirstOrDefault()));
CreateMap<AddressSetResults, AddressDto>();

Automapper - need to map a parent property when mapping children

The basic problem is that I have properties on my source parent entities that I need to map to my destination child DTOs when the child entities are mapped to the destination child DTOs. The child entities do not have navigation properties to their parent. As you can see below, the parent has a field of the child type. You can see in the code below that ParentSource has a property called TitleImage of type ImageSource. So linkage is one directional. In my code, I use the ImageSource everywhere. There are at least 15 different entities with properties of type ImageSource. I normalized my database. The ImageSource entity does not have X and Y coordinates because only a few, maybe 5 out of 30, places actually need those extra values. In those few places, I added the X and Y on the parent as you see on the ParentSource class with the TitleImageX and TitleImageY.
I could easily do what I want with a loop, and that is what I am doing now, but I would love to use Automapper for this if I could. It would be way less code.
Here are my classes:
public class ParentSource
{
public string Id { get; set; }
public string Name { get; set; }
public ImageSource HomeImage { get; set; }
public ImageSource TitleImage { get; set; }
//These should be copied to the child object
public int TitleImageX { get; set; }
public int TitleImageY { get; set; }
}
public class ParentDest
{
public string ParentId { get; set; }
public string DisplayName { get; set; }
public ImageDest HomeImage { get; set; }
public ImageDest TitleImage { get; set; }
}
public class ImageSource
{
public string Id { get; set; }
public string Url { get; set; }
public decimal Height { get; set; }
public decimal Width { get; set; }
}
public class ImageDest
{
public string ImageId { get; set; }
public string ImageUrl { get; set; }
public decimal Height { get; set; }
public decimal Width { get; set; }
//Not all images have Coordinate
public decimal XCoordinate { get; set; }
public decimal YCoordinate { get; set; }
}
Here are what I have so far for doing the mappings. I don't know how to copy the X & Y coordinates from the parent to the child.
CreateMap<ParentSource, ParentDest>()
.ForMember(
dest => dest.ParentId,
opt => opt.MapFrom(src => src.Id))
.ForMember(
dest => dest.DisplayName,
opt => opt.MapFrom(src => src.Name));
CreateMap<ImageSource, ImageDest>()
.ForMember(
dest => dest.ImageId,
opt => opt.MapFrom(src => src.Id))
.ForMember(
dest => dest.ImageUrl,
opt => opt.MapFrom(src => src.Url));
Thank you for your time and help.
Or you can do it in config (not tried or tested) which you may prefer, but there is no check for null on TitleImage
CreateMap<ParentSource, ParentDest>()
.ForMember(
dest => dest.ParentId,
opt => opt.MapFrom(src => src.Id))
.ForMember(
dest => dest.DisplayName,
opt => opt.MapFrom(src => src.Name))
.ForMember(dest=>dest.TitleImage,
opt=>opt.MapFrom(src=>new ImageDest
{
Width = src.TitleImage.Width,
Height = src.TitleImage.Height,
ImageId = src.TitleImage.Id,
ImageUrl = src.TitleImage.Url,
XCoordinate = src.TitleImageX,
YCoordinate = src.TitleImageY
}
));
From what I gather you want the X&Y from the parent into the child. You can use a converter class for this.
Converter:
public class ParentConverter : ITypeConverter<ParentSource, ParentDest>
{
public ParentDest Convert(ParentSource source, ParentDest destination, ResolutionContext context)
{
if (destination == null)
destination = new ParentDest();
destination.DisplayName = source.Name;
destination.ParentId = source.Id;
if (source.TitleImage != null)
{
destination.TitleImage = context.Mapper.Map<ImageDest>(source.TitleImage);
destination.TitleImage.XCoordinate = source.TitleImageX;
destination.TitleImage.YCoordinate = source.TitleImageY;
}
return destination;
}
}
Mapping:
CreateMap<ParentSource, ParentDest>()
.ConvertUsing<ParentConverter>();
CreateMap<ImageSource, ImageDest>()
.ForMember(
dest => dest.ImageId,
opt => opt.MapFrom(src => src.Id))
.ForMember(
dest => dest.ImageUrl,
opt => opt.MapFrom(src => src.Url);
});

Automapper mapping element with subelement that is list to the same Destination

I have the following structure (simplified):
class Source {
string test {get; set;}
List<SubClass> items {get; set;}
}
class SubClass {
string rating {get; set;}
string otherrating {get; set;}
}
class Destination {
string test {get; set;}
sttring rating {get; set;}
string otherrating {get; set;}
}
I need to use Automapper like this:
Source -> Destination (this will affect "test" property)
Source.Items[0] -> `Desination` (this will affect "rating" && "otherrating" property)
I would like to do this using automapper, because I have more fields than in above example
Can you give me suggestions? I am able to create map just for Source (without SubClass).
Mapper.CreateMap<Source, Destination>().ReverseMap();
...
var src = GetSourceWithListsFromDB(); // returns object of class Source
...
var model = Mapper.Map<Source, Destination>(src); // this maps Source, but doesn't map Source.Items[0].rating
I have tried the following mapping:
Mapper.CreateMap<Source, Destination>().ForMember(dest => dest, opt=>opt.MapFrom(src => src.items[0]))
but this throws error.
You need to specify the mapping for both rating and otherrating fields
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.rating, opt => opt.MapFrom(s => s.items[0].rating))
.ForMember(dest => dest.otherrating, opt => opt.MapFrom(s => s.items[0].otherrating));
Mapper.CreateMap<Destination, Source>()
.ForMember(dest =>dest.items, opt => opt.MapFrom(s=> new List<SubClass> {new SubClass(){ rating = s.rating, otherrating = s.otherrating}}));

AutoMapper property with TypeName start with Tbl in Source and property start with FK in destination

i have problem with automapper for convert property with TypeName start with Tbl in Source and property start with FK in destination, but this error occured:
Missing type map configuration or unsupported mapping. Tbl_Child -> Int32 Tbl_Child -> System.Int32 Destination path: Destination.FK_Child.FK_Child Source value: Tbl_Child
code is:
public class Source
{
public Tbl_Child Child { get; set; }
public string SourceName { get; set; }
}
public class Tbl_Child
{
public int ID_Child { get; set; }
public string ChildName { get; set; }
}
public class Destination
{
public int FK_Child { get; set; }
public string ChildName { get; set; }
public string SourceName { get; set; }
}
static void Main(string[] args)
{
var src = new Source()
{
Child = new Tbl_Child()
{
ChildName = "ch",
ID_Child = 1
},
SourceName = "src"
};
AutoMapper.Mapper.CreateMap<Source, Destination>();
var dest = AutoMapper.Mapper.Map<Source, Destination>(src);
Console.ReadKey();
}
i test AutoMapper version 2.0.0.0, and 3.1.1.0
Out of the box, Automapper is able to map properties with the same name and flattening in properties. For other properties you must specify the mapping that you want.
For example mapping from Source.Child.ID_Child to Destination.FK_Child would be accomplished like this:
Mapper.CreateMap<Source, Destination>()
.ForMember(d => d.FK_Child, o => o.MapFrom(s => s.Child.ID_Child));
And you can chain all your custom mappings like so
Mapper.CreateMap<Source, Destination>()
.ForMember(d => d.FK_Child, o => o.MapFrom(s => s.Child.ID_Child))
.ForMember(d => d.ChildName, o => o.MapFrom(s => s.Child.ChildName));
This mapping should be enough in this case but I think you may need to read the wiki a bit before going on

Complex Mapping form Source having nested classes to destination Having nested Classes

In my application i have to use mapping.
Structure of my Source object is
public class SourceContract
{
public string ContractNo {get; set;}
public string ContractDescription {get; set;}
List<SourceSalary> SourceSalaries {get;set;}
}
Source salary is
public class SourceSalary
{
public string SalaryNo {get; set;}
public int SalaryFixed {get; set;}
}
Structure of Destination Object is
public class DestinationContract
{
public string DestinationContractNo {get; set;}
public string DestinationDescription {get; set;}
List<DestinationSalary> DestinationSalaries {get;set;}
}
Destination Salary is
public class DestinationSalary
{
public string DestinationSalaryNo {get; set;}
pubbic int DestinationSalaryFixed {get; set;}
}
Now I am using two mappers
AutoMapper.Mapper.CreateMap<
SourceContract, DestinationContract>()
.ForMember(dest => dest.DestinationContractNo, opt => opt.MapFrom(src => src.ContractNo))
.ForMember(dest => DestinationDescription, opt => opt.MapFrom(src => src.ContractDescription))
And
AutoMapper.Mapper.CreateMap<
SourceSalary, DestinationSalary>()
.ForMember(dest => dest.DestinationSalaryNo, opt => opt.MapFrom(src => src.SalaryNo))
.ForMember(dest => DestinationSalaryFixed, opt => opt.MapFrom(src => src.SalaryFixed))
I want to use only one Mapper
I am not able to use nested mapping because SourceContract contains List and DestinationContract also contains List
Is there any way that I can map in One Mapper?? something like nested mapping
P.S- I cannot change the names or structure of nethier destination nor Source.

Resources