Automapper - exclude some objects from mapped collection - c#

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

Related

How to configure a mapping of collection types with AutoMapper?

For example, I've implemented two classes like these:
public class A
{
public List<C> Items { get; set; }
}
public class B
{
public IImmutableList<C> Items { get; set; }
}
public class C
{
}
When I try to map A to B and vice versa, I get an exception because List<string> cannot be converted to IImmutable<string>.
Probably I could provide a mapping for A<->B, but since it'll be a very common pattern in my solution, I'd like to avoid to manually mapping each class that may fall into the same case.
Is there anyway I can generalize the whole mapping using generic type definitions from a collection type to another collection type?
This is what I want to avoid
mapperConfig.CreateMap<A, B>()
.ForMember(a => a.Items, opts => opts.Ignore())
.AfterMap
(
(source, target) =>
{
target.Items = source.Items.ToImmutableList();
}
);

Automapper List to CustomCollection

I am trying to map 2 Entities. One has a List of strings and other one is using a custom collection. Can anybody please advice how can I map these 2 entities using AutoMapper.
public class SourceItem
{
public string Name { get; set; }
public List<string> ShipsTo { get; set; }
}
public class DestItem
{
public string Name { get; set; }
public MyCollection ShipsTo { get; set; }
}
public class MyCollection : CollectionBase
{
private readonly List<string> _list;
public MyCollection()
{
_list = new List<string>();
}
public MyCollection(List<string> list)
{
_list = list;
}
public void Add(string item)
{
_list.Add(item);
}
}
Here is my mapping code...
Mapper.Initialize(cfg =>
{
cfg.CreateMap<SourceItem, DestItem>()
.ForMember(d => d.ShipsTo, o => o.ResolveUsing<CustomResolver>());
});
Here is how my custom resolver looks like...
public class CustomResolver : IValueResolver<SourceItem, DestItem, MyCollection>
{
public MyCollection Resolve(SourceItem source, DestItem destination, MyCollection destMember, ResolutionContext context)
{
return new MyCollection(source.ShipsTo);
}
}
When I try to run this code, I get an error saying
No coercion operator is defined between types
'System.Collections.Generic.List`1[System.Object]' and 'MyCollection'.
Any help would be really appreciated.
I don't see that you need a custom resolver, try this:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<SourceItem, DestItem>()
.ForMember(d => d.ShipsTo, o => o.MapFrom(src => new MyCollection(src.ShipsTo)));
});
The answer is: you cannot use custom value resolver here, because there is a bug (I have added item to backlog, you can track it). But you can specify conversion between List<string> and MyCollection as Jimmy suggested. And everything will work fine:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<List<string>, MyCollection>().ConvertUsing(s => new MyCollection(s));
cfg.CreateMap<SourceItem, DestItem>();
});
BTW Seems like issue already fixed, and it should be available in next release of Automapper.
I could not really explain it, but it works, if you define explicit mapping configuration from MyCollection to MyCollection:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<MyCollection, MyCollection>().ConvertUsing((a,b)=>a);
cfg.CreateMap<SourceItem, DestItem>()
.ForMember(d => d.ShipsTo, o => o.ResolveUsing<CustomResolver>());
});
I have debugged code of automapper to find why. Reason is, Automapper is looking for suitable mapper to map MyCollection to MyCollection. It looks in a collection of all mappers (defined in your config and predefined). Actually, what it should find is a AssignableMapper, that just assigns one variable to another, but first he checks an EnumerableMapper and it looks ok, because your CollectionBase is Enumerable. The exception comes from this EnumerableMapper, because it just couldn't map this two Collections. Solution is to explicit define, that it just should assign one variable to another.
But. I am agree with Sergey. This is some ugly bug, what i have suggested is exact the same ugly workaround. But i've managed get it to work :)

Automapper 5.1.1 inheritance mapping

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.

Map a dictionary's values to a list using AutoMapper

I have a Database Object which has a dictionary of properties which I need to map to a list object within a new object. My structure (simplified) looks like this:
public class DbObject { // The source
public Dictionary<string, DbCustomProperty> customProperties;
}
public class DbCustomProperty {
public string Key;
public string Value;
}
public class DTOObject { // The destination
public List<DTOCustomProperty> DTOCustomProperties;
}
public class DTOCustomProperty {
public string Key;
public string Value;
}
As you can see, I would like to map DbObject (the source) onto a DTOObject (the destination). The problem is that my DBObject contains a dictionary where the values are the property that I've like to map into a list.
eg. dBObject.CustomProperties.Values --> dtoObject.CustomProperties
Can this be acheieved through AutoMapper?
You can use:
AutoMapper.CreateMap<IGrouping<string, DbCustomProperty>, DTOCustomProperty>()
.ForMember(dest => dest.Key, opt => opt.MapFrom(src => src.Key))
.ForMember(dest => dest.Value, opt => opt.MapFrom(src => src.Value)));

Automapper and class hierarchy

Given the following sources:
public class SourceBase { public string TheString { get; set; } }
public class SourceDerived : SourceBase { }
and destinations:
public class DestBase { public string MyString { get; set; } }
public class DestDerived : DestBase { }
And this mapping:
CreateMap<SourceBase, DestBase>()
.ForMember(dest => dest.MyString, o => o.MapFrom(x => x.TheString))
.Include<SourceDerived, DestDerived>();
CreateMap<SourceDerived, DestDerived>();
Mapper.AssertConfigurationIsValid(); // Exception is thrown here
However, this gives a mapping error saying MyString isn't mapped on DestDerived. What gives? Do I really need to repeat the mappings for base class properties in all derived types (I do have more than one subclass in my actual code).
EDIT:
The exact exception is The following 1 properties on DestDerived could not be mapped: MyString. Add a custom mapping expression, ignore, or rename the property on DestDerived.
Please check this post:
http://groups.google.com/group/automapper-users/browse_thread/thread/69ba514a521e9599
It works fine if you declare it like in the code below (using AutoMapper 1.1.0.188). I am not sure if this solves your problem.
var result = Mapper.CreateMap<SourceBase, DestBase>()
.ForMember(dest => dest.MyString, o => o.MapFrom(x => x.TheString));
//.Include<SourceDerived, DestDerived>();
Mapper.CreateMap<SourceDerived, DestDerived>();
var source = new SourceDerived();
var destDerived = new DestDerived();
source.TheString = "teststring";
var mapResult = Mapper.Map<SourceBase, DestBase>(source, destDerived).MyString;
Console.WriteLine(mapResult);

Categories