Automapper map an inherited object to a child - c#

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

Related

How to map a collection inside a object with automapper

I don't know how to map 'Poses' which is inside my dto coming into my controller.
My DTO looks like this
public class NewInstructorProgramDto
{
public ICollection<int> Poses { get; set; }
}
I'm have an object that looks like this below where I need to map my DTO to this
public class InstructorProgram : BaseEntity
{
public ICollection<InstructorProgramPose> Poses { get; set; }
}
where InstructorProgramPose looks like this
public class InstructorProgramPose : BaseEntity
{
public int PoseId { get; set; }
public Pose Pose { get; set; }
public int InstructorProgramId { get; set; }
public InstructorProgram InstructorProgram { get; set; }
}
In my controller I map it like this
var newInstructorProgram = _mapper.Map<NewInstructorProgramDto, InstructorProgram>(newInstructorProgramDto);
and in my mapping file I need to figure out how to map all the integers for Poses to a collection of 'InstructorProgramPose' objects, where the integer is the PoseId
Here is what I have so far but this won't work because it only maps a single InstructorProgramPose, not a collection
CreateMap<NewInstructorProgramDto, InstructorProgram>()
.ForMember(d => d.Poses, o => o.MapFrom(s => new InstructorProgramPose() { PoseId = s.}));
I think I'm on the right track with this code below
CreateMap<NewInstructorProgramDto, InstructorProgram>()
.ForMember(d => d.Poses, o => o.MapFrom(s => s.Poses));
CreateMap<ICollection<int>, ICollection<InstructorProgramPose>>()
.ForMember(d => d.Select(i => i.PoseId), o => o.MapFrom(s => s.Select(i => i)));
but the debugger is throwing an error that says
Exception has occurred: CLR/AutoMapper.AutoMapperConfigurationException
An exception of type 'AutoMapper.AutoMapperConfigurationException' occurred in AutoMapper.dll but was not handled in user code: 'Custom configuration for members is only supported for top-level individual members on a type.'
UPDATE - Just to give you an idea of what I'm trying to do in Automapper, I've included the code I used to create and fill the object without Automapper, just to show what's needed.
In my controller
var newInstructorProgram = new InstructorProgram() {
Name = newInstructorProgramDto.Name,
Description = newInstructorProgramDto.Description,
Length = (EventLength)newInstructorProgramDto.Length,
Experience = (Experience)newInstructorProgramDto.Experience,
Style = (YogaStyle)newInstructorProgramDto.Style,
Calories = newInstructorProgramDto.Calories,
InstructorId = userFromRepo.Id
};
foreach(var poseId in newInstructorProgramDto.Poses) {
var newPose = new InstructorProgramPose() { PoseId = poseId };
newInstructorProgram.Poses.Add(newPose);
}
_unitOfWork.Repository<InstructorProgram>().Add(newInstructorProgram);
I implemented a solution last night so I'm posting it here.
I added an extension method to the mapping shown below
CreateMap<InstructorProgramForUpdateDto, InstructorProgram>()
.ForMember(d => d.Poses, opt => opt.MapFrom(s => s.Poses.GetInstructorProgramPoses<InstructorProgramPose>()));
Here is the method in the extension class
public static ICollection<T> GetInstructorProgramPoses<T>(this ICollection<int> poses) where T : IInstructorProgramPose, new() {
var instructorProgramPoseColllection = new List<T>();
foreach(var poseId in poses) {
T pose = new T();
pose.PoseId = poseId;
instructorProgramPoseColllection.Add(pose);
}
return instructorProgramPoseColllection;
}

Automapper don't map null sources

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

Map foreign key in Fluent NHibernate without object property

