Automapper Map related sources into one destination and flatten them - c#

I have following database objects:
public class LinkedContact
{
public int LinkedContactID { get; set; }
public Nullable<int> ContactID { get; set; }
public Nullable<int> ContactTypeID { get; set; }
public Nullable<bool> Deleted { get; set; }
public virtual Contact Contact { get; set; }
public virtual ContactType ContactType { get; set; }
}
public class Contact
{
public Contact()
{
this.LinkedContact = new HashSet<LinkedContact>();
}
public int ContactID { get; set; }
public string DisplayName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Nullable<bool> Deleted { get; set; }
public virtual ICollection<LinkedContact> LinkedContact { get; set; }
}
public class ContactType
{
public ContactType()
{
this.LinkedContact = new HashSet<LinkedContact>();
}
public int ContactTypeID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ICollection<LinkedContact> LinkedContact { get; set; }
}
I want to map it into following DTO
public class ContactDTO
{
public int ContactID { get; set; }
public string DisplayName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ContactTypeDTO ContactType { get; set; }
}
public class ContactTypeDTO
{
public int ContactTypeID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
So basically, the Contact Type from Linked Contact should map to Contact DTO. End user will only see Contact View Model and is not aware of Linked Contact and hence the structure. Can anyone help me with the mapping configuration. I have tried the following with no luck
CreateMap<Contact, ContactDTO>().ReverseMap();
CreateMap<LinkedContact, ContactDTO>()
CreateMap<ContactType, ContactTypeDTO>().ReverseMap();

look through this link , there is a good sample
CreateMap<LinkedContact, ContactDTO>()
.ForMember(dest => dest.DisplayName, opts => opts.MapFrom(src => src.Contact.DisplayName))
.ForMember(dest => dest.FirstName, opts => opts.MapFrom(src => src.Contact.FirstName))
.ForMember(dest => dest.LastName, opts => opts.MapFrom(src => src.Contact.LastName))
another option:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<Contact, ContactDTO>().ReverseMap();
cfg.CreateMap<ContactType, ContactTypeDTO>().ReverseMap();
cfg.CreateMap<LinkedContact, ContactDTO>().ConvertUsing(src =>
{
var contact = Mapper.Map<Contact, ContactDTO>(src.Contact);
contact.ContactType = Mapper.Map<ContactType, ContactTypeDTO>(src.ContactType);
return contact;
});
});

Finally I used the below configuration which mapped both Contact and Contact Type. Leaving it here for people looking for answer to the same thing:
CreateMap<LinkedContact, ContactDTO>().ConvertUsing((src, dest, context) =>
{
dest = context.Mapper.Map<ContactDTO>(src.Contact);
dest.ContactType = context.Mapper.Map<ContactTypeDTO>(src.ContactType);
return dest;
});
CreateMap<Contact, ContactDTO>().ReverseMap();
CreateMap<ContactType, ContactTypeDTO>().ReverseMap();

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)));
}

Include in EF C#

I have three tables:
Member, business and business address
I'm able to fetch the business with member using Include, but I'm trying to get businessAddress, it is asking for direct relationship i guess.
Here is my models:
[Table("member")]
public partial class Member
{
public Member()
{
Business = new HashSet<Business>();
// BusinessAddress = new HashSet<BusinessAddress>();
}
[Key]
public int memberId { get; set; }
public int chapterid { get; set; }
public string title { get; set; }
public string firstname { get; set; }
public string lastname { get; set; }
public bool IsActive { get; set; }
public string classification { get; set; }
public int SortOrder { get; set; }
public virtual ICollection<Business> Business { get; set; }
// public virtual ICollection<BusinessAddress> BusinessAddress { get; set; }
}
Business
[Table("Business")]
public partial class Business
{
public Business()
{
BusinessAddress = new HashSet<BusinessAddress>();
}
[Key]
public int BusinessID { get; set; }
public int? categoryid { get; set; }
public int? subcategoryid { get; set; }
public int memberid { get; set; }
public string businessname { get; set; }
public string dealingin { get; set; }
public int? sortOrder { get; set; }
[ForeignKey("memberid")]
public Member Member { get; set; }
public ICollection<BusinessAddress> BusinessAddress { get; set; }
}
BusinessAddress
[Table("BusinessAddress")]
public partial class BusinessAddress
{
[Key]
public int businessaddressid { get; set; }
public int businessid { get; set; }
[ForeignKey("businessid")]
public virtual Business Business { get; set; }
public string address { get; set; }
}
Below is my EF query:
var list = _dbContext.Member.Include("Business").OrderByDescending(x => x.SortOrder).ThenBy(x => x.firstname).ToList();
I want to get Member with business and BusinessAddresses. I have tried Include("BusinesADdress") that didn't work, Help me please.
Try using Include accepting lambda selector followed by ThenInclude:
var list = _dbContext.Member
.Include(m => m.Business)
.ThenInclude(b => b.BusinessAddress)
.OrderByDescending(x => x.SortOrder)
.ThenBy(x => x.firstname)
.ToList();
Also you can just append all needed relations via . in the Include call but in general I would recommend against it cause the first approach is type safe:
var list = _dbContext.Member
.Include("Business.BusinessAddress")
.OrderByDescending(x => x.SortOrder)
.ThenBy(x => x.firstname)
.ToList();

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?

AutoMapper - How to Map to three level deep

