AutoMapper - How to map an object to list of objects - c#

I wanna map Person to list of Client with AutoMapper:
and this is my models:
public class Person
{
public Guid Id { get; set;}
public string Name { get; set;}
public string Country { get; set;}
public string PhoneNumber { get; set;}
}
public class Member
{
public Guid Id { get; set;}
public string FullName { get; set; }
}
public class Client
{
public Member User { get; set; }
}
I tried to do it with AutoMapper but I couldn't:
CreateMap<Person, List<Client>>();

You need 4 mapping rules:
Map Person to Member.
Map Member to Client.
Map Person to Client.
Map Person to List<Client>.
CreateMap<Person, Member>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => src.Name));
CreateMap<Member, Client>()
.ForMember(dest => dest.User, opt => opt.MapFrom(src => src));
CreateMap<Person, Client>()
.ConvertUsing((src, dest, ctx) => new Client { User = ctx.Mapper.Map<Member>(src) });
CreateMap<Person, List<Client>>()
.ConvertUsing((src, dest, ctx) => new List<Client> { ctx.Mapper.Map<Client>(src) });
Demo # .NET Fiddle

You can map them using the following code:
CreateMap<Person, Client>()
.ForMember(dest => dest.User, opt => opt.MapFrom(src => new Member { Id = src.Id, FullName = src.Name }));
CreateMap<Person, List<Client>>()
.ConvertUsing(src => src.Select(x => new Client { User = new Member { Id = x.Id, FullName = x.Name } }).ToList());

Related

How to map this using AutoMapper 11.0.0?

I have a model classes shown below.
public class ModelA
{
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string DetailId{ get; set; }
public string DetailName { get; set; }
public string DetailDescription { get; set; }
}
public class ModelB
{
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class ModelC
{
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public ModelB Detail { get; set; }
}
Now, I want to map ModelA to ModelC where
DetailId = Detail.Id
DetailName = Detail.Name
DetailDescription = Detail.Description
Is this possible using Automapper 11.0.0 ?
If possible, how ?
var config = new MapperConfiguration(cfg => {
cfg.CreateMap< ModelA, ModelC >().ForMember(u => u. Detail, o => o.MapFrom(s => s));
});
You want the modelB mapping in modelC,But the structure of modelA is different from that of modelC.
Use this code:
CreateMap<ModelA, ModelC>()
.ForMember(d => d.Detail.Id, src => src.MapFrom(e => e.DetailId))
.ForMember(d => d.Detail.Description, src => src.MapFrom(e => e.DetailDescription))
.ForMember(d => d.Detail.Name, src => src.MapFrom(e => e.DetailName))
.ReverseMap();
Thanks for the answers.
However I solved it already.
MapperConfiguration _config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ModelA, ModelB>()
.ForMember(d => d.DetailId, opt => opt.MapFrom(s => s.Id))
.ForMember(d => d.DetailName , opt => opt.MapFrom(s => s.Name))
.ForMember(d => d.DetailDescription, opt => opt.MapFrom(s => s.Description))
.ReverseMap();
cfg.CreateMap<ModelC, ModelA>()
.ForMember(d => d.DetailId, opt => opt.MapFrom(s => s.Detail.Id ))
.ForMember(d => d.DetailName , opt => opt.MapFrom(s => s.Detail.Name ))
.ForMember(d => d.DetailDescription, opt => opt.MapFrom(s => s.Detail.Description ))
cfg.CreateMap<ModelA, ModelC>()
.ForMember(d => d.Detail, opt => opt.MapFrom(s => Mapper.Map<ModelA, ModelB>(s)))
});
UPDATE
Found a better solution.
Check out the link below.
https://dotnettutorials.net/lesson/mapping-complex-type-to-primitive-type-using-automapper/

C# AutoMapper issue with mapping Many-to-Many, wrong foreign key

