Automapper map nested collection - automapper

I'm new to Automapper, and struggling to get a custom mapping working. I'm using 1.1 so that it works with .net 3.5.
Essentially, I want to create a list for my DTO which is built using one subclass contained within the list class of the parent model.
Below should better illustrate what I mean:
public class RedCar
{
public string Name{get;set;}
public List<RedCarPart> RedCarParts(get;set;}
}
public class RedCarPart
{
public string RedCarSerial{get;set;}
public CarPart Carpart {get;set;}
}
public class CarPart
{
public int PartID {get;set;}
public string Description {get;set;}
}
public class CarDTO
{
public string name{get;set;}
public List<CarPartDTO> carparts{get;set;}
}
public class CarPartDTO
{
public int PartID {get;set;}
public string description {get;set;}
}
I don't need the extra information provided within RedCarPart, and so I just need a list of CarParts.
I know I can just replicate RedCarPartDTO, and ignore the fields I don't need, but rather than pass that down to the client, I feel it would be better to see if I can just map a list of CarParts.
I'm thinking it must be to do with map from, but I'm having trouble with how I'm doing this:
Mapper.CreateMap<RedCar, CarDTO>()
.ForMember(s => s.CarParts, c => c.MapFrom(m => m.RedCarParts));
Mapper.CreateMap<RedCarPart, CarPartDTO>()
.ForMember(s => s.PartID, c => c.MapFrom(m => m.CarPart.PartID))
.ForMember(s => s.Description, c => c.MapFrom(m => m.CarPart.Description));

Related

C# Automapper on the nested list

I've the following dtos and I'm trying to use automapper, but couldn't quite understand on how to do the same, as this is the first time I'm using automapper any help is really appreciated
string order {get;set;}
string orderType {get;set;}
List<OrderItem> Items {get;set;}
}
public class OrderItem{
string itemID {get;set;}
Price price {get;set;}
}
public class Price {
string total {get;set;}
string regular {get;set;}
}
public class request {
string order {get;set;}
string orderType {get;set;}
List<Item> Items {get;set;}
}
public class Item {
string itemID {get;set;}
string itemPrice {get;set;}
string regularPrice {get;set;}
}
I'm trying the following
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<request,requestDTO>()
.ForMember(dest => dest.order, act=>act.MapFrom(src => src.order))
.ForMember(dest => dest.orderType, act=>act.MapFrom(src => src.orderType))
})
How do I create mapping to map
**
src.item.itemID to dest.item.itemID
src.item.itemPrice to dest.item.price.total
src.item.regularPrice to dest.item.price.regular**
for all the items in my source array
Just create another map for Item and OrderItem as follows:
cfg.CreateMap<Item, OrderItem>()
.ForPath(dest => dest.price.total, act => act.MapFrom(src => src.itemPrice))
.ForPath(dest => dest.price.regular, act =>act.MapFrom(src => src.item.regularPrice));
Also note that the mapper <request,requestDTO> does not need any explicit ForMember call, as both objects have identical property names:
cfg.CreateMap<request,requestDTO>();

How to configure map of List attribute in AutoMapper?

I'm pretty newbie to AutoMapper. I have four classes:
public class Source
{
public int Id {get;set;}
public List<SourceItem> items {get;set;}
}
public class SourceItem
{
public int Id {get;set;}
public string firstName {get;set;}
public string lastName {get;set;}
}
public class Destination
{
public int Id {get;set;}
public List<SourceItemDestination> items {get;set;}
}
public class SourceItemDestination
{
public int Id {get;set;}
public string firstName {get;set;}
}
Is it possible to create a mapping profile to map Source to Destination allowing to bring the list of SourceItemDestination when performing mapper.Map?
Done! Here are my classes:
using AutoMapper;
using AutoMapper.EquivalencyExpression;
using Infrastructure.Data.Context;
using Microsoft.Extensions.DependencyInjection;
using System;
using WebAPI.AutoMapper;
namespace WebAPI.Configurations
{
public static class AutoMapperConfig
{
public static void AddAutoMapperConfiguration(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));
services.AddAutoMapper((serviceProvider, automapper) =>
{
automapper.AddCollectionMappers();
automapper.UseEntityFrameworkCoreModel<CellLabDbContext>(serviceProvider);
}, typeof(MappingProfile).Assembly);
}
}
}
using ApplicationCore.Domain;
using AutoMapper;
namespace WebAPI.AutoMapper
{
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<SourceItem, SourceItemDestination>().ReverseMap();
CreateMap<Source, Destination>().ReverseMap();
}
}
}
Important tip: I had problems when mapping the lists because the property items of Source class were read only:
public class Source
{
public int Id {get;set;}
public List<SourceItem> items {get;}
}
By doing this the mapper does not map the list. However, it does not raise any error or warning.

