I'm using an Automapper and I need to map a List of objects into a nested object. I have these objects:
public abstract class FooSrcBase
{
}
public class FooSrc : FooSrcBase
{
public bool Prop { get; set; }
}
public class FooDest
{
public bool Prop { get; set; }
}
public class FooDestGroup
{
public FooDest FooDest { get; set; }
}
public class Dest
{
public FooDestGroup FooDestGroup { get; set; }
}
I have IEnumerable<FooSrc> which contains FooSrc objects (there are many implementations and only one object per each type may exist in the source) and I need to map it into Dest object. I need this because of mapping into the view models for front end.
When I register mapping like this:
CreateMap<IEnumerable<FooSrc>, Dest>().ForPath(dest => dest.FooDestGroup.FooDest, opt => opt.MapFrom(src => src.FirstOrDefault(x => x.GetType() == typeof(FooSrc))));
CreateMap<FooSrc, FooDest>();
When I map empty list, a problem occurs in Dest object - FooDestGroup is an instance of object, which has a property FooDest with null value.
How it would be possible to make Dest property FooDestGroup map to null, if I provide empty list as a source?
Firstly, your abstract FooSrc class will need a different name (conflicts with your concrete class name FooSrc)
How about modifying the constructor on Dest to avoid the nesting issue?
Automapper is capable of mapping to the constructor parameter automatically, but if you need more advanced behaviour you can refer to https://docs.automapper.org/en/stable/Construction.html
Something like this should work:
public class Dest
{
public FooGroup FooGroup { get; set; }
public Dest(FooDest fooDest)
{
FooGroup = new FooGroup { FooDest = fooDest };
}
}
[..]
Mapper.CreateMap<FooSrc, Dest>();
Mapper.Map<List<Dest>>(listOfFooSrc);
This fixed my problem:
CreateMap<IEnumerable<FooSrcBase>, Dest>()
.ForMember(dest => dest.FooDestGroup, opt => opt.MapFrom(src => src.FirstOrDefault(x => x.GetType() == typeof(FooSrc))));
CreateMap<FooSrc, FooDestGroup>()
.ForMember(dest => dest.FooDest, opt => opt.MapFrom(src => src));
CreateMap<FooSrc, FooDest>();
Related
I'm trying to map one list to another, but not everything I want is in the source object. Let me explain:
These are my two objects:
public class SourceItem
{
public int Id { get; set;}
public string Name { get; set;}
}
public class TargetItem
{
public int TargetId { get; set;}
public string TargetName { get; set;}
public int Foo { get; set; }
}
This is how I configure my Mapper:
public class MyProfile : Profile
{
public MyProfile()
{
CreateMap<SourceItem, TargetItem>()
.ForMember(dest => dest.TargetId, opt => opt.MapFrom(src => src.Id))
.ForMember(dest => dest.TargetName, opt => opt.MapFrom(src => src.Name));
}
}
When I do this...
var targets = Mapper.Map<List<SourceItem>, List<TargetItem>>(sourceItems);
...the targets list's objects will have Foo set to 0. Obviously. What I'm trying to do is to also initialize Foo (with the parameter). Like this:
fooParameter = 321;
List<TargetItem> targets = Mapper.Map<List<SourceItem>, List<TargetItem>>(sourceItems);
foreach(var target in targets)
{
target.Foo = fooParameter;
}
Is it possible, to do it using Mapper?
Thanks in advance!
You can't do exactly what you want, but you can get pretty close by specifying mapping options when you call Map.
fooParameter = 321;
var targets = Mapper.Map<List<SourceItem>, List<TargetItem>>(sourceItems, opt =>
opt.AfterMap(
(src, dest) => {
foreach (var d in dest)
{
d.Foo = fooParameter;
}
})
);
I am using Automapper 6.2.2.0, and trying to map from an entity object to a DTO, with different property names.
I am getting the following error:
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
CompanyInfo -> OwnerDto (Destination member list) Db.CompanyInfo ->
TransferObjects.OwnerDto (Destination member list)
Unmapped properties: Name
My mapping looks like this:
cfg.CreateMap<CompanyInfo, OwnerDto>().ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.CompanyName));
My OwnerDto class:
public class OwnerDto
{
public int Id { get; set; }
public string Name { get; set; }
}
And my CompanyInfo Class:
public class CompanyInfo
{
public int Id { get; set; }
public string CompanyName { get; set; }
}
My mapping:
var results = Mapper.Map<List<CompanyInfo>,List <OwnerDto>>(data);
Any suggestions on what I am missing?
Initializer:
public static class AutomapperMaps {
Mapper.Initialize(cfg => {
cfg.CreateMap<User, UserDto>().ReverseMap();
cfg.CreateMap<CompanyInfo, OwnerDto>().ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.CompanyName));
// There are more mappings like the user one above. Those are all working fine.
}
}
I have the following classes:
Source:
public class Source
{
public int Amount { get; set; }
}
Destination:
public class Parent
{
public ChildBase Child { get; set; }
}
public class ChildBase
{
}
public class Child : ChildBase
{
public int Amount { get; set; }
}
For the map I'm trying to create, I want to map from Source to Parent. The property on the Parent is defined as ChildBase but I want the map to actually map to Child. How can I get the mapper to map to Child?
I have a simple map defined as:
CreateMap<Source, Parent>()
.ForMember(d => d.Child, opt => opt.MapFrom(s => s));
CreateMap<Source, Child>();
But obviously this is trying to look for a map with the destination of ChildBase. I tried casting the destination to be Child but that didn't work.
Any ideas?
You need to use the custom ValueResolver in AutoMapper.
Mapper.CreateMap<Source, Parent>()
.ForMember(p => p.Child, o => o.ResolveUsing(s => new Child { Amount = s.Amount }));
If you want to re-use this logic, you can put the code into a class instead of a lambda expression.
class SourceToChildValueResolver : ValueResolver<Source, Child>
{
protected override Child ResolveCore(Source source)
{
return new Child { Amount = source.Amount };
}
}
//register the resolver
CreateMap<Source, Parent>()
.ForMember(p => p.Child, o => o.ResolveUsing<SourceToChildValueResolver>());
I'm using Automapper to define a mapping configuration between some objects. I need to retrieve single mapped values for the destination based on the configuration.
Let's say I have a domain object
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
and a corresponding DTO
class PersonDTO
{
public string FullName { get; set; }
}
and a mapping
Mapper.CreateMap<Person, PersonDTO>()
.ForMember(
dest => dest.FullName,
opts => opts.MapFrom(src => src.FirstName + " " + src.LastName));
I'd like a method like Mapper.MapField<PersonDTO>(person, x => x.FullName) that returns just one mapped value (that would get assigned to the specified field in a full mapping). Is there any way I can do this without mapping the whole object and extracting the value from the result?
First create a resolver and then use the resolve using method in your mapper call.
public class PersonNameResolver : ValueResolver<Person, PersonDTO>
{
protected override PersonDTO ResolveCore(Person source)
{
return new PersonDTO
{
FirstName = source.FirstName,
LastName = source.LastName
};
}
}
Once this is created, use the following in your mapper call.
Mapper.CreateMap<Person, PersonDTO>()
.ForMember(
dest => dest.Name,
opts => opts.ResolveUsing<PersonNameResolver>().FromMember(o => o))
I have two classes that looks as follows:
public class Rule
{
public int Id { get; set; }
public RuleGroup RuleGroup { get; set; }
}
public class RuleGroup
{
public int Id { get; set; }
public List<Rule> RuleList { get; set; }
}
A RuleGroup has a list of rules. My AutoMapper settings are as follows:
Mapper.CreateMap<RuleRecord, FirstSolar.Mes.Core.Entities.Recipe.Rule>()
.ForMember(destination => destination.RuleGroup, source => source.Ignore())
.ForMember(destination => destination.Id, source => source.MapFrom(item => item.RuleId));
Mapper.CreateMap<IList<RuleRecord>, IList<FirstSolar.Mes.Core.Entities.Recipe.Rule>>();
Mapper.CreateMap<RuleGroupRecord, FirstSolar.Mes.Core.Entities.Recipe.RuleGroup>()
.ForMember(destination => destination.Id, source => source.MapFrom(item => item.RuleGroupId));
Mapper.CreateMap<IList<RuleGroupRecord>, IList<FirstSolar.Mes.Core.Entities.Recipe.RuleGroup>>();
When I attempt to map a RuleGroupRecord (LinqToSQL object) to RuleGroup (DTO), AutoMapper says I need to add a mapping for RuleGroup.RuleList. I'm wondering why because I defined how to map a single RuleRecord and a List.
If I have to, how would I do it?
Simply add (I hope I got the syntax right, but you should see what I'm hinting at):
.ForMember(destination => destination.RuleList, source => source.MapFrom(item => item.Rules));
to the second mapping. While you handled the general mapping for RuleRecord to Rule in the first mapping, you didn't tell automapper to map the specific property RuleGroup.RuleList.