0% found this document useful (0 votes)
61 views

Automapper

The document discusses various mapping techniques using AutoMapper, including: 1. Mapping between different entity types and DTOs, ignoring specific members, and mapping value types. 2. Using custom type converters and value resolvers when mapping is too complex for convention-based mapping. 3. Setting default values for null properties using NullSubstitute and running custom logic before and after mapping using Pre and Post actions. 4. AutoMapper can map lists and arrays as long as the element mappings are defined. 5. Common AutoMapper configuration in ASP.NET Core using services.AddAutoMapper().

Uploaded by

tomas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
61 views

Automapper

The document discusses various mapping techniques using AutoMapper, including: 1. Mapping between different entity types and DTOs, ignoring specific members, and mapping value types. 2. Using custom type converters and value resolvers when mapping is too complex for convention-based mapping. 3. Setting default values for null properties using NullSubstitute and running custom logic before and after mapping using Pre and Post actions. 4. AutoMapper can map lists and arrays as long as the element mappings are defined. 5. Common AutoMapper configuration in ASP.NET Core using services.AddAutoMapper().

Uploaded by

tomas
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 6

Two entities:

Mapper.Initialize(cfg =>
cfg.CreateMap<OrderLine, OrderLineDTO>()
.ForMember(dto => dto.Item, conf => conf.MapFrom(ol => ol.Item.Name)));

Null values

.ForMember(x => x.ClientNo, opt => opt.MapFrom(x => x.ClientNo ?? 0))

Composition:

Mapper.CreateMap<LearningMVC.User, LearningMVC.Models.User>().ForMember(emp => emp.Fullname,


map => map.MapFrom(p => p.FirstName + " " + p.LastName));

Mapper.CreateMap<doctor, healthcareprofessional="">()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => string.Join(" ", src.Title, src.FirstName,
src.LastName)));

If your names don't exactly match, you could use MapFrom:

public static class AutoMapperConfiguration


{
public static void Configure()
{
Mapper.CreateMap<Team, TeamDTO>()
.ForMember(dest => dest.Players, opt => opt.Ignore())
.ForMember(dest => dest.FoundingDate, opt => opt.MapFrom(src => src.OrganizationDate));
}
}

If field names do not match, you need to specify them. You can define the reverse map.

CreateMap<ViewModels.Base, Models.Base>()
.ForMember(dest => dest.BaseId, opts => opts.MapFrom(src => src.BaseId))
.ForMember(dest => dest.UserId, opts => opts.MapFrom(src => src.UserId))
.ForMember(dest => dest.Name, opts => opts.MapFrom(src => src.Name));

CreateMap<ViewModels.Base, Models.Base>().ReverseMap();

Ignore one or more

CreateMap<IbisAssetAccDetail, AddressBodyDto>()
.ForMember(t => t.City, o => o.MapFrom(s => s.City))
.ForAllOtherMembers(t => t.Ignore());
CreateMap<IbisAssetAccDetail, AddressBodyDto>()
.ForMember(x => x.CountryName, o => o.Ignore());

PostProcess

CreateMap<IbisAssetAccDetail, AddressBodyDto>()
.ForMember(x => x.CountryName, o => o.Ignore());
.AfterMap<MapCountryByCultureExpression>();

CreateMap<FnGetHoldingResult, ExportHoldingDto>().AfterMap(
(a, x) =>
{
var resourceValue = ResourceManager.GetString("SecurityMasterType_" + a.SecurityType);
if (!string.IsNullOrEmpty(resourceValue))
x.SecurityType = resourceValue;
});

Reverse

CreateMap<IbisAssetAccDetail, AddressBodyDto>()
.ForMember(t => t.City, o => o.MapFrom(s => s.City)).ReverseMap()

Mapping for different types

AutoMapper does not know about any mapping from string to int, DateTime or Type. To create maps for these
types, we must supply a custom type converter, and we have three ways of doing so:

void ConvertUsing(Func<TSource, TDestination> mappingFunction);


void ConvertUsing(ITypeConverter<TSource, TDestination> converter);
void ConvertUsing<TTypeConverter>() where TTypeConverter : ITypeConverter<TSource, TDestination>;

Func
cfg.CreateMap<string, int>().ConvertUsing(s => Convert.ToInt32(s));

Converter (Custom conversion)


Sometimes when the source and destination objects are too different to be mapped using convention, and simply too
big to write elegant inline mapping code (ForMember) for each individual member, it can make sense to do the mapping
yourself.
Or in other words:
Sometimes, the only thing you can do when converting one type to another is to take complete control of the
conversion. This is where custom type converters come in.

