krombi
krombi

Reputation: 522

Automapper: map properties manually

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.

Upvotes: 20

Views: 44493

Answers (4)

AmirJabari
AmirJabari

Reputation: 473

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.

Upvotes: 5

Pawel Maga
Pawel Maga

Reputation: 5807

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

Upvotes: 12

Pranay Rana
Pranay Rana

Reputation: 176956

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

Upvotes: 34

Mark C.
Mark C.

Reputation: 6460

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

Upvotes: 0

Related Questions