Mapping a nested collections using Automapper - c#

I have been trying to map my entities to my viewmodels with AutoMapper. And faced problems with nested collection mapping.
The Source
public class Consignment
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<ConsignmentLine> ConsignmentLines { get; set; }
public ICollection<ConsignmentDocument> ConsignmentDocuments { get; set; }
}
public class ConsignmentLine
{
public Guid Id { get; set; }
public Guid ConsignmentId { get; set; }
public ICollection<ConsignmentDocument> ConsignmentDocuments { get; set; }
}
public class ConsignmentDocument
{
public Guid Id { get; set; }
public Guid ConsignmentId { get; set; }
public Guid ConsignmentLineId { get; set; }
public string DocumentName { get; set; }
}
public class ConsignmentLineViewModel
{
public Guid Id { get; set; }
public Guid ConsignmentId { get; set; }
public ICollection<ConsignmentDocumentViewModel> ConsignmentDocuments { get; set; }
}
public class ConsignmentDocumentViewModel
{
public Guid Id { get; set; }
public Guid ConsignmentId { get; set; }
public Guid ConsignmentLineId { get; set; }
public string DocumentName { get; set; }
}
The destination
public class ConsignmentDetailsViewModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public ICollection<ConsignmentLineViewModel> ConsignmentLines { get; set; }
public ICollection<ConsignmentDocumentViewModel> ConsignmentDocuments { get; set; }
}
I can map consignmentDocuments for each consignment very easily but while mapping consignmentlines for each consignment i am getting an "AutoMapper Exception". I know the exception is being generated because of each consignmentLine has it's own collection of consignmentDocuments.
Right now my automapper profile
CreateMap<Consignment, ConsignmentDetailsViewModel>()
.ForMember(vm => vm.consignmentLineViewModel, opt => opt.MapFrom(model => model.ConsignmentLine.ToList()))
.ForMember(vm => vm.consignmentDocumentViews, opt => opt.MapFrom(model => model.ConsignmentDocument.ToList()));
How can I map all of them to the ConsignmentViewModel class?

Resolved the problem.
The solution is to create a map for ConsignmentLine to get the collection of ConsignmentDocuments.
CreateMap<Consignment, ConsignmentDetailsViewModel>()
.ForMember(vm => vm.consignmentLineViewModel, opt => opt.MapFrom(model => model.ConsignmentLine))
.ForMember(vm => vm.consignmentDocumentViews, opt => opt.MapFrom(model => model.ConsignmentDocument));
CreateMap<ConsignmentLine, ConsignmentLineViewModel>()
.ForMember(vm => vm.consignmentDocumentViews, opt => opt.MapFrom(model => model.ConsignmentDocument));

If you act simply without thinking too complex in AutoMapper transactions, you can perform all your transactions.
Example:
CreateMap<Consignment, ConsignmentDetailsViewModel>();
CreateMap<ConsignmentLine, ConsignmentLineViewModel>();
CreateMap<ConsignmentDocument, ConsignmentDocumentViewModel>();

Related

How can use AutoMapper to map two collections of objects

