AutoMapper not mapping sub entity list - c#

I mapping a model across, which has a child list sub map as well. However, after calling the map, the sub list is not being mapped? I am using AutoMapper 9.0.0 with AutoMapper.Extensions.Microsoft.DependencyInjection 7.0.0 (note this is the parent node in the package list.
I have as follows (reduced for brevity):
public class Agreement
{
//...
public List<Document> Documents { get; set; }
}
public class Document : Entity
{
public string Url { get; set; }
public string Location { get; set; }
public string MimeType { get; set; }
public string FileHash { get; set; }
public float FileSize { get; set; }
public string Notes { get; set; }
public string Type { get; set; }
public byte[] Data { get; set; }
}
public class AgreementDataGridOutputModel : BaseModel
{
//...
public List<DocumentOutputModel> Documents { get; set; }
}
public class DocumentOutputModel
{
public int Id { get; set; }
public string Url { get; set; }
public string MimeType { get; set; }
public string Notes { get; set; }
}
My Mappings are as follows;
CreateMap<Document, DocumentOutputModel>();
CreateMap<List<Document>, List<DocumentOutputModel>>();
CreateMap<Agreement, AgreementDataGridOutputModel>()
.ForMember(dest => dest.AgreementType, opt => opt.MapFrom(src => src.Type.Name))
.ForMember(dest => dest.CompanyName, opt => opt.MapFrom(src => src.Company.Name))
.ForMember(dest => dest.Documents, opt => opt.MapFrom(src => src.Documents));
CreateMap<List<Agreement>, List<AgreementDataGridOutputModel>>();
I then map in my controller as follows;
var response = await _agreementService.FindAsync(criteria);
var output = _mapper.Map<IList<Agreement>,IList<AgreementDataGridOutputModel>>(response.Result);
Can anyone see what I am doing wrong here please?

Mapping collections prevent the collection properties maps from working. See a working test below:
using AutoMapper;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
var list = new List<ParentSource> {
new ParentSource {
Id =1,
Name = "My name",
MyList = new List<ChildSource> {
new ChildSource { Id = 1, Name = "Child name" }
}
}
};
var conf = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ParentSource, ParentTarget>();
cfg.CreateMap<ChildSource, ChildTarget>();
/*
* This line prevents the list mappings from working...
*/
// cfg.CreateMap<List<ChildSource>, List<ChildTarget>>();
});
var mapper = new Mapper(conf);
var targets = mapper.Map<List<ParentSource>, IList<ParentTarget>>(list);
Console.WriteLine(JsonSerializer.Serialize(targets));
// Output: [{"Id":1,"Name":"My name","MyList":[{"Id":1,"Name":"Child name"}]}]
Console.ReadLine();
}
}
public class ParentSource
{
public int Id { get; set; }
public string Name { get; set; }
public List<ChildSource> MyList { get; set; }
}
public class ChildSource
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ParentTarget
{
public int Id { get; set; }
public string Name { get; set; }
public List<ChildTarget> MyList { get; set; }
}
public class ChildTarget
{
public int Id { get; set; }
public string Name { get; set; }
}
}

Related

AutoMapper to map a nested list object

I have 2 objects : Parent and ParentDTO :
class ParentDTO
{
public string AttA { get; set; }
public List<List<BDto>> AttrBNestedList { get; set; }
}
class BDto
{
public string AttrC { get; set; }
public int AtterD { get; set; }
public string AtterE { get; set; }
public CDto AttrFobj { get; set; }
}
class CDto
{
public string AttrG { get; set; }
public int AtterH { get; set; }
}
//--------------------------------------
class Parent
{
public string AttA { get; set; }
public List<B> AttrBList { get; set; }
}
class B
{
public string AttrC { get; set; }
public int AtterD { get; set; }
public string AtterE { get; set; }
public C AttrFobj { get; set; }
public C AttrGobj { get; set; }
public C AttrHobj { get; set; }
}
class C
{
public string AttrI { get; set; }
public string AttrJ { get; set; }
public int AtterK { get; set; }
}
I want to using AutoMapper to map data from ParentDto object to Parent object
(all data in ParentDto has to transfer to Parent, but first element from AttrBNestedList copy to AttrBList)
Mapper.CreateMap<CDto, C>();
Mapper.CreateMap<BDto, B>();
Mapper.CreateMap<ParentDto, Parent>()
.ForMember(dest => dest.AttrBList , opt => opt.AttrBNestedList.singleorDefault??);
I need just singleorDefault item from List<List<BDto>> AttrBNestedList map to List<B> AttrBList
Can anyone help me. Thanks
There is a syntax error which doesn't allow you to use SingleOrDefault(), you need to use MapFrom method:
Mapper.CreateMap<ParentDTO, Parent>().ForMember(dest => dest.AttrBList, opt => opt.MapFrom(src => src.AttrBNestedList.SingleOrDefault()));

