Generic mapper instead of having numerous individual mappers
I am using ASP.NET MVC 3
with Razor
and Autofac
for dependency injection.
I am thinking of creating a generic mapper. Currently开发者_Python百科 I am using AutoMapper
for the mapping between my domain and view model. It can be any mapping framework, but I am using AutoMapper .
Here is my IMapper
interface:
public interface IMapper
{
object Map(object source, Type sourceType, Type destinationType);
}
I then have an IBankMapper interface that implements this IMapper interface. The reason why I did it like this is because I can have many different mappers. Using dependency injection I can know what instance I can inject. So for IBankMapper I will inject BankMapper, ICategoryMapper I will inject CategoryMapper.
IBankMapper
interface:
public interface IBankMapper : IMapper
{
}
BankMapper
class:
public class BankMapper : IBankMapper
{
static BankMapper()
{
Mapper.CreateMap<Bank, EditBankViewModel>();
Mapper.CreateMap<EditBankViewModel, Bank>();
}
public object Map(object source, Type sourceType, Type destinationType)
{
return Mapper.Map(source, sourceType, destinationType);
}
}
As the program grows so will the mapper classes. Is there a way that I can create a generic mapper, one that can be used in the whole application? Is this possible?
There is absolutely no need for you to create any mapper classes at all.
All you need to do is be sure that you call Mapper.CreateMap
at the beginning of your application. You can then use Mapper.Map
to do your mapping.
Typically, I create a static class with a single method to handle the creation of my Maps. It looks something like:
public static class Transformers
{
public static void Register()
{
Mapper.CreateMap<Bank, EditBankViewModel>();
Mapper.CreateMap<EditBankViewModel, Bank>();
Mapper.CreateMap<Account, EditAccountViewModel>();
Mapper.CreateMap<EditAccountViewModel, Account>();
// And so on. You can break them up into private methods
// if you have too many.
}
}
I then call that method inside Global.asax do ensure that it runs when necessary. In any other spot in my application, I'm free to call Mapper.Map
for any of the configured mappings:
protected void Application_Start()
{
Transformers.Register();
}
It looks like you are creating these interfaces to be able to mock AutoMapper in your unit tests. You can simply use IMappingEngine
from AutoMapper for this purpose.
Your classes would have a dependency on IMappingEngine
which would be injected. IMappingEngine
has the same methods as the static class Mapper
to map your objects.
In your composition root you would configure the mapper and register the mapping engine with the DI container.
Something like this:
// composition root:
Mapper.CreateMap<Bank, EditBankViewModel>();
Mapper.CreateMap<EditBankViewModel, Bank>();
builder.Register(Mapper.Engine);
// your classes:
class YourClass
{
public YourClass(IMappingEngine mappingEngine)
{
}
}
As Daniel said, Automapper is already a generic mapper. If you're worried about the direct dependency to automapper in your code, you can hide it behind a little facade, e.g.:
public interface IMapper
{
TDestination Map<TSource, TDestination>(TSource source);
}
public class MyMapper :IMapper
{
public TDestination Map<TSource, TDestination>(TSource source)
{
AutoMapper.Mapper.CreateMap<TSource, TDestination>();
return AutoMapper.Mapper.Map<TSource, TDestination>(source);
}
}
This can also help you to model the mapper as an injectable dependency
精彩评论