I try to use AutoMapper but when I what to map 2 collection I get an error.
These are my entity and dto classes that I want to use AutoMapper:
public class HeadQuarters
{
public int Id { get; private set; }
public string HeadQuartersName { get; set; }
public string HeadQuartersCode { get; set; }
public string HeadQuartersDescription { get; set; }
public bool IsActiv { get; set; }
public ICollection<Adresa> Adresa { get; set; }
}
public class HeadQuartersDTO
{
public string HeadQuartersName { get; set; }
public string HeadQuartersCode { get; set; }
public string HeadQuartersDescription { get; set; }
public ICollection<AdresaDTO> Addresses { get; set; }
public EntityState Status { get; set; }
}
These are my entity and dto collection classes:
public class AdresaDTO
{
public int Id { get; set; }
public string Street { get; set; }
public string StreetNr { get; set; }
public string Block { get; set; }
public string Entrance{ get; set; }
public string Apartment{ get; set; }
public double? Longitude { get; set; }
public double? Latitude { get; set; }
public int? CityId { get; set; }
public EntityState Status { get; set; }
}
public partial class Adresa
{
public int Id { get; private set; }
public string Street { get; set; }
public string StreetNr { get; set; }
public string Block { get; set; }
public string Entrance{ get; set; }
public string Apartment{ get; set; }
public double? Longitude { get; set; }
public double? Latitude { get; set; }
public int CityId { get; set; }
public int? HeadQuartersId { get; set; }
public int? EmployeeId { get; set; }
public int? ContractPersonDataId { get; set; }
}
I write this code for to use AutoMapper:
public static HeadQuarters DtoToEntity(HeadQuartersDTO dto)
{
var mapper = new Mapper(MapperConfiguration());
return mapper.Map<HeadQuarters>(dto);
}
private static MapperConfiguration MapperConfiguration()
{
return new MapperConfiguration(cfg =>
cfg.CreateMap<HeadQuartersDTO, HeadQuarters>()
.ForMember(dest => dest.Adresa, act => act.MapFrom(src => src.Addresses)));
}
But when I add some in collection I get an error. This is the error message that I get:
If I understand what you’re trying to do, you should be able to update your code like this:
public static HeadQuarters DtoToEntity(HeadQuartersDTO dto)
{
var mapper = new Mapper(MapperConfiguration());
return mapper.Map<HeadQuarters>(dto);
}
private static MapperConfiguration MapperConfiguration()
{
return new MapperConfiguration(cfg =>
cfg.CreateMap<AdresaDTO, Adresa>()
.ForMember(dest => dest.CityId, act => act.MapFrom(src => src.CityId ?? default(int)))
.ForMember(dest => dest.HeadQuartersId, act => act.Ignore())
.ForMember(dest => dest.EmployeeId, act => act.Ignore())
.ForMember(dest => dest.ContractPersonDataId, act => act.Ignore());
cfg.CreateMap<HeadQuartersDTO, HeadQuarters>()
.ForMember(dest => dest.Id, act => act.Ignore())
.ForMember(dest => dest.IsActiv, act => act.Ignore())
.ForMember(dest => dest.Adresa, act => act.MapFrom(src => src.Addresses)));
}

AutoMapper map a collection Property value from its Parent property

I have two models, Receipt.cs and ReceiptProduct.cs. What I want to achieve is to map the ICollection ReceiptProducts fields like PurchaseOrderId and ReceiptId from its parent Receipt.
Receipt.cs
public class Receipt
{
public Guid Id { get; set; }
public string Reference { get; set; }
public string PurchaseOrderId { get; set; }
public virtual ICollection<ReceiptProduct> ReceiptProducts { get; set; }
}
ReceiptProduct.cs
public class ReceiptProduct
{
public Guid Id { get; set; }
public string ReceiptId { get; set; }
public string PurchaseOrderId { get; set; }
public string ProductName { get; set; }
public string ProductId { get; set; }
public string Note { get; set; }
}
ReceiptProducts.ReceiptId <= Receipt.Id
ReceiptProducts.PurchaseOrderId <= Receipt.PurchaseOrderId
I tried the below code. But I got the error
CreateMap<DataEntities.Receipt, BusinessEntities.Receipt>()
.ForMember(dest => dest.ReceiptProducts.Select(x=>x.ReceiptId), automapper => automapper.MapFrom(src => src.Id));
Error : AutoMapper.AutoMapperConfigurationException: Custom configuration for members is only supported for top-level individual members on a type.
So how to map that collection property values.
try this.
public class ReceiptProduct
{
public Guid Id { get; set; }
public string ReceiptId { get; set; }
public string PurchaseOrderId { get; set; }
public string ProductName { get; set; }
public string ProductId { get; set; }
public string Note { get; set; }
**public Receipt Receipt { get; set; }**
}
Mapping
CreateMap<DataEntities.ReceiptProduct, BusinessEntities.Receipt>()
.ForMember(dest => x=>x.ReceiptId, opts => opts.MapFrom(src => src.Receipt.Id))
.ForMember(dest => x=>x.PurchaseOrderId , opts => opts.MapFrom(src => src.Receipt.PurchaseOrderId))
.ForMember(dest => x=>x.Reference , opts => opts.MapFrom(src => src.Receipt.Reference ));

