Automapper 5.1.1 inheritance mapping - c#

Hello I cant get my mappings working for inherited class.
Idea is to create map for base object and interface only once, and when child classes implements their own members, configure mapping nly for those members that are not defined in base class or intrface.
Let me begin with sample code.
public class DtoClass {
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
}
public interface IField3 {
public string EntityField3 { get; set; }
}
public class BaseEntityClass {
public string EntityField1 { get; set; }
}
public class ChildEntityClass : BaseEntityClass, IField3 {
public string EntityField2 { get; set; }
public string EntityField3 { get; set; }
}
CreateMap<BaseEntityClass, DtoClass>()
.ForMember(c => c.Field1 , m => m.MapFrom(a => a.EntityField1))
.Include<ChildEntityClass, DtoClass>();
CreateMap<IField3, DtoClass>()
.ForMember(c => c.Field3 , m => m.MapFrom(a => a.EntityField3));
CreateMap<ChildEntityClass, DtoClass>()
.ForMember(c => c.Field2 , m => m.MapFrom(a => a.EntityField2));
Attached code dosnt work ofcourse. when calling :
AutoMapper.Mapper.Map<ChildEntityClass, DtoClass>(instanceOfChildEntityClass);
I get only mapped members that are defined in CreateMap<ChildEntityClass, DtoClass>().
Any idea how to implement mappings for base class and interfaces only once?
And yes i want to map all types ChildEntityClass, BaseEntityClass and IField3 to DtoClass.
Any hints are welcome for elegant configuration such mappings.
Edit: I remove unecssary IncludeBase from subclass for clarity, but none of both
- IncludeBase in subclass
- Include in base class
Works for me. What can cause such problems ?

You should not be using .IncludeBase AND .Include - pick one and stick with it. I prefer .IncludeBase, as I think it makes more sense to define in the subclass. In your case, you cannot reference IField3 using Include because there is no implicit conversion.
The following code works using IncludeBase for me
CreateMap<BaseEntityClass, DtoClass>()
.ForMember(dest => dest.Field1, opt => opt.MapFrom(src => src.EntityField1))
;
CreateMap<IField3, DtoClass>()
.ForMember(dest => dest.Field3, opt => opt.MapFrom(src => src.EntityField3))
;
CreateMap<ChildEntityClass, DtoClass>()
.ForMember(dest => dest.Field2, opt => opt.MapFrom(src => src.EntityField2))
.IncludeBase<BaseEntityClass, DtoClass>()
.IncludeBase<IField3, DtoClass>()
;

As it often happens issue were outside the scope i delivered in sample code.
In my project in initialization method was hidden invocation of something like:
foreach (string propName in map.GetUnmappedPropertyNames())
{
expr.ForMember(propName, opt => opt.Ignore());
}
So all columns not mapped in subclass were automatically ignored even when invoking mapping for base type. Simple yet problematic.
Such code as above were probably aded for Mapper.Configuration.AssertConfigurationIsValid(); to pass.

Related

Automapper 2 source fields mapped to 1 destination field

I have 2 classes to be mapped:
class1 has fields PaymentState and PaymentStateId
public int PaymentStateId { get; set; }
[ForeignKey(nameof(PaymentStateId))]
[InverseProperty(nameof(PaymentStateEntity.OrderEntities))]
public virtual PaymentStateEntity PaymentState { get; set; }
class2 has field with the same name PaymentState but of enum type
public PaymentState PaymentState { get; set; }
while mapping class1 to class2 there is an error that field PaymentState cannot be mapped:
Unable to create a map expression from
class1.PaymentState (Entities.PaymentStateEntity) to PaymentState.PaymentState (Enums.PaymentState)
Mapping types:
class1-> class2
Destination Member:
PaymentState
have tried custom mapping fields, but I guess that the fact that there are 2 fields now which are to be mapped to 1 destication field makes the problem
CreateMap<class1, class2>()
.ForMember(dest => dest.PaymentState, opt => opt.MapFrom(src => src.PaymentStateId))
What is the way to ignore one source field though let another source field to be mapped to destination field?
I don't think the error cause is 2 fields mapped into 1 destination. Probably because of the difference of type between dest and source. You should check log what the error is.
But you can try to use
CreateMap<class1, class2>()
.ForMember(dest => dest.PaymentState, opt => opt.Ignore())
.ForMember(dest => dest.PaymentState, opt => opt.MapFrom(src => src.PaymentStateId))
Besides, you can convert 2 enums by setting their items to have the same value
.ForMember(dest => dest.PaymentState, opt => opt.MapFrom(src => (PaymentState)((int)src.PaymentState)))

AutoMapper - skip whole object from child collection