My question is, is there a possible Fluent NHibernate mapping for Parent and Child objects that does not require the Child object to have a Parent object property? I haven't figured out how to map the reference back to the Parent. When I call Create with the mappings as-is, I get an exception because the Child object does not have the required foreign key (required in the data store) back to the Parent.
I have two POCO classes:
public class Parent
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<Child> Childs { get; set; }
}
public class Child
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int ParentId { get; set; }
}
And some mappings:
public class ParentMap : ClassMap<Parent>
{
public ParentMap()
{
this.Table("Parents");
this.Id(x => x.Id);
this.Map(x => x.Name);
this.HasMany(x => x.Childs).KeyColumn("ChildId").Cascade.AllDeleteOrphan();
}
}
public class ChildMap : ClassMap<Child>
{
public ChildMap()
{
this.Table("Childs");
this.Id(x => x.Id);
this.Map(x => x.Name);
// Needs some sort of mapping back to the Parent for "Child.ParentId"
}
}
And Create method:
public Parent Create(Parent t)
{
using (this.session.BeginTransaction())
{
this.session.Save(t);
this.session.Transaction.Commit();
}
return t;
}
I want to be able to create a Parent object that has a list of Child objects, but not have the Child objects have references back to their Parent (other than the Parent ID). I want to do this to avoid the circular reference from Parent to a list of Childs back to the Parent object, since that is causing issues with JSON serialization.
You can map these entities without problem, try this:
public class ParentMap : ClassMap<Parent>
{
public ParentMap()
{
this.Table("Parents");
this.Id(x => x.Id);
this.Map(x => x.Name);
this.HasMany(x => x.Childs).KeyColumn("ChildId").Cascade.AllDeleteOrphan();
}
}
public class ChildMap : ClassMap<Child>
{
public ChildMap()
{
this.Table("Childs");
this.Id(x => x.Id);
this.Map(x => x.Name);
this.Map(x => x.ParentId);
// if you have a reference of Parent object, you could map as a reference, for sample:
this.References(x => x.Parent).Column("ParentId");
}
}
When you get entities from ISession, do not serialize it to some format because these can be proxies of nhibernate instead entities objects. Try to create DTO (Data Transfer Object) classes and convert these entities to a DTO object, and serialize it. You will avoid circular references. For sample:
public class ParentDTO
{
public int Id { get; set; }
public string Name { get; set; }
public int ParentId { get; set; }
/* here you just have the primitive types or other DTOs,
do not reference any Entity type*/
}
And when you need to read the values to share the serialized value:
var dto = ISession.Query<Parent>()
.Select(x =>
new ParentDTO() {
Id = x.Id,
Name = x.Name,
ParentId = x.ParentId)
.ToList();
Get this result from the Data Access Layer and try to serialize, for sample:
var result = Serialize(dto);

Mapping one source class to multiple derived classes with automapper

Suppose i have a source class:
public class Source
{
//Several properties that can be mapped to DerivedBase and its subclasses
}
And some destination classes:
public class DestinationBase
{
//Several properties
}
public class DestinationDerived1 : DestinationBase
{
//Several properties
}
public class DestinationDerived2 : DestinationBase
{
//Several properties
}
Then I wish the derived destination classes to inherit the automapper configuration of the baseclass because I do not want to have to repeat it, is there any way to achieve this?
Mapper.CreateMap<Source, DestinationBase>()
.ForMember(...)
// Many more specific configurations that should not have to be repeated for the derived classes
.ForMember(...);
Mapper.CreateMap<Source, DestinationDerived1 >()
.ForMember(...);
Mapper.CreateMap<Source, DestinationDerived2 >()
.ForMember(...);
When I write it like this it does not use the base mappings at all, and include doesn't seem to help me.
Edit:
This is what I get:
public class Source
{
public string Test { get; set; }
public string Test2 { get; set; }
}
public class DestinationBase
{
public string Test3 { get; set; }
}
public class DestinationDerived1 : DestinationBase
{
public string Test4 { get; set; }
}
public class DestinationDerived2 : DestinationBase
{
public string Test5 { get; set; }
}
Mapper.CreateMap<Source, DestinationBase>()
.ForMember(d => d.Test3, e => e.MapFrom(s => s.Test))
.Include<Source, DestinationDerived1>()
.Include<Source, DestinationDerived2>();
Mapper.CreateMap<Source, DestinationDerived1>()
.ForMember(d => d.Test4, e => e.MapFrom(s => s.Test2));
Mapper.CreateMap<Source, DestinationDerived2>()
.ForMember(d => d.Test5, e => e.MapFrom(s => s.Test2));
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
Source -> DestinationDerived1 (Destination member list)
Test3
Include derived mappings into base mapping:
Mapper.CreateMap<Source, DestinationBase>()
.ForMember(d => d.Id, op => op.MapFrom(s => s.Id)) // you can remove this
.Include<Source, DestinationDerived1>()
.Include<Source, DestinationDerived2>();
Mapper.CreateMap<Source, DestinationDerived1>()
.ForMember(d => d.Name, op => op.MapFrom(s => s.Text))
.ForMember(d => d.Value2, op => op.MapFrom(s => s.Amount));
Mapper.CreateMap<Source, DestinationDerived2>()
.ForMember(d => d.Value, op => op.MapFrom(s => s.Amount));
Usage:
Mapper.AssertConfigurationIsValid();
var s = new Source() { Id = 2, Amount = 10M, Text = "foo" };
var d1 = Mapper.Map<DestinationDerived1>(s);
var d2 = Mapper.Map<DestinationDerived2>(s);
See Mapping inheritance on AutoMapper wiki.
UPDATE: Here is full code of classes which works as it should.
public class Source
{
public int Id { get; set; }
public string Text { get; set; }
public decimal Amount { get; set; }
}
public class DestinationBase
{
public int Id { get; set; }
}
public class DestinationDerived1 : DestinationBase
{
public string Name { get; set; }
public decimal Value2 { get; set; }
}
public class DestinationDerived2 : DestinationBase
{
public decimal Value { get; set; }
}
UPDATE (workaround of AutoMapper bug):
public static class Extensions
{
public static IMappingExpression<Source, TDestination> MapBase<TDestination>(
this IMappingExpression<Source, TDestination> mapping)
where TDestination: DestinationBase
{
// all base class mappings goes here
return mapping.ForMember(d => d.Test3, e => e.MapFrom(s => s.Test));
}
}
And all mappings:
Mapper.CreateMap<Source, DestinationBase>()
.Include<Source, DestinationDerived1>()
.Include<Source, DestinationDerived2>()
.MapBase();
Mapper.CreateMap<Source, DestinationDerived1>()
.MapBase()
.ForMember(d => d.Test4, e => e.MapFrom(s => s.Test2));
Mapper.CreateMap<Source, DestinationDerived2>()
.MapBase()
.ForMember(d => d.Test5, e => e.MapFrom(s => s.Test2));
For Automapper 8.0.
Current version has new method IncludeAllDerived
Here's working example:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Source, DestinationBase>()
.ForMember(dest => dest.Test3, opt => opt.MapFrom(src => src.Test))
.IncludeAllDerived();
cfg.CreateMap<Source, DestinationDerived1>()
.ForMember(dest => dest.Test4, opt => opt.MapFrom(src => src.Test2));
cfg.CreateMap<Source, DestinationDerived2>()
.ForMember(dest => dest.Test5, opt => opt.MapFrom(src => src.Test2));
});
var mapper = config.CreateMapper();
var source = new Source { Test = "SourceTestProperty", Test2 = "SourceTest2Property" };
var d1 = mapper.Map<DestinationDerived1>(source);
var d2 = mapper.Map<DestinationDerived2>(source);
Assert.Equal("SourceTestProperty", d1.Test3);
Assert.Equal("SourceTest2Property", d1.Test4);
Assert.Equal("SourceTestProperty", d2.Test3);
Assert.Equal("SourceTest2Property", d2.Test5);
NB! For those who are having issues with derived interfaces. AutoMapper does not support registering against derived interfaces. Only classes are handled.
To make it work, you have to change your type reference for CreateMap to the class instead of interface.
Example:
interface Interface1 {}
class Class1: Interface1 {}
interface Interface2: Interface1 {}
class Class2: Class1, Interface2 {}
CreateMap<OtherClass, Interface1>().IncludeAllDerived();
CreateMap<OtherClass, Interface2>();
Any mapping against Interface2 will only use the first CreateMap. You will have to identify the second as
CreateMap<OtherClass, Class2>();

Need help with AutoMapper

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.

Categories

Resources