Automapper not mapping all fields that are configured

I have a db objects that i would like to map to my view object in my application, but not every property getting mapped.
here is my Automapper set up:
Here is my View class that i would like AutoMapper to map to
public class CustomerDetails
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Dob { get; set; }
public DateTime CreateDate { get; set; }
public decimal Balance { get; set; }
public List<Email> Emails { get; set; }
public List<Address> Addresses { get; set; }
public class Email
{
public Guid Id { get; set; }
public string EmailName { get; set; }
}
public class Address
{
public Guid Id { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public string Country { get; set; }
}
}
Here is db classes:
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual CustomerBalance Balance { get; set; }
public virtual ICollection<Email> Emails { get; set; }
public virtual ICollection<Address> Address { get; set; }
public class Email
{
public Guid Id { get; set; }
public string EmailName { get; set; }
public bool IsPrimary { get; set; }
public virtual Customer Customer { get; set; }
}
public class Address
{
public Guid Id { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public string Country { get; set; }
}
}
here is how i set up my configuration for AutoMapper
Mapper.Initialize(config =>
{
config.AddProfile<CustomerProfile>();
});
public class CustomerProfile : Profile
{
public CustomerProfile()
{
CreateMap<DataModels.Phone, Phone>();
CreateMap<DataModels.Email, Email>();
CreateMap<DataModels.Address, Address>();
CreateMap<DataModels.CustomerPin, Pin>()
.ForMember(x => x.PinNumber, y => y.MapFrom(s => s.Pin))
.ForMember(x => x.Id, y => y.MapFrom(s => s.Id))
;
CreateMap<Customer, CustomerDetails>()
.ForMember(x => x.Phones, y => y.MapFrom(s => s.Phones))
.ForMember(x => x.Emails, y => y.MapFrom(s => s.Emails))
.ForMember(x => x.Balance, y => y.MapFrom(s => s.Balance.Balance))
.ForMember(x => x.Pins, y => y.MapFrom(s => s.Pin))
.ForMember(x => x.Addresses, y => y.MapFrom(s => s.Address))
;
CreateMap<Customer, CustomerDetails>().ReverseMap();
}
}
The strange thing is is that everything getting mapped as expected, except the Addressesproperty and Balance on my CustomerDetails.cs. The list collection is null even though i specified to map it from a member. However, email List is getting mapped appropriately.
Am I missing something?

ArgumentNullException: Value cannot be null. Parameter name: key on Include Statement

