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
Related
I am using automapper 9.0.0.
My situation is as follows. I have a list of items which are all instances of an abstract base class, let's call it BaseClass. There are 2 classes that inherit that class, let's call those Bar1Class and Bar2Class.
I want to map a list of BaseClass to an object that contains 2 lists. One list is a DTO for the Bar1Class objects from the list, and the 2nd one is for the Bar2Class objects from the list:
List<BaseClass> items = GetItems();
var dto = Mapper.Map<FooResponseModel>(items);
The hierarchy is as follows:
// Response models
public class FooResponseModel
{
public IEnumerable<Bar1Model> Bar1Models {get;set;}
public IEnumerable<Bar2Model> Bar2Models {get;set;}
}
public class Bar1Model
{
public string MyString {get;set;}
public int MyInt {get;set;}
}
public class Bar2Model
{
public string MyString {get;set;}
public bool MyBool {get;set;}
}
public abstract class BaseClass
{
public string MyString {get;set;}
}
public class Bar1Class : BaseClass
{
public int MyInt {get;set;}
}
public class Bar2Class : BaseClass
{
public bool MyBool {get;set;}
}
How would I set this up?
Using the CreateMap<BaseClass, FooResponseModel>() doesn't really work because I can't divide the collections. Doing something like CreateMap<Bar1Class, Bar1Model>() would allow me to map the classes itself, but not allow me to set up the lists.
Thanks!
Edit:
I would map it by hand like this now, because i dont know how to map the upper object correctly. I would of course add CreateMap<Bar1Class, Bar1Model>() and such beforehand.
var dto = new FooResponseModel
{
Bar1Models = items
.Where(x => x is Bar1Class)
.Cast<Bar1Class>()
.Select(x => Mapper.Map<Bar1Model>()),
Bar2Models = items.
.Where(x => x is Bar2Class)
.Cast<Bar2Class>()
.Select(x => Mapper.Map<Bar2Model>())
}
Create two maps between source classes and destination classes. Then, add mapping from a sequence of BaseClass to FooResponseModel and point AutoMapper how to populate Bar1Models and Bar2Models properties.
CreateMap<Bar1Class, Bar1Model>();
CreateMap<Bar2Class, Bar2Model>();
CreateMap<IEnumerable<BaseClass>, FooResponseModel>()
.ForMember(d => d.Bar1Models, o => o.MapFrom(s => s.Where(b => b is Bar1Class)))
.ForMember(d => d.Bar2Models, o => o.MapFrom(s => s.Where(b => b is Bar2Class)));
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))))
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.
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)));
With AutoMapper, can I override the resolved type for a property? For example, given these classes:
public class Parent
{
public Child Value { get; set; }
}
public class Child { ... }
public class DerivedChild : Child { ... }
Can I configure AutoMapper to automap the Child Value property with a DerivedChild instance? The hypothetical mapping would look something like this:
map.CreateMap<ChildEntity, DerivedChild>();
map.CreateMap<ParentEntity, Parent>()
.ForMember(p => p.Value, p => p.UseDestinationType<DerivedChild>());
(I'm projecting from LINQ entities. The closest I could find is using a custom type converter, but it looks like I'd need to override the entire mapping.)
Here is one way to do it:
map.CreateMap<ChildEntity, DerivedChild>();
map.CreateMap<ParentEntity, Parent>()
.ForMember(
x => x.Value,
opt => opt.ResolveUsing(
rr => map.Map<DerivedChild>(((ParentEntity)rr.Context.SourceValue).Value)));
ResolveUsing allows to specify custom logic for mapping the value.
The custom logic that is used here is actually calling map.Map to map to DerivedChild.
You may re-assign new object manually after mapping:
Mapper.CreateMap<ParentEntity, Parent>()
.AfterMap((src, dest) => dest.Value = new DerivedChild(){...});
or even re-map it:
.AfterMap((src, dest) => dest.Value = Mapper.Map<DerivedChild>(dest.Value));