AutoMapper Map derived classes to property of base type collection

I'm trying to map two different objects to objects that are derived from an interface. Additionally, I need to have another property mapped to the derived types from the dtos. Given this object structure:
public interface ICoverage
{
string Name { get; set; }
string Code { get; set; }
}
public class CoverageA : ICoverage
{
public string Name { get; set; }
public string Code { get; set; }
public string Current { get; set; }
}
public class CoverageB : ICoverage
{
public string Name { get; set; }
public string Code { get; set; }
public bool HasRecord { get; set; }
}
public class Application
{
public int ApplicationId { get; set; }
public string Code { get; set; }
public List<ICoverage> Coverages { get; set; }
public Application()
{
Coverages = new List<ICoverage>();
}
}
public class StagingDto
{
public string Referrer { get; set; }
public string Code { get; set; }
public CoverageADto CoverageA { get; set; }
public CoverageBDto CoverageB { get; set; }
}
public class CoverageADto
{
public string Current { get; set; }
}
public class CoverageBDto
{
public bool HasRecord { get; set; }
}
This mapping below works but I am wondering if there is a better way to do it:
cfg.CreateMap<StagingDto, Application>()
.AfterMap((src, dest) => dest.Coverages.Add(new CoverageB()
{
HasRecord = src.CoverageB.HasRecord,
Code = src.Code
}))
.AfterMap((src, dest) => dest.Coverages.Add(new CoverageA()
{
Current = src.CoverageA.Current,
Code = src.Code
}));
Ideally I'd like to stay away from having to create any extension method.
For me it looks a bit better:
cfg.CreateMap<StagingDto, Application>()
.ForMember(dest => dest.Coverages,
opt => opt.ResolveUsing(src => new ICoverage[]
{
new CoverageA
{
Current = src.CoverageA.Current,
Code = src.Code
},
new CoverageB
{
HasRecord = src.CoverageB.HasRecord,
Code = src.Code
}
}));

AutoMap To Eliminate Join Table

Currently I have the following Objects that I need to map
OBJECT 1
public class ContactInfo
{
public int ContactInfoId { get; set; }
public ICollection<PhoneToCustomer> Phones { get; set; }
}
public class PhoneToCustomer
{
public int ContactInformationId { get; set; }
public Phone Phone { get; set; }
public int PhoneId { get; set; }
}
public class Phone
{
public int PhoneId { get; set; }
public long Number { get; set; }
public PhoneType Type { get; set; }
public string Extension { get; set; }
public string CountryCode { get; set; }
public PhoneRestrictions Restrictions { get; set; }
public bool Primary { get; set; }
}
I am trying to map it to the following object
OBJECT 2
public class ContactInfoModel
{
public int ContactInfoId { get; set; }
public ICollection<PhoneModel> Phones { get; set; }
}
public class PhoneModel
{
public int PhoneId { get; set; }
public long Number { get; set; }
public PhoneType Type { get; set; }
public string Extension { get; set; }
public string CountryCode { get; set; }
public PhoneRestrictions Restrictions { get; set; }
public bool Primary { get; set; }
}
Essentially I am wanting to eliminate the PhoneToCustomer object when mapping it. I need the ContactInfoModel.Phones to map to ContactInfo.PhoneToCustomer.Phone (to list)
In order to map ContactInfo to ContactInfoModel you need to add the following mappings:
AutoMapper.Mapper.CreateMap<Phone, PhoneModel>();
AutoMapper.Mapper.CreateMap<ContactInfo, ContactInfoModel>()
.ForMember(x => x.Phones, y => y.MapFrom(z => z.Phones.Select(q => q.Phone)));
If you want to map vice versa from ContactInfoModel to ContactInfo you can use the following mappings:
AutoMapper.Mapper.CreateMap<PhoneModel, Phone>();
AutoMapper.Mapper.CreateMap<PhoneModel, PhoneToCustomer>()
.ForMember(x => x.Phone, y => y.MapFrom(z => z))
.ForMember(x => x.ContactInformationId, y => y.Ignore())
.ForMember(x => x.PhoneId, y => y.Ignore());
AutoMapper.Mapper.CreateMap<ContactInfoModel, ContactInfo>();
Here is a test example of usage:
var contactInfo = new ContactInfo()
{
ContactInfoId = 1,
Phones = new List<PhoneToCustomer>()
{
new PhoneToCustomer()
{
Phone = new Phone(){CountryCode = "Code1", Extension = "Extension1"},
},
new PhoneToCustomer()
{
Phone = new Phone(){CountryCode = "Code2", Extension = "Extension2"}
}
}
};
var contactInfoModel = AutoMapper.Mapper.Map<ContactInfoModel>(contactInfo);
var contactInfoBack = AutoMapper.Mapper.Map<ContactInfo>(contactInfoModel);

