I have this structure:
public class UserEntity
{
public long Id { get; set; }
public string Name { get; set; }
public string UserName { get; set; }
public ComplexObject TimeZone { get; set; }
}
public class UserQueryResult
{
public UserEntity User { get; set; }
public ObjectId AccountId { get; set; }
}
public class UserDto
{
public long Id { get; set; }
public string Name { get; set; }
public string UserName { get; set; }
public string TimeZoneId { get; set; }
public string AccountId { get; set; }
}
I have UserEntity to UserDto mapping like this:
CreateMap<UserEntity, UserDto>()
.ForMember(dest => dest.TimeZoneId, opt => opt.ResolveUsing<UserTimeZoneResolver>())
.ForMember(dest => dest.AccountId, opt => opt.Ignore());
As you can see, there's a resolver from UserEntity to UserDto. Now, if I want to map UserQueryResult to UserDto, how could I reuse the above mapper without the need to map all properties manually?
CreateMap<UserQueryResult, UserDto>()
.ForMember(dest => dest.AccountId, opt => opt.MapFrom(src => src.AccountId.ToString()))
.// use the UserEntity to UserDto map?
Related
Class Customer
public class Customer
{
public int Id { get; set; }
public int PersonId { get; set; }
public int DiscountValue { get; set; }
public Person Person { get; set; }
public ICollection<Receipt> Receipts { get; set; }
}
Class Person
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public DateTime BirthDate { get; set; }
}
Class CustomerModel
public class CustomerModel
{
public int Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public DateTime BirthDate { get; set; }
public int DiscountValue { get; set; }
public ICollection<int> ReceiptsIds { get; set; }
}
I need to create mapping that combines Customer and Person into CustomerModel.
public class AutomapperProfile : Profile
{
public AutomapperProfile()
{
CreateMap<(Customer, Person), CustomerModel>();
}
}
How can I combine Customer and Person?
You can just map from Customer to CustomerModel as:
Solution 1: Specify the property mapping from source to destination via .ForMember().
CreateMap<Customer, CustomerModel>()
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Person.Name))
.ForMember(dest => dest.Surname, opt => opt.MapFrom(src => src.Person.Surname))
.ForMember(dest => dest.BirthDate, opt => opt.MapFrom(src => src.Person.BirthDate));
Solution 2: Flattening Person model via .IncludeMembers().
cfg.CreateMap<Customer, CustomerModel>()
.IncludeMembers(src => src.Person);
cfg.CreateMap<Person, CustomerModel>();
Demo Solution 1 & 2 # .NET Fiddle
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>();
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 ));
From the nested Entity list we want to select an object and map it in the Dto object, but when we use the Automapper to filter for object which deleteDate is equal null and map it, for each column Automapper filter again on db. how to filter one time and map selected to Dto object
We need to store all the information in our database so use DeleteDate column with default value null. so we use one to many relation to store history of our data.
public class User
{
public Guid UserId { get; set; }
public string MobileNumber { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public IList<CustomerDetail> CustomerDetails { get; set; }
}
public class CustomerDetail
{
public Guid CustomerDetailId { get; set; }
public float MaximumAmountPerTransaction { get; set; }
public float MaximumAmountPerDay { get; set; }
public bool IsConfirm { get; set; }
public Guid UserId { get; set; }
public virtual DateTimeOffset CreateDate { get; set; }
public virtual DateTimeOffset? DeleteDate { get; set; }
}
public class CustomerResponse
{
[Key]
public Guid CustomerId{ get; set; }
public string MobileNumber{ get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool IsConfirm { get; set; }
public float? MaximumAmountPerTransaction { get; set; }
public float? MaximumAmountPerDay { get; set; }
}
public class CustomerDomainProfile : Profile
{
public UserDomainProfile()
{
CreateMap<User, CustomerResponse>()
.ForMember(dest => dest.CustomerId,
opt => opt.MapFrom(src => src.UserId))
.ForMember(dest => dest.Country,
opt => opt.MapFrom(src => src.Country.CommonName))
.ForMember(dest => dest.IsConfirm,
opt => opt.MapFrom(src => src.CustomerDetails.FirstOrDefault(cd => cd.DeleteDate == null).IsConfirm))
.ForMember(dest => dest.MaximumAmountPerTransaction,
opt => opt.MapFrom(src => src.CustomerDetails.FirstOrDefault(cd => cd.DeleteDate == null).MaximumAmountPerTransaction))
.ForMember(dest => dest.MaximumAmountPerDay,
opt => opt.MapFrom(src => src.CustomerDetails.FirstOrDefault(cd => cd.DeleteDate == null).MaximumAmountPerDay))
}
}
I expect a query to the database and select the CustomerDetails with DeleteDate is null and map it, but the type of our define, for each column trying to query to database because
I expect that querying the database and selecting CustomerDetails with DeleteDate is null and its map, but our definition, for each column, is trying to query the database because it can not recognize that they are from a same table. How can we identify them that have fallen from a row to reduce the query? is any other solution or idea?
I have a simple class called Supplier which has contacts and contact can have addresses. right now we are using only 1 contact and 1 address (line1 to line4) in that contact. In future we might use multiple contacts having multiple addresses. below is my class
public class SupplierDto
{
public string Name { get; set; }
public string Alias { get; set; }
public int? SupplierTypeId { get; set; }
public int? WebclicsManufacturerId { get; set; }
public string SAPCode { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
public string Line4 { get; set; }
public int CountryId { get; set; }
public string PostalCode { get; set; }
public string ContactName { get; set; }
public string ContactEmail { get; set; }
public string ContactTelephone { get; set; }
public string ContactJobTitle { get; set; }
}
My Supplier class is a bit complex, so I am trying to map from Supplier to DTO and below is my mapping.
CreateMap<Supplier, SupplierDto>()
.ForMember(dest => dest.Line1, options => options.MapFrom(source => source.SupplierContacts.First().Contact.EntityAddresses.First().Address.Line1))
.ForMember(dest => dest.Line2, options => options.MapFrom(source => source.SupplierContacts.First().Contact.EntityAddresses.First().Address.Line2))
.ForMember(dest => dest.Line3, options => options.MapFrom(source => source.SupplierContacts.First().Contact.EntityAddresses.First().Address.Line3))
.ForMember(dest => dest.Line4, options => options.MapFrom(source => source.SupplierContacts.First().Contact.EntityAddresses.First().Address.Line4))
.ForMember(dest => dest.CountryId, options => options.MapFrom(source => source.SupplierContacts.First().Contact.EntityAddresses.First().Address.CountryId))
.ForMember(dest => dest.PostalCode, options => options.MapFrom(source => source.SupplierContacts.First().Contact.EntityAddresses.First().Address.PostalCode))
.ForMember(dest => dest.ContactName, options => options.MapFrom(source => source.SupplierContacts.First().Contact.Name))
.ForMember(dest => dest.ContactEmail, options => options.MapFrom(source => source.SupplierContacts.First().Contact.Email))
.ForMember(dest => dest.ContactTelephone, options => options.MapFrom(source => source.SupplierContacts.First().Contact.Telephone))
.ForMember(dest => dest.ContactJobTitle, options => options.MapFrom(source => source.SupplierContacts.First().Contact.JobTitle));
As you can see I have custom logic for each column. Now problem is if there is no contact/address, code breaks because I am using
First()
from LINQ, is there a better way to check if contacts exists then do mapping and if it has addresses then proceed with address mapping?
Just create an ContactDto and add it to the SupplierDto, then move all contact relevant properties to the ContactDto.
public class ContactDto
{
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
public string Line4 { get; set; }
public int CountryId { get; set; }
public string PostalCode { get; set; }
public string ContactName { get; set; }
public string ContactEmail { get; set; }
public string ContactTelephone { get; set; }
public string ContactJobTitle { get; set; }
}
public class SupplierDto
{
public string Name { get; set; }
public string Alias { get; set; }
public int? SupplierTypeId { get; set; }
public int? WebclicsManufacturerId { get; set; }
public string SAPCode { get; set; }
public ContactDto Contact { get; set; }
}
Then the mapping should look something like this
CreateMap<Supplier, SupplierDto>()
.ForMember(dest => dest.Contact, options => options.MapFrom(source => source.SupplierContacts.FirstOrDefault()));
If Contact is null then AutoMapper didn't try to map it. If you want in the Future support multiple Contacts on the Supplier, then just change the ContactDto Contact Property to a List<ContactDto> and remove the .FirstOrDefault() in the Mapping.