I'm trying to user AutoMapper to flatten a Entity with relation to another Entity which has relation to third Entity to view model
How to map these three entities into one?
Source:
public class Address
{
public int AddressId { get; set; }
public string AddressLine1 { get; set; }
public int CityId { get; set; }
public virtual City City { get; set; }
}
public class City
{
public int CityId { get; set; }
public string CityName { get; set; }
public int CountryId { get; set; }
public virtual Country Country { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
public class Country
{
public int CountryId { get; set; }
public string CountryName { get; set; }
public virtual ICollection<City> Cities { get; set; }
}
Destination:
Public Class AddressViewModel
{
public int AddressId { get; set; }
public string AddressLine1 { get; set; }
public int CityId { get; set; }
public string CityName { get; set; }
public int CountryId { get; set; }
public string CountryName { get; set}
}
A couple of ways (at least). If you name your viewmodel fields differently it can happen by convention:
Public Class AddressViewModel
{
public int AddressId { get; set; }
public string AddressLine1 { get; set; }
public int CityCityId { get; set; }
[DisplayName("City Name")]
public string CityCityName { get; set; }
public int CityCountryCountryId { get; set; }
[DisplayName("Country Name")]
public string CityCountryCountryName { get; set}
}
If that's too ugly, you can do it in CreateMap:
Mapper.CreateMap<Address, AddressViewModel>()
.ForMember(dest => dest.CityId, opts => opts.MapFrom(src => src.City.CityId))
.ForMember(dest => dest.CityName, opts => opts.MapFrom(src => src.City.CityName))
.ForMember(dest => dest.CountryId, opts => opts.MapFrom(src => src.City.Country.CountryId))
.ForMember(dest => dest.CountryName, opts => opts.MapFrom(src => src.City.Country.CountryName));
http://automapper.codeplex.com/wikipage?title=Flattening&referringTitle=Home
Example to use Include() to query data in deeper levels:
public AddressViewModel GetAddressById(int id)
{
var result = applicationDbContext.Address
.Include(o=>o.City)
.Include(o=>o.City.Country)
.FirstOrDefault(x=>x.AddressId == id);
return mapper.Map<AddressViewModel>(result);
}

Automapper many to many mapping

Patrick, thanks for advice about correct question!
EDIT 1:
I have three table for many to many relationship. Like this:
GoodEntity:
public partial class GoodEntity
{
public GoodEntity()
{
this.GoodsAndProviders = new HashSet<GoodAndProviderEntity>();
}
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public decimal cost { get; set; }
public Nullable<decimal> price { get; set; }
public virtual ICollection<GoodAndProviderEntity> GoodsAndProviders { get; set; }
}
ProviderEntity:
public partial class ProviderEntity
{
public ProviderEntity()
{
this.GoodsAndProviders = new HashSet<GoodAndProviderEntity>();
}
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public string address { get; set; }
public string phone { get; set; }
public string email { get; set; }
public string url { get; set; }
public Nullable<int> rating { get; set; }
public virtual ICollection<GoodAndProviderEntity> GoodsAndProviders { get; set; }
}
Entity for many-to-many relationship:
public partial class GoodAndProviderEntity
{
public int id { get; set; }
public int good_id { get; set; }
public int provider_id { get; set; }
public virtual GoodEntity Goods { get; set; }
public virtual ProviderEntity Providers { get; set; }
}
GoodDTO:
public class GoodDTO
{
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public decimal cost { get; set; }
public decimal? price { get; set; }
public IList<ProviderDTO> providers { get; set; }
}
ProviderDTO:
public class ProviderDTO
{
public int id { get; set; }
public string name { get; set; }
public string description { get; set; }
public string address { get; set; }
public string phone { get; set; }
public string email { get; set; }
public string url { get; set; }
public int? rating { get; set; }
}
This is code for creation maps:
Mapper.CreateMap<ProviderDTO, ProviderEntity>();
Mapper.CreateMap<ProviderEntity, ProviderDTO>();
Mapper.CreateMap<GoodEntity, GoodDTO>()
.ForMember(dto => dto.providers, opt => opt.MapFrom(x => x.GoodsAndProviders));
Mapper.CreateMap<GoodAndProviderEntity, ProviderDTO>();
And it works half. Automapper was mapped "goods" completely and was created list for all providers for this goods. But automapper don`t fill providers.
If I use Mapper.AssertConfigurationIsValid(), then:
Unmapped members were found. Review the types and members below. Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type ======================================================= ProviderDTO -> ProviderEntity (Destination member list) Core.DTO.ProviderDTO -> DAL.EF.Entities.ProviderEntity (Destination member list) Unmapped properties: GoodsAndProviders ============================================================== GoodAndProviderEntity -> ProviderDTO (Destination member list) DAL.EF.Entities.GoodAndProviderEntity -> Core.DTO.ProviderDTO (Destination member list)
How to create mapping for many-to-many relationship?
Regards, Anton
With your current code you're trying to map the GoodAndProviderEntity into ProviderDTO.
Mapper.CreateMap<GoodEntity, GoodDTO>()
.ForMember(dto => dto.providers, opt => opt.MapFrom(x => x.GoodsAndProviders));
What you want to do, is to map ProviderEntity into ProviderDTO, so all you have to do is select the Providers from GoodsAndProviders as a list:
Mapper.CreateMap<GoodEntity, GoodDTO>()
.ForMember(dto => dto.providers, opt => opt.MapFrom(x => x.GoodsAndProviders.Select(y => y.Providers).ToList()));

Categories

Resources