Worked a lot with EF 6.x (via designer) and now started on a new project using EF Core.
I'm getting an error that says value cannot be null, not sure exactly what I'm doing wrong. I've got rid of a lot of fields for brevity as there are hundreds.
All these tables are views via synonyms that connect to a different database. I can get it to work fine, if I do each individual call to a database, but as soon as I do include. I get an error on that line. The error I'm getting is
ArgumentNullException: Value cannot be null.
Parameter name: key
System.Collections.Generic.Dictionary.FindEntry(TKey key)
OnGetAsync
var equipment = _context.EMEMs.Include(x => x.EMEDs).Where(x => x.KeyID.ToString() == key);
EMEM = await equipment.Include(x => x.EMCM).ThenInclude(x=>x.EMCDs).FirstOrDefaultAsync();
EMEM
public class EMEM
{
public byte? EMCo { get; set; }
[Display(Name = "Equipment Code")]
public string Equipment { get; set; }
public string Type { get; set; }
public string Category { get; set; }
public Guid? UniqueAttchID { get; set; }
[Key]
public long KeyID { get; set; }
[NotMapped] public string EquipmentDetails => $"{Equipment.Trim()} - {Description} - {VINNumber}";
public virtual IEnumerable<EMWH> EMWHs { get; set; }
public virtual EMCM EMCM { get; set; }
public virtual IEnumerable<udEMED> EMEDs { get; set; }
}
EMCM
public class EMCM
{
[Key]
public long KeyID { get; set; }
public byte? EMCo { get; set; }
public string Category { get; set; }
public string Description { get; set; }
public string Notes { get; set; }
public virtual IEnumerable<EMEM> EMEMs { get; set; }
public virtual IEnumerable<udEMCD> EMCDs { get; set; }
}
udEMCD
public class udEMCD
{
[Key]
public long KeyID { get; set; }
public byte? Co { get; set; }
public string Category { get; set; }
public string DocumentCategory { get; set; }
public int Seq { get; set; }
public Guid? UniqueAttchID { get; set; }
public virtual udEMDC EMDC { get; set; }
public virtual EMCM EMCM { get; set; }
public virtual IEnumerable<HQAT> HQATs { get; set; }
}
Context
modelBuilder.Entity<EMEM>().ToTable("EMEM").HasOne(x => x.EMCM).WithMany(x => x.EMEMs).HasForeignKey(x => new { x.EMCo, x.Category }).HasPrincipalKey(x => new { x.EMCo, x.Category });
modelBuilder.Entity<EMEM>().ToTable("EMEM").HasMany(x => x.EMEDs).WithOne(x => x.EMEM).HasForeignKey(x => new { x.Co, x.Equipment }).HasPrincipalKey(x => new { x.EMCo, x.Equipment });
modelBuilder.Entity<EMCM>().ToTable("EMCM").HasMany(x => x.EMCDs).WithOne(x => x.EMCM)
.HasForeignKey(x => new { x.Co, x.Category }).HasPrincipalKey(x => new { x.EMCo, x.Category });
modelBuilder.Entity<udEMCD>().ToTable("udEMCD").HasOne(x => x.EMDC).WithMany(x => x.EMCDs)
.HasForeignKey(x => x.DocumentCategory).HasPrincipalKey(x => x.Category);
modelBuilder.Entity<udEMDC>().ToTable("udEMDC").HasMany(x => x.EMEDs).WithOne(x => x.EMDC).HasForeignKey(x => new{ x.DocumentCategory}).HasPrincipalKey(x => new{ x.Category});
modelBuilder.Entity<udEMED>().ToTable("udEMED");
modelBuilder.Entity<EMWH>().ToTable("EMWH");
modelBuilder.Entity<EMWI>().ToTable("EMWI");
modelBuilder.Entity<HQAT>().HasOne(x => x.EMWH).WithMany(x => x.HQATs).HasForeignKey(x => x.UniqueAttchID)
.HasPrincipalKey(x => x.UniqueAttchID);
modelBuilder.Entity<EMWH>().HasOne(x => x.EMEM).WithMany(x => x.EMWHs)
.HasForeignKey(x => new {x.EMCo, x.Equipment}).HasPrincipalKey(x => new {x.EMCo, x.Equipment});
EDIT: I added nullable KeyID's just to test prior to uploading and still didn't work.
I think the error is that you're declaring the Key as nullable, which it should never happen.
[Key]
public long? KeyID { get; set; }
change your code to this...
[Key]
public long KeyID { get; set; }

Entity Framework Core - Mapping many-to-many with AutoMapper