Mapping int (Id) to Model instance

I often have Models and Dtos like this:
// DTO class
public class OrderDto
{
public int Id { get; set; }
public DateTime Date { get set; }
public int ProductId { get; set; }
// ...
}
// Model class
public class Order : BaseObject
{
public DateTime Date { get set; }
public Product Product { get; set; }
// ...
}
// Model class
public class Product : BaseObject
{
// ...
}
In order to map my OrderDto to the Order class I have to configure AutoMapper for this particular "association" like so:
CreateMap<OrderDto, Order>()
.ForMember(m => m.Product, d => d.ResolveUsing((d, m) =>
{
return m.Session.GetObjectByKey<Product>(dto.ProductId);
}))
This is quite cumbersome to do this for each case like this. Therefore I was looking into generalizing this behaviour by using a custom TypeConverter class:
public class IntBaseObjectTypeConverter : ITypeConverter<int, BaseObject>
{
private UnitOfWork uow;
// ...
public BaseObjectConvert(int source, BaseObject destination, ResolutionContext context)
{
return uow.Session.GetObjectByKey(typeof(destination), source);
}
}
However, this will fail of course if the destination is null. Unfortunately the ResolutionContext does not give me any clue about the specific type of the destination property.
Now my question is, if there is another way to achieve with AutoMapper what I would like to do?
Please note that I am not using Entity Framework which of course would solve this issue on the model level with foreign key and navigational properties. I use XPO from DevExpress which does not allow foreign key and navigational properties like in Entity Framework.

Flat object to nested objects in Automapper

I use Automapper all the time, and for some reason the issue is escaping me. Below is an example of the code I'm using (names have been changed.)
Problem
All objects are mapped but the Keys object.
Code
I have a flat object coming from a WCF service.
public class FlatDTO
{
public string Key1 {get;set;}
public string Key2 {get;set;}
public string Name {get;set;}
public DateTime Date {get;set;}
}
This is the structure for my Business Object:
public class BaseModel
{
public Datetime Date{get;set;}
}
public class Keys
{
public string Key1 {get;set;}
public string Key2 {get;set;}
}
public class Summary : BaseModel
{
public Keys List {get;set;}
public string Name{get;set;}
public Summary ()
{
List = new Keys();
}
}
Below is my profile for Automapper (the init of the profile is done in the global.aspx page)
internal class MapDTO : Profile
{
protected override void configure()
{
Mapper.CreateMap<FlatDTO,BaseModel>().IgnoreAllNonExisting();
Mapper.CreateMap<FlatDTO,Role>().IgnoreAllNonExisting();
Mapper.CreateMap<FlatDTO,Summary>().IgnoreAllNonExisting();
}
Help with this 'simple' issue would be great.

Automapper: CreateMap but for a property which is not initialized yet

How can I create a Map with Automapper when in the underlying destination type a property not yet has initialized?
Example:
public class UserAccount
{
public string name { get; set; }
public Dictionary<string,string> properties { get; set; }
}
public class UserAccountOtherType
{
public string name { get; set; }
public string Property1 {get;set; }
}
public static UserAccount CustomMap(UserAccountOtherType type2)
{
AutoMapper.Mapper.CreateMap<UserAccount,UserAccountOtherType>()
.ForMember(dest => dest.properties["Property1", opt => opt.MapFrom(src => (string)src.Property1));
return AutoMapper.Mapper.Map<UserAccount,UserAccountOtherType>(type2);
}
When I try to execute this code it fails because the Dictionary in UserAccount is not yet initialized. I cannot initialize the Object by myself because the UserAccount Class is a Datacontract of a WCF Serviceinterface.
I have to create a Dicationary by myself and assign it to the property.
UserAccount b = new UserAccount();
Dictionary<string,string> properties = new Dictionary<string,string>();
b.properties = properties;
How can I solve this with Automapper? Or is my approach not senseful?
Only way I can think of to do something like this is to write a class that implements AutoMapper.IValueResolver. Then opt.MapFrom... becomes opt.ResolveUsing....FromMember... Probably at that point your destination is the entire dictionary (I think if you leave out the FromMember you get the whole object into your resolver
in your implementation of IValueResolver.Resolve, try breaking into the debugger and checking out the ResolutionContext in source.Context, then once you have your dictionary built, return source.New(myDictionary)

Resources