Automapper with different type of nested objects - automapper

I have a destination class like below
public class Order
{
public string OrderId { get; set; }
public List<Delivery> Deliveries { get; set;}
}
public class Delivery
{
public string ProductName { get; set; }
}
I've to map ProductName in above class from below source class
public class OrderDTO
{
public string OrderId { get; set; }
public List<OrderDelivery> Deliveries { get; set; }
}
public class OrderDelivery
{
public List<OrderDeliveryDetails> ProductDeliveryDetails { get; set; }
}
public class OrderDeliveryDetails
{
public string ProductName { get; set; }
}
How I can do this using Automapper.
(Note: Please Don't confuse with the List<OrderDeliveryDetails> in OrderDeliveryclass. It is because, it may have child products as well, but i need to take parent ProductNameonly)

Well if by taking the parent ProductName you mean the first, you can try something along these lines (otherwise I might've misunderstood your question and you can ignore my answer):
public class AutoMapperConfiguration : Profile
{
private readonly IConfiguration _mapper;
public AutoMapperConfiguration(IConfiguration mapper)
{
_mapper = mapper;
}
protected override void Configure()
{
_mapper.CreateMap<OrderDTO, Order>
.ForMember(d => d.OrderId, o => o.MapFrom(s => s.OrderId))
.ForMember(d => d.Deliveries, o => o.MapFrom(s => s.Deliveries));
_mapper.CreateMap<OrderDelivery, Delivery>
.ForMember(d => d.ProductName, o => o.MapFrom(s => s.ProductDeliveryDetails.First().ProductName));
}
}
and then simply
_mapper.Map(orderDto);
(this is, provided you have all the wireup needed to use AutoMapper).

Related

Automapper 8 mapping not working properly

I have two model classes, when I try to map different properties of different name by using Automapper ForMember method. It throws an automapper configuration validation exception on the mapping of different property.
I have tried a lot but It does not help.I do not know why It is throwing an exception when I try to map Quantity property with Quntity property. but when I put same name of the property in both the model classes then it works
Below is located all the model classes, exception and configurations regarding automapper.
Could you please help me, that how to solve problem?
public class ProductModel
{
public ProductModel()
{
Id = GuidContext.Current.NewGuid();
ProductHistory = new HashSet<ProductHistoryModel>();
}
public Guid Id { get; set; }
public string ProductCode { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public bool IsActive { get; set; }
public decimal? Price { get; set; }
public int? Quntity { get; set; }
public Guid ProductCategoryId { get; set; }
public Guid? BrandId { get; set; }
public Guid ProductAttributeId { get; set; }
public virtual BrandModel Brand { get; set; }
public virtual ProductCategoryModel ProductCategory { get; set; }
public virtual ProductAttributeModel ProductAttribute { get; set; }
public virtual ICollection<ProductHistoryModel> ProductHistory { get; set; }
}
The another class is
public class ProductModel
{
public string Name { set; get; }
//public List<string> Attributes { set; get; }
//public string Brand { get; set; }
public decimal? Price
{
get; set;
}
public int? Quantity { get; set; }
}
}
and the mapping configuration is
public class ProductModelMapConfigurator : Profile, IMapConfigurator
{
public void Configure()
{
Mapper.Initialize(cfg =>
{
CreateMap<StandardizeInventory.Models.Product.ProductModel, Models.ProductModel>()
//.ForMember(dest => dest.Brand, opt => opt.MapFrom(src => src.Brand.Name))
.ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.Name))
.ForMember(dest => dest.Price, opt => opt.MapFrom(src => src.Price))
.ForMember(dest => dest.Quantity, opt => opt.MapFrom(src => src.Quntity));
//.AfterMap((src, dest) => {
// dest.Attributes = src.ProductAttribute.ProductAttributeValue.Select(x => x.Value).ToList();
//});
CreateMap<Models.ProductModel, StandardizeInventory.Models.Product.ProductModel>();
});
}
}
Below is the Exception Details
AutoMapper.AutoMapperConfigurationException:
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
For no matching constructor, add a no-arg ctor, add optional arguments, or map all of the constructor parameters
==========================================================================================
AutoMapper created this type map for you, but your types cannot be mapped using the current configuration.
ProductModel -> ProductModel (Destination member list)
StandardizeInventory.Models.Product.ProductModel -> InventoryStoreApi.Models.ProductModel (Destination member list)
Unmapped properties:
Quantity
at AutoMapper.ConfigurationValidator.AssertConfigurationIsValid(IEnumerable`1 typeMaps) in
Any help would be appreciated. Thanks
You're using a Profile wrong, see the documentation on Profiles
Your profile should look like:
public class ProductModelMapConfigurator : Profile, IMapConfigurator
{
public ProductModelMapConfigurator()
{
CreateMap<StandardizeInventory.Models.Product.ProductModel, Models.ProductModel>()
//.ForMember(dest => dest.Brand, opt => opt.MapFrom(src => src.Brand.Name))
.ForMember(dest => dest.Quantity, opt => opt.MapFrom(src => src.Quntity));
//.AfterMap((src, dest) => {
// dest.Attributes = src.ProductAttribute.ProductAttributeValue.Select(x => x.Value).ToList();
//});
CreateMap<Models.ProductModel, StandardizeInventory.Models.Product.ProductModel>();
}
}
Get rid of that Mapper.Initialize call from inside your Profile, and change the profile to use a constructor, not whatever that Configure method is. You also don't need MapFrom when the names match, that's the "Auto" of "AutoMapper".

How to use same complex type as a property of several output types but different source types

I have a class for CountryAndCity:
public class CountryAndCity
{
public Country Country { get; set; }
public City City { get; set; }
public ZipCode ZipCode { get; set; }
}
I am using this class in several output classes, for example:
public class OutputClassA
{
public CountryAndCity CountryAndCity { get; set; }
}
public class OutputClassB
{
public CountryAndCity CountryAndCity { get; set; }
}
In addition, i have some "Input Classes" which includes the same relevant fields for mapping with additional other fields:
public class InputClassA : ICountryAndCity
{
...Some other properties.....
public int? CountryId { get; set; }
public string CountryDesc { get; set; }
public int? CityId { get; set; }
public string CityDesc { get; set; }
...Some other properties.....
}
public class InputClassB : ICountryAndCity
{
...Some other properties.....
public int? CountryId { get; set; }
public string CountryDesc { get; set; }
public int? CityId { get; set; }
public string CityDesc { get; set; }
...Some other properties.....
}
i didn't want to duplicate the code for every CreateMap statement for CountryAndCity property mapping for every Input and output combination types so i decided to use an interface that all the "input types" implements.
i created this configuration and its working great if i implement ICountryAndCity on all the relevant "input classes" and using "Mapper.Map" for the interface inside the CreateMap function.
Mapper.Initialize(cfg =>
{
cfg.CreateMap<InputClassA, OutputClassA>()
.ForMember(dest => dest.CountryAndCity,
opts => opts.MapFrom(src => Mapper.Map<ICountryAndCity, CountryAndCity>((ICountryAndCity)src)));
cfg.CreateMap<InputClassB, OutputClassB>()
.ForMember(dest => dest.CountryAndCity,
opts => opts.MapFrom(src => Mapper.Map<ICountryAndCity, CountryAndCity>((ICountryAndCity)src)));
cfg.CreateMap<ICountryAndCity, CountryAndCity>()
.ForMember(dest => dest.Country,
opts => opts.MapFrom(
src => new Country
{
CountryId = src.CountryId,
CountryDesc = src.CountryDesc
}))
.ForMember(dest => dest.City,
opts => opts.MapFrom(
src => new City
{
CityId = src.CityId,
CityDesc = src.CityDesc
}))
.ForMember(dest => dest.ZipCode,
opts => opts.MapFrom(
src => new ZipCode
{
ZipCodeId = src.ZipCodeId,
ZipCodeDesc = src.ZipCodeDesc
}));
}
I am sure that there is other better way to do it using AutoMapper without using interface. Can someone help with that?

AutoMapper .ReverseMap() .Ignore() not working

Having an issue with version 6.1.1. In the below, the result of the reverse map still has the Company object populated. Per this post, which shows what I am doing below, except they are ignoring a property, and I'm ignoring a complex object.
What am I missing?
CreateMap<Item, ItemViewModel>(MemberList.Destination)
.ReverseMap()
.ForMember(x => x.Company, x => x.Ignore())
;
With AutoMapper 6.1 you could use ForPath instead ForMember to ignore complex objects.
See How to ignore property with ReverseMap for further information.
I see not what is wrong, but here is a running sample:
namespace AutomapperTest2
{
internal class Program
{
#region Methods
private static void Main(string[] args)
{
// Configure the mappings
Mapper.Initialize(cfg =>
{
cfg.CreateMap<ApplicantEducation, ApplicantEducationVM>();
cfg.CreateMap<Applicant, ApplicantVM>().ReverseMap()
.ForMember(x => x.Education, x => x.Ignore());
});
var config = new MapperConfiguration(cfg => cfg.CreateMissingTypeMaps = true);
var mapper = config.CreateMapper();
Applicant ap = new Applicant
{
Name = "its me",
Education =
new ApplicantEducation
{
SomeInt = 10,
SomeString = "sampleString"
}
};
// Map
ApplicantVM apVm = Mapper.Map<Applicant, ApplicantVM>(ap);
Applicant apBack = Mapper.Map<ApplicantVM, Applicant>(apVm);
}
#endregion
}
/// Your source classes
public class Applicant
{
public ApplicantEducation Education { get; set; }
public string Name { get; set; }
}
public class ApplicantEducation
{
public int SomeInt { get; set; }
public string SomeString { get; set; }
}
// Your VM classes
public class ApplicantVM
{
public string Description { get; set; }
public ApplicantEducationVM Education { get; set; }
public string Name { get; set; }
}
public class ApplicantEducationVM
{
public int SomeInt { get; set; }
public string SomeString { get; set; }
}
}
}

Automapper, Mapping one object member type to multiple concrete type

I have this Party class which contains an object data type coming from a service. It can contain two different member types for the Item property.
public class Party
{
public string DMVID {get; set;}
public object Item { get; set; }
}
and this DTO
public class PartyDTO
{
public string DMVID {get; set;}
public BusinessDTO BusinessItem { get; set; }
public IndividualDTO IndividualItem { get; set; }
}
How can I map the output of the Item to BusinessItem or IndividualItem.
I know this one would not work. Mapper.CreateMap<Party, PartyDTO>();
I don't know if conditional mapping can solve this or a resolver like this one.
Hey maybe this will help you out! I tested it, but i am using AutoMapper just for two days!
Allright here are your noted classes!!!
public class Party
{
public string DMVID { get; set; }
public object Item { get; set; }
}
public class PartyDTO
{
public string DMVID { get; set; }
public BuisnessDTO BusinessItem { get; set; }
public IndividualDTO IndividualItem { get; set; }
}
public class BuisnessDTO
{
public int Number
{
get;
set;
}
}
public class IndividualDTO
{
public string Message
{
get;
set;
}
}
and here your is the MapperConfiguration for this current scenario!
// Edit There was no need here for some conditions
AutoMapper.Mapper.CreateMap<Party, PartyDTO>()
.ForMember(dto => dto.BusinessItem, map =>
map.MapFrom(party => party.Item as BuisnessDTO);
)
.ForMember(dto => dto.IndividualItem, map =>
map.MapFrom(party => party.Item as IndividualDTO);
);
// And this is another way to achive the mapping in this scenario
AutoMapper.Mapper.CreateMap<PartyDTO, Party>()
.ForMember(party => party.Item, map => map.MapFrom( dto => (dto.BusinessItem != null) ? (dto.BusinessItem as object) : (dto.IndividualItem as object)));
And i created this sample for it!
Party firstParty = new Party()
{
DMVID = "something",
Item = new BuisnessDTO()
{
Number = 1
}
};
Party secondParty = new Party()
{
DMVID = "something",
Item = new IndividualDTO()
{
Message = "message"
}
};
PartyDTO dtoWithBuisness = AutoMapper.Mapper.Map<PartyDTO>(firstParty);
PartyDTO dtoWithIndividual = AutoMapper.Mapper.Map < PartyDTO>(secondParty);
Party afterParty = AutoMapper.Mapper.Map<Party>(dtoWithBuisness);
afterParty = AutoMapper.Mapper.Map < Party>(dtoWithIndividual);
Of course there are other possibility, but I think thats exactly what you wanted.

Nhibernate confused by class inheritance and returns mixed results

I have a class with a few properties and some methods
public class Content
{
public int Id { get; set; }
public string Application { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public override bool Equals(object obj) {...}
public override int GetHashCode() {...}
}
With this Fluent NHibernate mapping:
public class ContentMapping : ClassMap<Content>
{
public ContentMapping()
{
Table("vw_all_contents");
CompositeId()
.KeyProperty(x => x.Id, "id")
.KeyProperty(x => x.Application, "application");
Map(x => x.Property1, "property1");
Map(x => x.Property2, "property2");
}
}
Up to here everything works fine.
I now want to populate the same object but with a table a federated table that connects to another database.
So I have:
public class ContentOnProductionDatabase : Content { }
With a mapping:
public class ContenOnProductionDatabasetMapping : ClassMap<ContentOnProductionDatabase>
{
public ContentOnProductionDatabaseMapping()
{
Table("vw_federated_all_contents");
CompositeId()
.KeyProperty(x => x.Id, "id")
.KeyProperty(x => x.Application, "application");
Map(x => x.Property1, "property1");
Map(x => x.Property2, "property2");
}
}
And here is where NHibernate gets really confused and the queries return mixed results from both databases.
The problem goes away if my ContentOnProductionDatabase does not extend Content but instead is a duplicate class like this:
public class ContentOnProductionDatabaseMapping
{
public int Id { get; set; }
public string Application { get; set; }
public string Property1 { get; set; }
public string Property2 { get; set; }
public override bool Equals(object obj) {...}
public override int GetHashCode() {...}
}
So now everything is fine but I don't like the fact that there is so much code duplication and it seems to me there must be some sort of Mapping configuration out there to force NHibernate to ignore the inheritance and differentiate the two, especially since they map to different databases.
The repository framework is an inbuilt one handles the session and the queries.
public class ContentRepository : NHibernateRepositoryBase, IContentRepository
{
public ContentRepository(INHibernateContext context, ISettingsManager settingsManager): base(context){ }
public Content ReadContent(int id, string application)
{
using (ISessionContainer container = Context.GetSessionContainer())
{
return
container.AsQueryable<Content>()
.FirstOrDefault(c => c.Id == id && c.Application == application);
// All queries using <Content> return the correct results
}
}
public ContentOnProductionDataBase ReadFederatedContent(int id, string application)
{
using (ISessionContainer container = Context.GetSessionContainer())
{
return
container.AsQueryable<ContentOnProductionDataBase>()
.FirstOrDefault(c => c.Id == id && c.Application == application);
// All queries using <ContentOnProductionDataBase> return the combined results of <Content> and <ContentOnProductionDataBase>
}
}
}
Internally the container.AsQueryable works by invoking this:
public IQueryable<TEntity> AsQueryable<TEntity>() where TEntity : class
{
return LinqExtensionMethods.Query<TEntity>(this.Session);
}
Any ideas how to get rid of the code duplication?
To define the class mapping and the properties only once, you have to define a base class and define the mapping with UseUnionSubclassForInheritanceMapping which will allow you to use independent tables per entity which is derived from that base class.
You don't have to but you should declare your base class as abstract, because it will not have a database representation. So persisting the base class will fail! Meaning, you don't want anyone to use it as an entity, instead use your derived classes...
To do so, create one base, and 2 derived classes which should be stored in one table per class.
public abstract class ContentBase
{
public virtual int Id { get; set; }
public virtual string Application { get; set; }
public virtual string Property1 { get; set; }
public virtual string Property2 { get; set; }
public override bool Equals(object obj)
{
[..]
}
public override int GetHashCode()
{
[..]
}
}
public class Content : ContentBase
{
}
public class ContentOnProductionDatabaset : ContentBase
{
}
The mapping of the base class must call UseUnionSubclassForInheritanceMapping, otherwise nHibernate would combine the classes.
public class ContentBaseMapping : ClassMap<ContentBase>
{
public ContentBaseMapping()
{
UseUnionSubclassForInheritanceMapping();
CompositeId()
.KeyProperty(x => x.Id, "id")
.KeyProperty(x => x.Application, "application");
Map(x => x.Property1, "property1");
Map(x => x.Property2, "property2");
}
}
The subclass mappings just have to define that the base is abstract.
Here you can also define each table name the entity should use.
public class ContentMapping : SubclassMap<Content>
{
public ContentMapping()
{
Table("vw_all_contents");
Abstract();
}
}
public class ContentOnProductionDatabaseMapping : SubclassMap<ContentOnProductionDatabaset>
{
public ContentOnProductionDatabaseMapping()
{
Table("vw_federated_all_contents");
Abstract();
}
}

Resources