I have faced with the strange issue with mapping model to entity with Many-to-Many relationship
I have the following entities and models:
using System;
using System.Diagnostics;
using System.Collections.Generic;
namespace AutoMapper.ReproducedExample
{
public class StoreEntity
{
public int Id { get; set; }
public string Name { get; set; }
public List<StoreProductEntity> Products { get; set; } = new List<StoreProductEntity>();
}
public class ProductEntity
{
public int Id { get; set; }
public string Name { get; set; }
public List<StoreProductEntity> Stores { get; set; } = new List<StoreProductEntity>();
}
public class StoreProductEntity
{
public int StoreId { get; set; }
public StoreEntity Store { get; set; }
public int ProductId { get; set; }
public ProductEntity Product { get; set; }
}
public class StoreModel
{
public int Id { get; set; }
public string Name { get; set; }
public List<ProductModel> Products { get; set; } = new List<ProductModel>();
}
public class ProductModel
{
public int Id { get; set; }
public string Name { get; set; }
public List<StoreModel> Stores { get; set; } = new List<StoreModel>();
}
public class CustomProfile : Profile
{
public CustomProfile()
{
CreateMap<StoreModel, StoreEntity>()
.ForMember(d => d.Products,
opt => opt.MapFrom(s => s.Products))
.AfterMap((model, entity) =>
{
foreach (var entityProduct in entity.Products)
{
entityProduct.StoreId = entity.Id;
entityProduct.Store = entity;
}
});
CreateMap<StoreModel, StoreProductEntity>()
.ForMember(entity => entity.StoreId, opt => opt.MapFrom(model => model.Id))
.ForMember(entity => entity.Store, opt => opt.MapFrom(model => model))
.ForMember(entity => entity.ProductId, opt => opt.Ignore())
.ForMember(entity => entity.Product, opt => opt.Ignore());
CreateMap<ProductModel, ProductEntity>()
.ForMember(d => d.Stores,
opt => opt.MapFrom(s => s.Stores))
.AfterMap((model, entity) =>
{
foreach (var entityStore in entity.Stores)
{
entityStore.ProductId = entity.Id;
entityStore.Product = entity;
}
});
CreateMap<ProductModel, StoreProductEntity>()
.ForMember(entity => entity.StoreId, opt => opt.Ignore())
.ForMember(entity => entity.Store, opt => opt.Ignore())
.ForMember(entity => entity.ProductId, opt => opt.MapFrom(model => model.Id))
.ForMember(entity => entity.Product, opt => opt.MapFrom(model => model));
}
}
class Program
{
static void Main(string[] args)
{
var configuration = new MapperConfiguration(cfg =>
{
cfg.AddProfile<CustomProfile>();
});
#if DEBUG
configuration.AssertConfigurationIsValid();
#endif
var mapper = configuration.CreateMapper();
var store0 = new StoreModel()
{
Id = 1,
Name = "Store0",
};
var store1 = new StoreModel()
{
Id = 2,
Name = "Store1",
};
var product = new ProductModel()
{
Id = 1,
Name = "Product",
};
store1.Products.Add(product);
product.Stores.Add(store1);
store0.Products.Add(product);
product.Stores.Add(store0);
var store0Entity = mapper.Map<StoreEntity>(store0);
Debug.Assert(store0Entity.Products[0].Product.Stores[0].Store.Id ==
store0Entity.Products[0].Product.Stores[0].Store.Products[0].StoreId);
}
}
}
Mapping pass successfully, but by some reason some deep key is not mapped to the related StoreEntity
The following assertion is failed ...
Debug.Assert(store0Entity.Products[0].Product.Stores[0].Store.Id ==
store0Entity.Products[0].Product.Stores[0].Store.Products[0].StoreId);
Seems like by some reason in depth Store class AutoMapper uses wrong mapped List ... but I am not sure ...
I have figured out how to fixed this issue (thanks to answer on question Automapper creates 2 instances of one object)
For detecting Circular references I can add .PreserveReferences() method for mapping
Here is proper CustomProfile:
public class CustomProfile : Profile
{
public CustomProfile()
{
CreateMap<StoreModel, StoreEntity>()
.ForMember(d => d.Products,
opt => opt.MapFrom(s => s.Products))
.AfterMap((model, entity) =>
{
foreach (var entityProduct in entity.Products)
{
entityProduct.StoreId = entity.Id;
entityProduct.Store = entity;
}
})
.PreserveReferences();
CreateMap<StoreModel, StoreProductEntity>()
.ForMember(entity => entity.StoreId, opt => opt.MapFrom(model => model.Id))
.ForMember(entity => entity.Store, opt => opt.MapFrom(model => model))
.ForMember(entity => entity.ProductId, opt => opt.Ignore())
.ForMember(entity => entity.Product, opt => opt.Ignore());
CreateMap<ProductModel, ProductEntity>()
.ForMember(d => d.Stores,
opt => opt.MapFrom(s => s.Stores))
.AfterMap((model, entity) =>
{
foreach (var entityStore in entity.Stores)
{
entityStore.ProductId = entity.Id;
entityStore.Product = entity;
}
})
.PreserveReferences();
CreateMap<ProductModel, StoreProductEntity>()
.ForMember(entity => entity.StoreId, opt => opt.Ignore())
.ForMember(entity => entity.Store, opt => opt.Ignore())
.ForMember(entity => entity.ProductId, opt => opt.MapFrom(model => model.Id))
.ForMember(entity => entity.Product, opt => opt.MapFrom(model => model));
}
}
But actually I did not know if it is an issue or not, because according the documentation AutoMapper should detect Circular References automatically for AutoMapper version 10.0.0 and seems like this is an issue

How to map Object to KeyValuePair<char, string> using AutoMapper?

I'm trying to map my object model to a KeyValuePair<char, string> but my results are KeyValuePairs where Key = null and Value = null.
What's the correct way to do this?
Model:
public class Symbol
{
public int Id { get; set; }
public int TemplateId { get; set; }
public Template Template { get; set; }
public char Letter { get; set; }
public string ImgSource { get; set; }
}
Profile:
public class AutoMapping : Profile
{
public AutoMapping()
{
CreateMap<Symbol, KeyValuePair<object, object>>()
.ForMember(dest => dest.Key, opt => opt.MapFrom(src => src.Letter))
.ForMember(dest => dest.Value, opt => opt.MapFrom(src => src.ImgSource))
.ReverseMap();
}
}
Attempt:
using (_dbContext)
{
var q = await _dbContext.Symbol
.Where(x => x.TemplateId == templateId)
.OrderBy(x => x.Letter)
.Select(x => _mapper.Map<KeyValuePair<char, string>>(x))
.ToListAsync();
//return _mapper.Map<List<KeyValuePair<char, string>>>(q);
return q;
}
Solved by using the following :
CreateMap<Symbol, KeyValuePair<char, string>>()
.ConstructUsing(sym => new KeyValuePair<char, string>(sym.Letter, sym.ImgSource));

