It's on the outer layer but it only references the Core. In my case the infrastructure layer also houses Automapper. The core defines a simple interface (let's say IAutoMapper ) and a simple object that exists in the infrastructure implements it and the object can be passed to the UI layer through dependency injection.
AutoMapper is used whenever there are many data properties for objects, and we need to map them between the object of source class to the object of destination class, Along with the knowledge of data structure and algorithms, a developer is required to have excellent development skills as well.
Where do I configure AutoMapper? ¶ Configuration should only happen once per AppDomain. That means the best place to put the configuration code is in application startup, such as the Global.
1. If you use the convention-based mapping and a property is later renamed that becomes a runtime error and a common source of annoying bugs. 2. If you don't use convention-based mapping (ie you explicitly map each property) then you are just using automapper to do your projection, which is unnecessary complexity.
Doesn't matter, as long as it's a static class. It's all about convention.
Our convention is that each "layer" (web, services, data) has a single file called AutoMapperXConfiguration.cs
, with a single method called Configure()
, where X
is the layer.
The Configure()
method then calls private
methods for each area.
Here's an example of our web tier config:
public static class AutoMapperWebConfiguration
{
public static void Configure()
{
ConfigureUserMapping();
ConfigurePostMapping();
}
private static void ConfigureUserMapping()
{
Mapper.CreateMap<User,UserViewModel>();
}
// ... etc
}
We create a method for each "aggregate" (User, Post), so things are separated nicely.
Then your Global.asax
:
AutoMapperWebConfiguration.Configure();
AutoMapperServicesConfiguration.Configure();
AutoMapperDomainConfiguration.Configure();
// etc
It's kind of like an "interface of words" - can't enforce it, but you expect it, so you can code (and refactor) if necessary.
EDIT:
Just thought I'd mention that I now use AutoMapper profiles, so the above example becomes:
public static class AutoMapperWebConfiguration
{
public static void Configure()
{
Mapper.Initialize(cfg =>
{
cfg.AddProfile(new UserProfile());
cfg.AddProfile(new PostProfile());
});
}
}
public class UserProfile : Profile
{
protected override void Configure()
{
Mapper.CreateMap<User,UserViewModel>();
}
}
Much cleaner/more robust.
You can really put it anywhere as long as your web project references the assembly that it is in. In your situation I would put it in the service layer as that will be accessible by the web layer and the service layer and later if you decide to do a console app or you are doing a unit test project the mapping configuration will be available from those projects as well.
In your Global.asax you will then call the method that sets all of your maps. See below:
File AutoMapperBootStrapper.cs
public static class AutoMapperBootStrapper
{
public static void BootStrap()
{
AutoMapper.CreateMap<Object1, Object2>();
// So on...
}
}
Global.asax on application start
just call
AutoMapperBootStrapper.BootStrap();
Now some people will argue against this method violates some SOLID principles, which they have valid arguments. Here they are for the reading.
Configuring Automapper in Bootstrapper violates Open-Closed Principle?
Update: The approach posted here is no more valid as SelfProfiler
has been removed as of AutoMapper v2.
I would take a similar approach as Thoai. But I would use the built-in SelfProfiler<>
class to handle the maps, then use the Mapper.SelfConfigure
function to initialize.
Using this object as the source:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
public string GetFullName()
{
return string.Format("{0} {1}", FirstName, LastName);
}
}
And these as the destination:
public class UserViewModel
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class UserWithAgeViewModel
{
public int Id { get; set; }
public string FullName { get; set; }
public int Age { get; set; }
}
You can create these profiles:
public class UserViewModelProfile : SelfProfiler<User,UserViewModel>
{
protected override void DescribeConfiguration(IMappingExpression<User, UserViewModel> map)
{
//This maps by convention, so no configuration needed
}
}
public class UserWithAgeViewModelProfile : SelfProfiler<User, UserWithAgeViewModel>
{
protected override void DescribeConfiguration(IMappingExpression<User, UserWithAgeViewModel> map)
{
//This map needs a little configuration
map.ForMember(d => d.Age, o => o.MapFrom(s => DateTime.Now.Year - s.BirthDate.Year));
}
}
To initialize in your application, create this class
public class AutoMapperConfiguration
{
public static void Initialize()
{
Mapper.Initialize(x=>
{
x.SelfConfigure(typeof (UserViewModel).Assembly);
// add assemblies as necessary
});
}
}
Add this line to your global.asax.cs file: AutoMapperConfiguration.Initialize()
Now you can place your mapping classes where they make sense to you and not worry about one monolithic mapping class.
For those of you who adhere to the following:
I did a combo between profiles and leveraging my ioc container:
IoC configuration:
public class Automapper : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly().BasedOn<Profile>().WithServiceBase());
container.Register(Component.For<IMappingEngine>().UsingFactoryMethod(k =>
{
Profile[] profiles = k.ResolveAll<Profile>();
Mapper.Initialize(cfg =>
{
foreach (var profile in profiles)
{
cfg.AddProfile(profile);
}
});
profiles.ForEach(k.ReleaseComponent);
return Mapper.Engine;
}));
}
}
Configuration example:
public class TagStatusViewModelMappings : Profile
{
protected override void Configure()
{
Mapper.CreateMap<Service.Contracts.TagStatusViewModel, TagStatusViewModel>();
}
}
Usage example:
public class TagStatusController : ApiController
{
private readonly IFooService _service;
private readonly IMappingEngine _mapper;
public TagStatusController(IFooService service, IMappingEngine mapper)
{
_service = service;
_mapper = mapper;
}
[Route("")]
public HttpResponseMessage Get()
{
var response = _service.GetTagStatus();
return Request.CreateResponse(HttpStatusCode.Accepted, _mapper.Map<List<ViewModels.TagStatusViewModel>>(response));
}
}
The trade-off is that you have to reference the Mapper by the IMappingEngine interface instead of the static Mapper, but that's a convention I can live with.
All of above solutions provide a static method to call (from app_start or any where) that it should call other methods to configure parts of mapping-configuration. But, if you have a modular application, that modules may plug in and out of application at any time, these solutions does not work. I suggest using WebActivator
library that can register some methods to run on app_pre_start
and app_post_start
any where:
// in MyModule1.dll
public class InitMapInModule1 {
static void Init() {
Mapper.CreateMap<User, UserViewModel>();
// other stuffs
}
}
[assembly: PreApplicationStartMethod(typeof(InitMapInModule1), "Init")]
// in MyModule2.dll
public class InitMapInModule2 {
static void Init() {
Mapper.CreateMap<Blog, BlogViewModel>();
// other stuffs
}
}
[assembly: PreApplicationStartMethod(typeof(InitMapInModule2), "Init")]
// in MyModule3.dll
public class InitMapInModule3 {
static void Init() {
Mapper.CreateMap<Comment, CommentViewModel>();
// other stuffs
}
}
[assembly: PreApplicationStartMethod(typeof(InitMapInModule2), "Init")]
// and in other libraries...
You can install WebActivator
via NuGet.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With