I am using Auto Mapper to map source to destination object, I have configured my mapper like this:
Mapper.Initialize(cfg => {
cfg.CreateMap< SourceModel, DestModel>();
}
This source and dest object mapping is being used in many places, now in some cases, I have to ignore one of source model field, but not for all places. I could do like this:
CreateMap< SourceModel, DestModel>()
.ForMember(x => x.CreatedDateTime, opt => opt.Ignore());
But this will ignore CreatedDateTime property for all scenario, so I want to do it inline only.
Mapper.Map< DestModel>(sourceObject); //Here I want to ignore one property.
Please help me how I can achieve this.
Sounds like you need conditional mapping.
This answer on SO shows how to use it and the documentation can be found here.
example usage:
Mapper.CreateMap<SourceModel, DestModel>()
.ForMember(dest => dest.CreatedDateTime, opt => opt.Condition(source => source.Id == 0))
Related
Is it possible to resolve a few destination properties from one source property using AutoMapper?
The obvious way to go is to do like this:
CreateMap<Source, Destination>
.ForMember(d => d.dest1, opt => opt.MapFrom(s => s.sour["dest1"]))
.ForMember(d => d.dest2, opt => opt.MapFrom(s => s.sour["dest2"]))
...
.ForMember(d => d.dest10, opt => opt.MapFrom(s => s.sour["dest10"]))
But is there maybe something like a custom resolver which would allow to write a method which would perform the assignment of multiple destination properties using one (or maybe even a few) source property (properties)?
I'd like to send some additional information to Automapper so I can use them in the CreateMap. It seems that I have to use MappingOperatingOption and Items.
So my call is like this:
var obj = Mapper.Map<class>(x, o => o.Items.Add("data", 23));
The problem is that I cannot access that value in the MapFrom.
Mapper.CreateMap<ClassA, ClassB>()
.ForMember(x => x.FieldA, o => o.MapFrom(d =>
//accessing item here))
There's very little documentation about Automapper and I didn't come up with anything, any guidance is welcome.
Use ResolveUsing instead of MapFrom like this:
Mapper.CreateMap<ClassA, ClassB>()
.ForMember(dst => dst.FieldA,
opt =>
opt.ResolveUsing((resolution_result, src) =>
(int)resolution_result.Context.Options.Items["data"] + src.FieldB));
I am using (int)resolution_result.Context.Options.Items["data"] + src.FieldB just as an example here. You can read any piece of data from resolution_result.Context.Options.Items and use it as you like.
Automapper allows you to define a mapping and invoke it with the following syntax:
Mapper.CreateMap<Order, OrderDto>();
OrderDto dto = Mapper.Map<OrderDto>(order);
Is it possible to specify the destination type using a predicate of sorts?
Mapper.CreateMap<Order, FooType>().Where(s => s["_type"].ToString() == "Foo");
Mapper.CreateMap<Order, BarType>().Where(s => s["_type"].ToString() == "Bar");
Both FooType and BarType have internal properties corresponding to the values of separate keys within the source types internal dictionary.
In order to map these values correctly I need to be sure they exist, which in this case is determined by the value of the _type key.
Edit: If this is possible what would the syntax be for using Mapper.Map<>();?
You may set a map condition for each property, look on these samples:
Mapper.CreateMap<Foo,Bar>()
.ForMember(dest => dest.baz, opt => opt.Condition(src => (src.baz >= 0)));
and
Mapper.CreateMap<Source, Target>()
.ForMember(dest => dest.Value,
opt => opt.MapFrom
(src => src.Value1.StartsWith("A") ? src.Value1 : src.Value2));
I just started to use automapper to map DTOs<->Entities and it seems to be working great.
In some special cases I want to map only some properties and perform additional checks. Without automapper the code looks like this (using fasterflect's PropertyExtensions):
object target;
object source;
string[] changedPropertyNames = { };
foreach (var changedPropertyName in changedPropertyNames)
{
var newValue = source.GetPropertyValue(changedPropertyName);
target.SetPropertyValue(changedPropertyName, newValue);
}
Of course this code won't work if type conversions are required. Automapper uses built-in TypeConverters and I also created some specific TypeConverter implementations.
Now I wonder whether it is possible to map individual properties and use automapper's type conversion implementation, something like this
Mapper.Map(source, target, changedPropertyName);
Update
I think more information is necessary:
I already created some maps, e.g.
Mapper.CreateMap<CalendarEvent, CalendarEventForm>()
and I also created a map with a custom typeconverter for the nullable dateTime property in CalendarEvent, e.g.
Mapper.CreateMap<DateTimeOffset?, DateTime?>().ConvertUsing<NullableDateTimeOffsetConverter>();
I use these maps in a web api OData Controller. When posting new EntityDTOs, I use
Mapper.Map(entityDto, entity);
and save the entity to a datastore.
But if using PATCH, a Delta<TDto> entityDto is passed to my controller methods. Therefore I need to call entityDto.GetChangedPropertyNames() and update my existing persistent entity with the changed values.
Basically this is working with my simple solution, but if one of the changed properties is e.g. a DateTimeOffset? I would like to use my NullableDateTimeOffsetConverter.
If you just want to map only some select property than you have to do as below
// Create a map
var map = CreateMap<Source,Target>();
// ingnore all existing binding of property
map.ForAllMembers(opt => opt.Ignore());
// than map property as following
map.ForMember(dest => dest.prop1, opt => opt.MapFrom( src => src.prop1));
map.ForMember(dest => dest.prop2, opt => opt.MapFrom( src => src.prop2));
You can make some projection using MapFrom method - http://automapper.readthedocs.io/en/latest/Projection.html
Mapper.Map(source, target)
.ForMember(m => m.Property, opt => opt.MapFrom(src => src.ChangedProperty));
For example (reffering to AutoMapper documentation):
// Model
var calendarEvent = new CalendarEvent
{
Date = new DateTime(2008, 12, 15, 20, 30, 0),
Title = "Company Holiday Party"
};
// Configure AutoMapper
Mapper.CreateMap<CalendarEvent, CalendarEventForm>()
.ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.Date.Date))
.ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.Date.Hour))
.ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.Date.Minute));
To do this with the latest version of AutoMapper first you should map the properties that you want then ignore the rest.
CreateMap<TSource, TDestination>()
.ForMember(dest => dest.FirstName, opt => opt.MapFrom(src => src.FirstName))
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => $"{src.FirstName} {src.LastName}"))
.ForMember(dest => dest.Prop, opt => opt.MapFrom(src => src.AnotherProp))
// ...
.ForAllOtherMembers(opt => opt.Ignore()); // <=== Ignore The rest
Otherwise if you do map.ForAllMembers(opt => opt.Ignore()); first, it will ignore all mappings even the mappings after this.
If I read your question correctly, yes, as long as your destination (target) property matches your conversion.
So if I am going from a string to a bool for a Status of "A" or "I" (active/inactive), I can do something like:
.ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status == "A"))
And then when going the other direction, convert it back:
.ForMember(dest => dest.Status, opt => opt.MapFrom(src => src.Status ? "A" : "I"))
A date example:
.ForMember(dest => dest.SomeDate, opt => opt.MapFrom(src => src.SomeDate.ToString("M/d/yyyy")));
Is it possible in AutoMapper to ignore certain properties while mapping a list?
For example, I have two classes Metadata and MetadataInput.
Both have the same fields except for destination, "MetadataInput", which has an extra field.
Mapper.CreateMap <IList<Metadata>, IList<MetadataInput>>()
I've tried to use the formember option but it generates errors, probably because the property cannot be used when you want to map a list? If yes, is there alternative solution?
As Darin Dimitrov suggested, you should not try and map lists or collections.
If you have a 1 -> 1 relationship between them all, just make a map like:
Mapper.CreateMap<Metadata, MetadataInput>().ForMember(s => s.Property, t => t.Ignore());
Then you could use your list and select it to the other list.
var metadataList = new List<Metadata>();
var meatadataInputList = metadataList.Select(p => Mapper.Map<MetadataInput>(p).ToList();
Use this mapper configuration to get the desired result:
Mapper.CreateMap<Output, Input>().ForMember(s => s.InputProperty1, t => t.Ignore());
Mapper.CreateMap<Output, Input>().ForMember(s => s.InputProperty2, t => t.Ignore());
Mapper.CreateMap<Input, Output>();
listOfItems = Mapper.Map<List<Input>, List<Output>>(InputListObject);
As others suggested, we should avoid mapping lists and collections. In order to ignore all the unmapped properties, the following worked out for me.
CreateMap<Metadata, MetadataInput>()
.ForMember(dest => dest.Id, o => o.MapFrom(src => src.sourceIdentifier))
.ForMember(dest => dest.Name, o => o.MapFrom(src => src.sourceName))
.ForAllOtherMembers(opts => opts.Ignore());
ForAllOtherMembers should be the last in the method chain.
Thanks for the useful comments.
Because both lists are already made before mapping I've done it this way:
Gets the list from the db:
List<Metadata> metadatas = _Metadataservice.GetList(_Collectionservice.Get("Koekelare").ID).ToList();
Create the mapper (thanks for pointing out my mistake):
Mapper.CreateMap<Metadata, MetadataInput>().ForMember(s => s.Property, t => t.Ignore());
Make a new list and map the new (single) values one by one:
List<MetadataInput> meta = new List<MetadataInput>();
for (int i = 0; i < e.Count; i++)
{
meta.Add(Mapper.Map<Metadata, MetadataInput>(metadatas[i], input.Metadatas[i]));
}
Could someone confirm this is a good way?