AutoMapper advance mapping

I want to like merge those Source objects into a List<Destination>. Notice that SourceParent and Destination Id property MUST be the same.
var parent = new SourceParent
{
Id = 1,
Childs = new List<SourceChild>
{
new SourceChild { ChildId = 12, OtherProperty = "prop1" },
new SourceChild { ChildId = 13, OtherProperty = "prop2" },
new SourceChild { ChildId = 14, OtherProperty = "prop3" },
}
};
Mapper.Initialize(cfb =>
{
cfb.CreateMap<SourceParent, List<Destination>>()
.ForMember(dest => dest, opt => opt.MapFrom(src => src.Childs));
cfb.ValidateInlineMaps = false;
});
List<Destination> destination = Mapper.Map<SourceParent, List<Destination>>(parent);
Classes:
public class SourceParent
{
public int Id { get; set; }
public List<SourceChild> Childs { get; set; }
}
public class SourceChild
{
public string OtherProperty { get; set; }
public int ChildId { get; set; }
}
public class Destination
{
public int SourceParentId { get; set; }
public string OtherProperty { get; set; }
public int ChildId { get; set; }
}
Is there a way to create a mapping rule for this case? Is it even possible?
I think your best option here is to define a TypeConverter.
You can do TypeConverters inline like I've done below or you can define a class that implements the ITypeConverter<TIn, TOut> interface.
cfb.CreateMap<SourceParent, List<Destination>>().ConvertUsing((src, dest, context) =>
{
return src.Childs.Select(x =>
{
var destination = context.mapper.Map<Destination>(x);
destination.SourceParentId = src.Id;
return destination;
}
});
If you wanted to (I usually stay away from this because it can get unruly fast) you could define another custom mapping using a tuple or a wrapper class like this.
cfb.CreateMap<SourceParent, List<Destination>>().ConvertUsing((src, dest, context) =>
{
return src.Childs.Select(x => context.mapper.Map<Destination>((src.Id, x)))
});
cfb.CreateMap<(int partentId, SourceChild child), Destination>()
.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.parentId))
.ForMember(dest => dest.ChildId, opt => opt.MapFrom(src => src.child.Id))
.ForMember(dest => dest.OtherProperty , opt => opt.MapFrom(src => src.child.OtherProperty ));
This can be nice for small examples but if you are doing it often it can lead to really cluttered mapper configurations (in my opinion), but it does simplify your type converter.

AutoMapper settings properties to null on destination object

I have something like this:
public class DomainEntity
{
public string Name { get; set; }
public string Street { get; set; }
public IEnumerable<DomainOtherEntity> OtherEntities { get; set; }
public IEnumerable<DomainAnotherEntity> AnotherEntities { get; set; }
}
public class ApiEntity
{
public string Name { get; set; }
public string Street { get; set; }
public int OtherEntitiesCount { get; set; }
}
And following mapper configuration:
Mapper.Configuration.AllowNullCollections = true;
Mapper.CreateMap<DomainEntity, ApiEntity>().
ForSourceMember(e => e.OtherEntities, opt => opt.Ignore()).
ForSourceMember(e => e.AntherEntities, opt => opt.Ignore()).
ForMember(e => e.OtherEntitiesCount, opt => opt.MapFrom(src => src.OtherEntities.Count()));
Mapper.CreateMap<ApiEntity, DomainEntity>().
ForSourceMember(e => e.OtherEntitiesCount, opt => opt.Ignore()).
ForMember(e => e.OtherEntities, opt => opt.Ignore()).
ForMember(e => e.AnotherEntities, opt => opt.Ignore());
To get the ApiEntity from the DomainEntity I'm using var apiEntity = Mapper.Map<DomainEntity, ApiEntity>(myDomainEntity);
To get the merged DomainEntity from an ApiEntity I'm using var domainEntity = Mapper.Map(myApiEntity, myDomainEntity);
But when using this, the properties OtherEntities and AnotherEntities are set to null - even when they had values before calling the mapping from myApiEntity to myDomainEntity. How can I avoid this so they really merge and not just replacing values?
Thanks for any help.
I think you're looking for UseDestinationValue instead of Ignore:
Mapper.CreateMap<ApiEntity, DomainEntity>().
ForSourceMember(e => e.OtherEntitiesCount, opt => opt.UseDestinationValue()).
ForMember(e => e.OtherEntities, opt => opt.UseDestinationValue()).
ForMember(e => e.AnotherEntities, opt => opt.UseDestinationValue());

Categories