public class HealthcareProfessionalTypeConverter : ITypeConverter<doctor, healthcareprofessional="">


{
public HealthcareProfessional Convert(ResolutionContext context)
{
if (context == null || context.IsSourceValueNull)
return null;

Doctor source = (Doctor)context.SourceValue;

return new HealthcareProfessional


{
FullName = string.Join(" ", new[] { source.Title, source.FirstName, source.LastName })
};
}
}

//Arrange
Mapper.CreateMap<doctor, healthcareprofessional="">()
.ConvertUsing<healthcareprofessionaltypeconverter>();

Doctor source = new Doctor


{
Title = "Mr",
FirstName = "Jon",
LastName = "Preece",
};

Mapper.AssertConfigurationIsValid();

//Act
HealthcareProfessional result = Mapper.Map<healthcareprofessional>(source);

//Assert
Assert.IsNotNull(result);

Value Resolvers
Value resolves allow for correct mapping of value types. The source object KitchenCutlery contains a precise breakdown
of the number of knifes and forks in the kitchen, whereas the destination object Kitchen only cares about the sum total of
both. AutoMapper won’t be able to create a convention based mapping here for us, so we use a Value (type) Resolver;

Hide Copy Code


public class KitchenResolver : ValueResolver<kitchencutlery, int="">
{
protected override int ResolveCore(KitchenCutlery source)
{
return source.Knifes + source.Forks;
}
}

The value resolver, similar to the type converter, takes care of the mapping and returns a result, but notice that it is specific to
the individual property, and not the full object. The following code snippet shows how to use a Value Resolver;

Hide Copy Code


[Test]
public void Kitchen_KnifesKitchen_ConfigurationIsValid()
{
//Arrange

Mapper.CreateMap<kitchencutlery, kitchen="">()
.ForMember(dest => dest.KnifesAndForks, opt => opt.ResolveUsing<kitchenresolver>());

//Act

//Assert
Mapper.AssertConfigurationIsValid();
}

Null Substitution
Think default values. In the event that you want to give a destination object a default value when the source value is null, you
can use AutoMapper’s NullSubstitute feature.

Example usage of the NullSubstitute method, applied individually to each property;

Hide Copy Code


[Test]
public void Doctor_TitleIsNull_DefaultTitleIsUsed()
{
//Arrange
Doctor source = new Doctor
{
FirstName = "Jon",
LastName = "Preece"
};

Mapper.CreateMap<doctor, person="">()
.ForMember(dest => dest.Title, opt => opt.NullSubstitute("Dr"));

//Act
Person result = Mapper.Map<person>(source);

//Assert
Assert.AreSame(result.Title, "Dr");
}

Special parsing function

.ForMember(d => d.Arguments, o => o.ResolveUsing(s => ParseArguments(s.Arguments)));

private InvoiceLineArguments ParseArguments(string xml)


{
InvoiceLineArguments args;
using (var reader = new StringReader(xml))
{
args = (InvoiceLineArguments)serializer.Deserialize(reader);
return args;
}
}

List and Array Support

AutoMapper doesn't require special support for converting a list or an array of items. As long as I define the element
type mapping, AutoMapper can handle enumerating the list, performing the mapping and returning a new, mapped list.
All of the mappings I've showed so far don't need any changes (or additional mappings defined) to support a collection
of employees:

var partTimers = GetParttimeEmployees();


var mapped = Mapper.Map<IEnumerable<Employee>,
IEnumerable<EmployeeStats>>(partTimers);

Pre and Post Mapping Actions

AutoMapper will also let you define code to run before and after the mapping process. The pre-condition is useful if you
have a property on your source object that you want to use in the mapping, but it has to be loaded or initialized before it
can be used (perhaps loaded from NHibernate). For this type of situation, use the BeforeMap method:

Mapper.CreateMap<SelectedJob, Job>().BeforeMap((s, d) => ...);

The lambda gets access to both the mapped source object (the first parameter) as well as the destination object (the
second parameter). Likewise, you can define an AfterMap lambda that will be called after AutoMapper has done its job:

Mapper.CreateMap<SelectedJob, Job>().AfterMap((s, d) => ...);

Cons
One of the most common architectures for web apps right now is based on passing DataTransferObjects(DTOs) to and
from CRUD services that updates your business/domain entities using tools like AutoMapper and EntityFramework. I
will try to explain why this is a truly horrible approach.

ASP.NET Core with automapper

In Startup.cs, add this line to ConfigureServices method.

Startup.cs

using AutoMapper;

public void ConfigureServices(IServiceCollection services)


{
services.AddMvc();
services.AddAutoMapper();
}

You might also like