Mapping Source Collection's Id to Destination Objects Collection Id

Suppose in the source object I have classes:
// Source classes
class Source
{
public Source
{
things = new List<Thing>();
}
public Guid SourceId { get; set; }
public string Name { get; set; }
public virtual List<Thing> Things { get; set; }
}
class Thing
{
public Guid ThingId { get; set; }
public double Price { get; set; }
}
//Destination class
class Dest
{
public Guid DestId { get; set; }
public string Name { get; set; }
public virtual List<Guid> ThingsIds { get; set; }
}
How do I map Things -> ThingId (src) to ThingsIds (dest) using Automapper?
I would use the LINQ extension method .Select:
Mapper.CreateMap<Source, Dest>()
.ForMember(
dest => dest.ThingsIds,
opt => opt.MapFrom(src => src.Things.Select(th => th.ThingId)));

ASP.Net C# MVC Issue Mapping Domain Model to ViewModel using AutoMapper

I'm having a little difficulty mapping a domain model to a view model, using AutoMapper.
My controller code is:
//
// GET: /Objective/Analyst
public ActionResult Analyst(int id)
{
var ovm = new ObjectiveVM();
ovm.DatePeriod = new DateTime(2013, 8,1);
var objectives = db.Objectives.Include(o => o.Analyst).Where(x => x.AnalystId == id).ToList();
ovm.ObList = Mapper.Map<IList<Objective>, IList<ObjectiveVM>>(objectives);
return View(ovm);
}
I am getting an error on the ovm.ObList = Mapper.... (ObList is underlined in red with the error):
'ObList': cannot reference a type through an expression; try 'Objectives.ViewModels.ObjectiveVM.ObList' instead
My Objective Class is:
public class Objective
{
public int ObjectiveId { get; set; }
public int AnalystId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public Analyst Analyst { get; set; }
}
My ObjectiveVM (view model) is:
public class ObjectiveVM
{
public DateTime DatePeriod { get; set; }
public class ObList
{
public int ObjectiveId { get; set; }
public int AnalystId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string AnalystName { get; set; }
public bool Include { get; set; }
}
}
In my startup/global.asax.cs I have used AutoMapper to map the Objective to the ObjectiveVM:
Mapper.CreateMap<Objective, ObjectiveVM.ObList>()
.ForMember(dest => dest.Include, opt => opt.Ignore())
.ForMember(dest => dest.AnalystName, opt => opt.MapFrom(y => (y.Analyst.AnalystName)));
Any help would be much appreciated,
Mark
Ok, thanks for all the suggestions - what I've ended up with is:
Controller:
//
// GET: /Objective/Analyst
public ActionResult Analyst(int id)
{
var ovm = new ObjectiveVM().obList;
var objectives = db.Objectives.Include(o => o.Analyst).Where(x => x.AnalystId == id).ToList();
ovm = Mapper.Map<IList<Objective>, IList<ObjectiveVM.ObList>>(objectives);
var ovm2 = new ObjectiveVM();
ovm2.obList = ovm;
ovm2.DatePeriod = new DateTime(2013, 8,1);
return View(ovm2);
}
ViewModel:
public class ObjectiveVM
{
public DateTime DatePeriod { get; set; }
public IList<ObList> obList { get; set; }
public class ObList
{
public int ObjectiveId { get; set; }
public int AnalystId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string AnalystName { get; set; }
public bool Include { get; set; }
}
}
CreateMap:
Mapper.CreateMap<Objective, ObjectiveVM.ObList>()
.ForMember(dest => dest.Include, opt => opt.Ignore())
.ForMember(dest => dest.AnalystName, opt => opt.MapFrom(y => (y.Analyst.AnalystName)))
;
If I've mis-understood any advice, and you provided the answer, please post it - and I'll mark it as such.
Thank you,
Mark
As the commenter nemesv has rightly mentioned, the problem is about
ovm.ObList = Mapper.Map<IList<Objective>, IList<ObjectiveVM>>(objectives);
ObList is not a member of ObjectiveVM so, you should change the ObjectiveVM like this:
public class ObjectiveVM
{
public DateTime DatePeriod { get; set; }
public IList<ObList> obList { get; set; }
public class ObList
{
public int ObjectiveId { get; set; }
public int AnalystId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string AnalystName { get; set; }
public bool Include { get; set; }
}
}
Update:
Controller:
public ActionResult Analyst(int id)
{
var ovm = new ObjectiveVM { DatePeriod = new DateTime(2013, 8, 1) };
var objectives = db.Objectives.Include(
o => o.Analyst).Where(x => x.AnalystId == id).ToList();
ovm.obList = Mapper.Map<IList<Objective>,
IList<ObjectiveVM.ObList>>(objectives);
return View(ovm);
}

Categories