I'm unable to create correct mappings between DAO and DTO objects with many-to-many relations using AutoMapper.
Now I could map these many-to-many properties but the other ones were null.
What is the correct mapping? I see that models' attributes have the same name and type.
Also I'm trying to create the mapping between the parent models "ClaseFabricante" and "ClaseConstructor". Should I make it with the child ones which are many-to-many related "ReferenciaFabricante" and "ReferenciaConstructor" with ForMember()?
Entities:
[Table("ReferenciasFabricante", Schema = "public")]
public class ReferenciaFabricante
{
public string NombreFabricante { get; set; }
public string RefFabricanteIA { get; set; }
[InverseProperty("ReferenciaFabricante")]
public virtual ClaseFabricante Clase { get; set; }
[InverseProperty("ReferenciaFabricante")]
public virtual IList<ReferenciaFabricanteTieneReferenciaConstructor> MontaReferenciasConstructor { get; set; }
}
[Table("ReferenciasConstructor", Schema = "public")]
public class ReferenciaConstructor
{
public string Referencia { get; set; }
public string NombreConstructor { get; set; }
[InverseProperty("ReferenciaConstructor")]
public virtual ClaseConstructor Clase { get; set; }
[InverseProperty("ReferenciaConstructor")]
public virtual IList<ReferenciaFabricanteTieneReferenciaConstructor> MontadaPorReferenciasFabricante { get; set; }
}
[Table("ReferenciaFabricanteTieneReferenciaConstructor", Schema = "public")]
public class ReferenciaFabricanteTieneReferenciaConstructor {
[Key]
public int IdReferenciaFabricante { get; set; }
[ForeignKey("IdReferenciaFabricante")]
public ReferenciaFabricante ReferenciaFabricante { get; set; }
[Key]
public int IdReferenciaConstructor { get; set; }
[ForeignKey("IdReferenciaConstructor")]
public ReferenciaConstructor ReferenciaConstructor { get; set; }
}
[Table("ClasesFabricante", Schema = "public")]
public class ClaseFabricante
{
public int? IdReferenciaFabricante { get; set; }
[ForeignKey("IdReferenciaFabricante")]
public ReferenciaFabricante ReferenciaFabricante { get; set; }
}
[Table("ClasesConstructor", Schema = "public")]
public class ClaseConstructor
{
public int? IdReferenciaConstructor { get; set; }
[ForeignKey("IdReferenciaConstructor")]
public ReferenciaConstructor ReferenciaConstructor { get; set; }
}
ViewModels:
public class ReferenciaFabricante {
public string NombreFabricante { get; set; }
public string RefFabricanteIA { get; set; }
public virtual ClaseFabricante Clase { get; set; }
public virtual IList<ReferenciaConstructor> ReferenciasConstructor { get; set; }
}
public class ReferenciaConstructor {
public string Referencia { get; set; }
public string NombreConstructor { get; set; }
public virtual ClaseConstructor Clase { get; set; }
public virtual IList<ReferenciaFabricante> ReferenciasFabricante { get; set; }
}
public class ClaseFabricante {
public ReferenciaFabricante ReferenciaFabricante { get; set; }
}
public class ClaseConstructor {
public ReferenciaConstructor ReferenciaConstructor { get; set; }
}
Mapper initialization:
Mapper.Initialize((config) => {
config.CreateMap<ApplicationCore.Entities.ReferenciaFabricante, ApplicationCore.ViewModels.ReferenciaFabricante>()
;
config.CreateMap<ApplicationCore.Entities.ReferenciaConstructor, ApplicationCore.ViewModels.ReferenciaConstructor>()
;
config.CreateMap<ApplicationCore.Entities.ClaseFabricante, ApplicationCore.ViewModels.ClaseFabricante>()
.ForPath(dto => dto.ReferenciaFabricante.ReferenciasConstructor, opt => opt.MapFrom(x => x.ReferenciaFabricante.MontaReferenciasConstructor.Select(y => y.ReferenciaConstructor).ToList()))
.ForPath(dto => dto.ReferenciaFabricante.VersionesVehiculo, opt => opt.MapFrom(x => x.ReferenciaFabricante.MontaVersionesVehiculo.Select(y => y.VersionVehiculo).ToList()))
;
config.CreateMap<ApplicationCore.Entities.ClaseConstructor, ApplicationCore.ViewModels.ClaseConstructor>()
.ForPath(dto => dto.ReferenciaConstructor.ReferenciasFabricante, opt => opt.MapFrom(x => x.ReferenciaConstructor.MontadaPorReferenciasFabricante.Select(y => y.ReferenciaFabricante).ToList()))
;
});
Code:
foreach (ApplicationCore.Entities.ClaseFabricante cfDAO in vClasesFabricante) {
result.Add(AutoMapper.Mapper.Map<ApplicationCore.ViewModels.ClaseFabricante>(cfDAO));
}

Categories

Resources