I have a parent class that has a list of children objects. Child has a bool property that defines if it should be in the Parent list after mapping. Parent has the same property but it's not the one that's relevant in this case:
class Parent
{
public List<Child> Children { get; set; }
public bool WillMap { get; set; }
// more stuff
}
class Child
{
public bool WillMap { get; set; }
// more things
}
I was wondering if a mapping can be written that will end up with a Parent with a collection of Child objects that have WillMap == true?
I know about conditional mapping and that we can do something like
CreateMap<Parent, Parent>()
.ForMember(d => d.Children, opt => opt.Condition(s => s.WillMap == true));
but in this case it's the Parent's WillMap property that's being targeted.
Thanks.
.ForMember(dest => dest.Children, opt => opt.MapFrom(source => source.Children.Where(child => child.WillMap));
You can perform filtering inside MapFrom
.ForMember(d => d.Children, opt => opt.MapFrom((s, d, obje, conext) => s.WillMap && s.Children != null ? conext.Mapper.Map<Child>(s.Children.Where(x => x.WillMap).ToList()) : null));
Or create a custom converter with filtering inside:
public class ParentConverter : ITypeConverter<Parent, Parent>
{
public Parent Convert(ResolutionContext context)
{
// implement conversion logic
}
}
http://docs.automapper.org/en/stable/Custom-type-converters.html

How to map an ICollection<T> to Class derived from List<T>

I am setting up a mapping between my models and my view models and I'm trying to map from an ICollection to class that derives from List
I have tried to make a mapping between my ListItemClassVM and ICollection but get an error 'Argument types do not match'
Option one mapping works with this:
public class ParentVM
{
public List<ListItemClass> ListItemClasses { get; set; }
}
Option two mapping not working:
public class ParentVM
{
public ListItemClassVM ListItemClasses { get; set; }
}
public ListItemClassVM : List<ListItemClass>
{
}
Mapping Setup:
public ModelClass_ParentVM_Profile()
{
CreateMap<ModelClass, ParentVM>()
.ForMember(d => d.ListItemClasses, o => o.MapFrom(i => i.ModelCollection))
;
CreateMap<ParentVM, ModelClass>()
;
}
trying to setup the mapping so option two will map.
I think that there are more way to reach the solution, but you can't escape from a manual transposition from ICollection< ListItemClass > to ListItemClassVM.
The simplier way maybe is to add to your ListItemClassVM a constructor that accepts an ICollection< ListItemClass > and initialize itself with the elements in ICollection, then you could do something like:
CreateMap<ModelClass, ParentVM>()
.ForMember(d => d.ListItemClasses, o => o.MapFrom(i =>new ListItemClassVM (i.ModelCollection)))
;

Automapper Map from outer class to inner class variable

I'm using Automapper and need to know whether I can map a source variable to a nested member variable?
This is the source I want to map from
public class source
{
public string name;
}
This is the destination - I need the name variable assigned to the Nested.Name member
public class Destination
{
public Nested info;
}
public class Nested
{
public string name;
}
Any help greatly appreciated.
Ron.
ForPath would do the trick
CreateMap<source, Destination>()
.ForPath(d => d.info.name, c => c.MapFrom(src => src.name));
Something like this should do the trick, although you might run into an uninitialized dst.info
CreateMap<source, Destination>()
//reegular mapping here
.ForMember(dst => dst.foo, c => c.MapFrom(src => src.otherfoo))
//AfterMap to bind your properties
.AfterMap((src, dst) => { dst.info.name = src.name; });

Automapper - exclude some objects from mapped collection

I have the following map rules:
CreateMap<ViewModels.ApplicationDriverAccidentFormVM, ApplicationDriverAccidentDomain>();
then I want to map ViewModels.ApplicationDriverFormVM to ApplicationDriverDomain, both are have Accidents property, which are appropriate collections for each type.
public class ApplicationDriverDomain
{
public List<ApplicationDriverAccidentDomain> Accidents { get; set; }
}
public class ApplicationDriverFormVM
{
public List<ApplicationDriverAccidentFormVM> Accidents { get; set; }
}
And I want to exclude (not map) all records, which are not satisfied some conditions
I try to write the following code:
CreateMap<ViewModels.ApplicationDriverFormVM, ApplicationDriverDomain>()
.ForMember(dest => dest.Accidents, opt => opt.MapFrom(src => GetNotNullFromCollection(src.Accidents)))
where GetNotNullFromCollection is:
List<object> GetNotNullFromCollection(object input)
{
List<object> output = new List<object>();
foreach (var item in (List<object>)input)
{
if (!Utils.IsAllNull(item))
output.Add(item);
}
return output;
}
but it says me:
Unable to cast object of type
'System.Collections.Generic.List1[Web.ViewModels.ApplicationDriverAccidentFormVM]'
to type 'System.Collections.Generic.List1[System.Object]'.
Why and how to do it?
your method GetNotNullFromCollection receives an object but you are passing it a list.
Anyway, I would recommend using Generics instead of objects.
I solved it by the following way:
CreateMap<ViewModels.ApplicationDriverFormVM, ApplicationDriverDomain>().ForMember(dest => dest.Accidents, opt => opt.MapFrom(src => src.Accidents.Where(o => !Utils.IsAllNull(o))))

Categories