I know that Automapper can automatically map from:
class SomeClassA
{
public int Id { get; set; }
public B Member { get; set; }
}
class SomeClassB
{
public int Id { get; set; }
public string Name { get; set; }
}
to:
class SomeClassADto
{
public int Id { get; set; }
public int Member_Id { get; set; }
public string Member_Name { get; set; }
}
But how can I make Automapper map from my SomeClassADto to SomeClassA automatically?
No the prettiest thing in the world, but it does work. Downside is maintenance is a pain as you add properties to the DTO and class you need to update the mapping.
var config = new MapperConfiguration(x =>
{
x.CreateMap<SomeClassADto, SomeClassB>()
.ForMember(i => i.Id, i => i.MapFrom(src => src.Member_Id))
.ForMember(i => i.Name, i => i.MapFrom(src => src.Member_Name));
x.CreateMap<SomeClassADto, SomeClassA>()
.AfterMap((s, d, r) => d.Member = r.Mapper.Map<SomeClassB>(s));
});
IMapper mapper = config.CreateMapper();
var foo = mapper.Map<SomeClassA>(new SomeClassADto() { Id = 1, Member_Id = 2, Member_Name = "Name" });
Related
Let's say I have the following objects:
Source:
public class Scores()
{
public int Id { get; set; }
public string Name { get; set; }
public decimal ScoreA { get; set; }
public decimal ScoreB { get; set; }
public decimal ScoreC { get; set; }
}
Destination:
public class ScoresDto()
{
public int Id { get; set; }
public string Name { get; set; }
public IList<Score> ScoreDetails { get; set; }
}
public class Score()
{
public string Name { get; set; }
public decimal Value { get; set; }
}
I understand how I can map straight 1 to 1 property mapping like this:
public AutomapperProfiles()
{
CreateMap<Scores, ScoresDto>()
.ForMember(x => x.Id, opt => opt.MapFrom(y => y.Id))
.ForMember(x => x.Name, opt => opt.MapFrom(y => y.Name))
}
However, how would I map as part of the above mapping ScoreA, ScoreB and ScoreC into individual new Score objects, where 'Name' is the name of the property and Value is the decimal value, and then have those scores make up the ScoreDetails list on ScoresDto?
You can create a custom resolver to do the job;
public class ScoreDetailsResolver : IValueResolver<Scores, ScoresDto, IList<Score>>
{
public IList<Score> Resolve(Scores source, ScoresDto destination, IList<Score> member, ResolutionContext context)
{
destination = destination ?? new ScoresDto();
destination.ScoreDetails = destination.ScoreDetails ?? new List<Score>();
destination.ScoreDetails.Add(new Score { Name = source.Name, Value = source.ScoreA });
destination.ScoreDetails.Add(new Score { Name = source.Name, Value = source.ScoreB });
destination.ScoreDetails.Add(new Score { Name = source.Name, Value = source.ScoreC });
return destination.ScoreDetails;
}
}
And use it like this:
CreateMap<Scores, ScoresDto>()
.ForMember(d => d.ScoreDetails, o => o.MapFrom<ScoreDetailsResolver>());
No need to map other properties as they will be mapped automatically.
I have defined a mapping from one type to DTO. Another type references the first type as an property, but the output should be an flattened DTO that should use the already defined mapping for the first type.
class Program {
static void Main(string[] args) {
var mapperConfiguration = new MapperConfiguration(cfg => {
cfg.CreateMap<FirstDataType,
FirstTypeDto>().ForMember(d => d.TypeResult, opt => opt.MapFrom(s => s.ToString()));
/* HOW TO CONFIGURE MAPPING OF THE 'FirstData' PROPERTY TO USE THE ABOVE DEFINED MAPPING
cfg.CreateMap<SecondDataType, SecondTypeDto>()
*/
});
var firstData = new FirstDataType {
TypeName = "TestType",
TypeValue = "TestValue"
};
var secondData = new SecondDataType {
Id = 1,
Name = "Second type",
FirstData = firstData
};
var mapper = mapperConfiguration.CreateMapper();
var firstDto = mapper.Map<FirstTypeDto>(firstData);
var secondDto = mapper.Map<SecondTypeDto>(secondData);
Console.ReadKey(true);
}
}
public class FirstDataType {
public string TypeName {
get;
set;
}
public string TypeValue {
get;
set;
}
public override string ToString() {
return $ "{TypeName}: {TypeValue}";
}
}
public class SecondDataType {
public int Id {
get;
set;
}
public string Name {
get;
set;
}
public FirstDataType FirstData {
get;
set;
}
}
public class FirstTypeDto {
public string TypeName {
get;
set;
}
public string TypeValue {
get;
set;
}
public string TypeResult {
get;
set;
}
}
public class SecondTypeDto: FirstTypeDto {
public int Id {
get;
set;
}
public string Name {
get;
set;
}
}
How should I configure mapping for the second type to use the defined mapping for the property 'FirstData'?
Thanks!
First, credit goes to Lucian Bargaoanu for leading me in the right direction.
Basically, you need to create a mapping from the source to the destination derived type, but just include the existing mapping.
cfg.CreateMap<FirstDataType, SecondTypeDto>()
.IncludeBase<FirstDataType, FirstTypeDto>()
.ReverseMap();
cfg.CreateMap<SecondDataType, SecondTypeDto>()
.IncludeMembers(s => s.FirstData)
.ReverseMap();
I have 2 objects which need to be converted to Dto object using automapper.
Those 2 objects are
public class Parent
{
public int ParentID { get; set;}
public string ParentCode { get; set; }
}
public class Child
{
public int ChildID { get; set; }
public int ParentID { get; set;}
public string ChildCode { get; set; }
}
And 2 Dto objects
public class ParentDto
{
public int ParentID { get; set; }
public string ParentCode { get; set; }
public List<ChildDTO> ListChild { get; set; }
}
public class ChildDto
{
public int ChildID { get; set; }
public string ChildCode { get; set; }
}
Need to convert 2 set of list
List<Parent> ListParent
List<Child> ListChild
To
List<ParentDto> ListParentDto
Because you have multiple sources, my solution was a method like
public static List<ParentDto> MapToDto(List<Parent> parents, List<Child> childs)
{
List<ParentDto> parentDtos = new List<ParentDto>();
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Parent, ParentDto>().BeforeMap((s, d) =>
{
d.ListChild = new List<ChildDto>();
foreach (var child in childs.Where(c => c.ParentID == s.ParentID))
{
d.ListChild.Add(new ChildDto { ChildCode = child.ChildCode, ChildID = child.ChildID });
}
}).ForMember(dest => dest.ParentID, opt => opt.MapFrom(src => src.ParentID)).ForMember(dest => dest.ParentCode, opt => opt.MapFrom(src => src.ParentCode));
});
IMapper mapper = config.CreateMapper();
foreach (var p in parents)
{
var source = p;
var destination = mapper.Map<Parent, ParentDto>(source);
parentDtos.Add(destination);
}
return parentDtos;
}
I'm working with Entity Framework, and getting ready to redo a project that is currently using EF->BDO->DTO->client with manual conversions for each class along the way.
I came across AutoMapper and think this would be a better solution overall. I have had no issues mapping CustomerEF -> CustomerDto, OfficeEF -> OfficeDto, etc, but I am now working on a more complex class, and struggling to get everything in place.
I feel I am close, and that something has to happen in reverse, but have not been able to identify what I'm missing.
public class CaseDto
{
public int CaseId { get; set; }
public string CaseReason { get; set; }
public CustomersDto _customer { get; set; }
public OfficeDto _office { get; set; }
public CaseNotesDto[] _caseNotes { get; set; }
}
public class CustomersDto
{
public int CustomerId { get; set; }
public string CustomerName { get; set; }
}
public class OfficeDto
{
public int OfficeId { get; set; }
public string OfficeName { get; set; }
}
public class CaseNotesDto
{
public int CaseNotesId { get; set; }
public int CaseId { get; set; }
public string CaseNote { get; set; }
}
// EF objects
public class CaseEF
{
public int CaseId { get; set; }
public string CaseReason { get; set; }
public int CustomerId { get; set; }
public int OfficeId { get; set; }
}
public class CustomerEF
{
public int CustomerId { get; set; }
public string CustomerName { get; set; }
}
public class OfficeEF
{
public int OfficeId { get; set; }
public string OfficeName { get; set; }
}
public class CaseNotesEF
{
public int CaseNotesId { get; set; }
public int CaseId { get; set; }
public string CaseNote { get; set; }
}
// execution classes
public class CaseFramework
{
// set 'ef' variables
private readonly OfficeEF _officeEf = new OfficeEF { OfficeId = 1, OfficeName = "Washington" };
private readonly CustomerEF _customerEf = new CustomerEF { CustomerId = 1, CustomerName = "Blips and Chitz" };
private readonly CaseNotesEF[] _caseNotesEf =
{
new CaseNotesEF {CaseNotesId = 1, CaseNote = "Case 1", CaseId = 1000},
new CaseNotesEF {CaseNotesId = 2, CaseNote = "Case 2", CaseId = 1000}
};
private readonly CaseEF _case =
new CaseEF { CaseId = 1000, CaseReason = "Roy is back!", CustomerId = 1, OfficeId = 1 };
public CaseDto GetCase(int caseId)
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<OfficeEF, OfficeDto>();
cfg.CreateMap<CustomerEF, CustomersDto>();
cfg.CreateMap<CaseNotesEF, CaseNotesDto>();
cfg.CreateMap<CaseEF, CaseDto>()
.ForMember(dest => dest._customer, opt => opt.MapFrom(src => src.CustomerId))
.ForMember(dest => dest._office, opt => opt.MapFrom(src => src.OfficeId))
.ForMember(dest => dest._caseNotes,
opt => opt.MapFrom(src => _caseNotesEf.Where(x => x.CaseId == caseId)));
});
Mapper.AssertConfigurationIsValid();
var source = new CaseEF { CaseId = caseId };
var destination = Mapper.Map<CaseEF, CaseDto>(source);
return destination;
}
}
To run this I am doing:
var b = new CaseFramework();
var result = b.GetCase(1000);
The results are populating the CaseId (set manually) and the CaseNotesDto, but nothing else.
Having the first 3 cfg.CreateMap items in the Mapper.Initialize section makes no difference if they are there or not.
Thanks in advance for any guidance.
Appreciate the responses.
#MickyD, I was a little confused on the POCO comment, as the EF already generated a tt, and the Canonical schema/model looks like something I'll have to research more.
#Steve I checked my navigation properties from my EF database and we need to do some fixes to the relationships before making the Include method work, but I think that will ultimately be the solution. In the meantime I was able to accomplish the original goal with this:
Mapper.AssertConfigurationIsValid();
var destination = Mapper.Map<CaseDto>(_case);
destination.Customers = Mapper.Map<CustomerEF, CustomersDto>(_customerEf);
destination.Office = Mapper.Map<OfficeEF, OfficeDto>(_officeEf);
return destination;
Thanks.
I have two classes below:
public class Module
{
public int Id { get; set; }
public string Name { get; set; }
public string ImageName { get; set; }
public virtual ICollection<Page> Pages { get; set; }
}
public class ModuleUI
{
public int Id { get; set; }
public string Text { get; set; }
public string ImagePath { get; set; }
public List<PageUI> PageUIs { get; set; }
}
The mapping must be like this:
Id -> Id
Name -> Text
ImageName -> ImagePath
Pages -> PageUIs
How can I do this using Automapper?
You can use ForMember and MapFrom (documentation).
Your Mapper configuration could be:
Mapper.CreateMap<Module, ModuleUI>()
.ForMember(s => s.Text, c => c.MapFrom(m => m.Name))
.ForMember(s => s.ImagePath, c => c.MapFrom(m => m.ImageName))
.ForMember(s => s.PageUIs, c => c.MapFrom(m => m.Pages));
Mapper.CreateMap<Page, PageUI>();
Usage:
var dest = Mapper.Map<ModuleUI>(
new Module
{
Name = "sds",
Id = 2,
ImageName = "sds",
Pages = new List<Page>
{
new Page(),
new Page()
}
});
Result: