I have fairly simple question regarding Automapper mapping definition. My intent is to deep clone an object via Automapper while ignoring 'Id' property, this is why i have chosen it to customize the mapping.
public interface IEntity<T>
{
T Id { get; }
}
public abstract class Entity : IEntity<Guid>
{
public Guid Id { get; set; }
}
All my entities are deriving from Entity class and i simply wants to ignore all Id property in the nested hierarchy of my object without being so explicit about the mapping definition.
So far i have come up with the following piece of code to do the cloning but how to ignore Id property mapping for the nested properties and not just for the root.
public static T AutomapperClone<T>(this T source)
where T : IEntity<Guid>
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<T, T>()
.ForMember(d => d.Id, o => o.Ignore());
});
// checking configuration validity
config.AssertConfigurationIsValid();
// creating mapper
var mapper = config.CreateMapper();
var copy = mapper.Map<T, T>(source);
return copy;
}
The idea is that all entities get their new Id instead of using the same mapped ones. Is it accomplishable via Automapper?
Appreciate your feedback.
I wouldn't use Automapper for this person, try AnyClone to do this. It does deep cloning and can ignore by property name which seems to be what you are looking for.
Related
I have troubles trying to ignore a destination property
Source class:
public class ClassDto
{
public int Id { get; set; }
}
Destination class:
public class ClassModel
{
public int Id { get; set; }
public IList<string> ListString { get; set; }
}
Example:
public class Program
{
static void Main(string[] args)
{
var dto = new ClassDto { Id = 1 };
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<ClassDto, ClassModel>().
ForMember(i => i.ListString, opt => opt.DoNotUseDestinationValue());
});
config.AssertConfigurationIsValid();
var mapper = config.CreateMapper();
var model = mapper.Map<ClassDto, ClassModel>(dto);
}
}
Unfortunately config.AssertConfigurationIsValid(); rises an exception:
$exception {"\nUnmapped members were found. Review the types and members below.\nAdd a custom mapping expression, ignore, add a custom
resolver, or modify the source/destination type\nFor no matching
constructor, add a no-arg ctor, add optional arguments, or map all of
the constructor
parameters\n===================================================\r\nClassDto
-> ClassModel (Destination member list)\r\nAutoMapperFoo.ClassDto -> AutoMapperFoo.ClassModel (Destination member list)\r\n\r\nUnmapped
properties:\r\nListString\r\n"} AutoMapper.AutoMapperConfigurationException
I can't undesrtand why, I explicit to ignore ListString by DoNotUseDestinationValue
Thanks in advance
By default, AutoMapper tries to map all the properties from the source
type to the destination type when both source and destination type
property names are the same. If you want some of the properties not to map
with the destination type property then you need to use the AutoMapper
Ignore Property in C#. Learn more AutoMapper Ignore Property in C#
Automapper gives the property Ignore which tells the mapper to not
take the value of a property from the source class. Ignore not only
ignore the mapping for the property, but also ignore the mapping of
all inner properties. It means that if the property is not a primitive
type, but another class, that if you are using Ignore, this class
properties won't be mapped. So, A.B.C would be A.Null.
So try using this:
.ForMember(x => x.ListString, opt => opt.Ignore())
I'm currently experiencing an issue when trying to map the entire destination object from a child property on the source object. Something similar as described here: Automapper - How to map from source child object to destination
I've made use of the .ConstructUsing method as described in the link above however I'm seeing some weird behaviour where the outputted, mapped object is getting values from the parent instead of the child.
I made a demo of the problem here: https://dotnetfiddle.net/OdaGUr
Is this a problem with my code, should I be using a different method to achieve what I'm trying to do or is this a fault with AutoMapper?
EDIT:
public static void Main()
{
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Child1, Child2>();
cfg.CreateMap<Parent, Child2>().ConstructUsing((src, ctx) => ctx.Mapper.Map<Child2>(src.Child1));
});
var mapper = config.CreateMapper();
var parent = new Parent{
Id = 1,
Child1 = new Child1 {
Id = 2
}
};
var child2 = mapper.Map<Parent, Child2>(parent);
Console.WriteLine(child2.Id); // Returns 1. Expect this to be 2 from Parent.Child1
}
public class Parent
{
public int Id {get;set;}
public Child1 Child1 {get;set;}
}
public class Child1
{
public int Id {get;set;}
}
public class Child2
{
public int Id {get;set;}
}
ConstructUsing() is used to create the destination object, where the value should be stored in. In your case you are returning a Child2 object with the Id value set to 2 (as returned by the ctx.Mapper.Map<Child1, Child2>(src.Child1) line).
However, after the object has been created, the default mapping will still be applied. This means that the Parent.Id value will be saved in the Child2.Id property, because the names of the property match ("Id"). So, the initial value of 2 will be replaced with the value 1 from the Parent object.
Depending on what you want to do, you might want to use ForMember() to configure special handling on how the property values should be mapped. An example would be:
.ForMember(dest => dest.Id, src => src.MapFrom(it => it.Child1.Id))
Is it possible to tell AutoMapper during map creating to map onto existing instance of nested property?
Let's suppose I've got a class:
public class SomeClass
{
public int Id {get; set;}
public Complex Settings {get; set;}
}
public class Complex
{
public int Id { get; set;}
public string SomeText { get; set;}
}
I want to create map from SomeClass to SomeClass and use it to map properties onto existing instance.
Mapper.CreateMap<SomeClass, SomeClass>()
.ForMember(src => src.Settings, opts => opts.MapFrom(src => Mapper.Map<Complex, Complex>(src));
Mapper.CreateMap<Complex, Complex>();
Mapper.Map<SomeClass, SomeClass>(a, b);
Where a and b are instances of SomeClass. The problem is this solution maps properties onto existing instance but creates new instance of Complex instead of mapping a.Complex onto existing b.Complex.
Is it possible to configure AutoMapper to get desired behavior?
(It's causing me a lot of problems with Entity Framework).
I figured it out myself. Solution was pretty simple.
Correct map creation looks like this:
Mapper.CreateMap<SomeClass, SomeClass>()
.ForMember(src => src.Settings, opts => opts.Ignore())
.AfterMap((src, dst) => Mapper.Map<TestSettings,TestSettings>(src.TestSettings, dst.TestSettings);
Mapper.CreateMap<Complex, Complex>();
Ive searched for this for few days now and cant seem to get anything to work, I am using c# MVC Entity Framework with Automapper and im trying to achieve the below ViewModels (mainly LostDocumentVM) to be mapped from my database, all other properties will be set in controllers.
Here is my ViewModels...
DocumentVM
{
Public Enum.HistoricType HistoricType {get;set;}
Public DocumentChildVM Document { get; set;}
}
DocumentChildVM
{
Public bool ShowHistoricLink {get;set;}
Public IEnumerable<ListDocumentVM> DocumentsToReview {get;set;}
}
ListDocumentVM
{
Public int Id {get;set;}
Public string Name {get; set;}
Public DateTime? ReviewDate {get;set;}
}
I initialise the DocumentVM like this...
DocumentVM documentVM = DataContext.SystemUser.Where(x=>x.SustemUserID==LoggedOnUserID).Project().To<DocumentVM>().SingleOrDefault();
And my mapping is like this...
Mapper.CreateMap<SystemUser,DocumentVM>()
.ForMember(dest=>dest.Document.DocumentsToReview, opt=>opt.MapFrom(src=>src.Documents.Where(x=>x.DocumentType == Enum.DocumentType.Assessment));
Im new to AutoMapper and struggling to get more advanced mappings to work.
Yes, your ForMember member must refer to a member on the destination type, and yours is referring to a member on the child type. Instead, you'll need to create an AfterMap function that fills in this information on that child entity.
It's not difficult, but you have a bit of a strange set up where a child object Document has a property DocumentsToReview from another property on the parent DocumentVM:
documentVM.Document.DocumentsToReview =
src.Documents.Where(doc => doc.DocumentType == Enum.DocumentType.Assessment);
When you have to shuffle data between sibling/nephew members, it gets a little more challenging.
To do this with AfterMap:
Mapper.CreateMap<SystemUser, DocumentVM>()
.AfterMap((src, dest) => dest.Document.DocumentsToReview =
src.Documents.Where(doc => doc.DocumentType == Enum.DocumentType.Assessment));
class SomeObject
{
public string name {get;set;}
}
class CustomCollection : List<SomeObject>
{
public int x {get;set;}
public string z {get;set;}
}
class A
{
public CustomCollection collection { get ; set; }
}
class B
{
public CustomCollection collection { get ; set; }
}
// Creating mapping
Mapper.CreateMap<A, B>();
When I Map A to B, all properties get mapped correctly except X and Z in CustomCollection.
CustomCollection correctly gets the List of SomeObject initialized and SomeObject.Name is also mapped correctly.
Only the custom properties X, Z that I've declared in the collection do not get mapped.
What am I doing wrong?
Only way I've found is to do an after mapping like below, but then it kinda defeats the purpose of using automapper and it breaks everytime I add a new property to CustomCollection.
Mapper.CreateMap<A, B>().AfterMap((source, destination) => {
source.x = destination.x;
source.z = destination.z ;
});
Your current mapping configuration does create a new CustomCollection but the SomeObject items inside are references to the objects in the source collection. If that's not an issue you can use the following mapping configuration:
CreateMap<CustomCollection, CustomCollection>()
.AfterMap((source, dest) => dest.AddRange(source));
CreateMap<A, B>();
If your are also fine with b.collection referencing to a.collection you could use the following mapping configuration:
CreateMap<CustomCollection, CustomCollection>()
.ConstructUsing(col => col);
CreateMap<A, B>();
AutoMapper is not designed for cloning so if you need that you must write your own logic for that.