var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<SomeSourceModel, SomeDestinationModel>();
});
config.AssertConfigurationIsValid();
var mapper = config.CreateMapper();
I am repeating these code in the project. Thinking to create a common interface IMapper so that I can invoke whenever it is needed to be used.
The solution I create is
private IMapper Mapper(TSource source, TDestination dest)
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<source, dest>();
});
config.AssertConfigurationIsValid();
return config.CreateMapper();
}
It doesn't work. The problem is I can't pass source model and destination model as parameters in this way. How to solve this?
Update 1:
As #12seconds mentioned, I start initializing MapperConfigration in Global.asax.cs
In App_Start folder, I created
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<SourceModel1, DestinationModel1>();
CreateMap<SourceModel2, DestinationModel2>();
CreateMap<SourceModel3, DestinationModel3>();
CreateMap<SourceModel4, DestinationModel4>();
CreateMap<SourceModel5, DestinationModel5>();
Mapper.AssertConfigurationIsValid();
}
}
In Global.asax.cs
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x =>
{
x.AddProfile<MappingProfile>();
});
}
}
And then I tried to call AutoMapperConfiguration.Configure(); in several places. When I start running the App, I got same error messages:
Mapper not initialized. Call Initialize with appropriate
configuration. If you are trying to use mapper instances through a
container or otherwise, make sure you do not have any calls to the
static Mapper.Map methods, and if you're using ProjectTo or
UseAsDataSource extension methods, make sure you pass in the
appropriate IConfigurationProvider instance.
Where I suppose to call AutoMapperConfiguration.Configure();? Did I miss something?
Version 5.0.x +
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x =>
{
x.AddProfile<MappingProfile>();
});
Mapper.AssertConfigurationIsValid();
}
}
The problem solved. Mapper.AssertConfigurationIsValid(); should be executed after Mapper initialized.
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x =>
{
x.AddProfile<MappingProfile>();
});
Mapper.Configuration.AssertConfigurationIsValid();
}
}
Related
I´m trying to use Automapper with Dependency Injection configuration on a n-layer application.
public class ApplicationMapping : Profile
{
public ApplicationMapping()
{
RegisterMappings();
Mapper.AssertConfigurationIsValid();
}
private void RegisterMappings()
{
CreateMap<IEnumerable<App>, ListAppsDto>()
.ForMember(dest => dest.Apps,
opt => opt.MapFrom(src =>
Mapper.Map<IEnumerable<App>, List<App>>(src.ToList())
)
);
}
}
This class is inside my Application dll, where I put my services and DTOs. Also in this dll, I have an extension method to register the mapping:
public static class MappingServiceExtension
{
public static void AddApplicationMappings(this IServiceCollection services)
{
var mapperConfig = new MapperConfiguration(config =>
{
config.AddProfile<ApplicationMapping>();
});
IMapper mapper = mapperConfig.CreateMapper();
services.AddSingleton(mapper);
}
}
Then in my WebAPI project, on the Startup.cs class I put:
services.AddApplicationMappings();
And I use it normally with DI in my services:
public class AppService : IAppService
{
private readonly IAppRepository _appRepository;
private readonly IMapper _mapper;
public TruckService(IAppRepository appRepository, IMapper mapper)
{
_appRepository = appRepository;
_mapper = mapper;
}
}
I would like to use like this. But I'm getting an exception when the Mapper.AssertConfigurationIsValid(); line runs, saying that:
'Mapper not initialized. Call Initialize with appropriate configuration. If you are trying to use mapper instances through a container or otherwise, make sure you do not have any calls to the static Mapper.Map methods, and if you're using ProjectTo or UseAsDataSource extension methods, make sure you pass in the appropriate IConfigurationProvider instance.'
What am I missing here? The problem seems to be in the Mapper.Map<IEnumerable<App>, List<App>>(src.ToList()) line of code.
But how can I get an instance of the Mapper there without using the static Mapper?
Mapper.AssertConfigurationIsValid();
This calls the static IMapper instance which is used in situations where you don’t use dependency injection. Since you never set up the static mapper, using it there will fail.
What you want to do instead is call AssertConfigurationIsValid on the actual mapper instance, the one that you are registering as a singleton. So you should remove the assert from the mapper profile and instead call it within your AddApplicationMappings method:
IMapper mapper = mapperConfig.CreateMapper();
mapper.AssertConfigurationIsValid();
services.AddSingleton(mapper);
Try using AddAutoMapper from AutoMapper.Extensions.Microsoft.DependencyInjection which you can add as a NuGet package.
So, you'd completely remove the MappingServiceExtension class, and then in Startup.cs add these two lines:
AutoMapper.Mapper.Reset();
services.AddAutoMapper(typeof(ApplicationMapping).Assembly);
I forgot the exact reason, but when using AutoMapper in across multiple projects/assemblies, you need to register it for DI this way. Read more here.
Similar to what #johnluke.laue suggested. In AddApplicationMappings simply replace the code with the following:
services.AddAutoMapper(config =>
{
config.AddProfile<ApplicationMapping>();
});
The above will automatically add the IMapper to the DI. In addition, modify the RegisterMappings function as below. You don't need to explicitly map the IEnumerable<T>. It will be mapped implicitly if the source/destination mappings exist.
private void RegisterMappings()
{
CreateMap<IEnumerable<App>, ListAppsDto>()
.ForMember(dest => dest.Apps, opt => opt.MapFrom(src => src.ToList());
}
It would be helpful to see the actual App and ListAppDto classes, as you don't explicitly need the above mappings. I hope this helps
I am using AutoMapper profiles for mapping entities. In one of the mapping profiles I need to call a service method. I m trying to inject the service using profile constructor but then I m not able to register/add it. Any ideas how can I handle this?
public class HistoryProfile : Profile
{
private readonly MappingService _mappingService;
public HistoryProfile(MappingService mappingService)
{
_mappingService = mappingService;
this.CreateMap<HistoryCHR, History>()
.ForMember(h => h.BirthDate, hisChr => hisChr.MapFrom(x => x.DateOfBirth))
.....................
}
}
private static void InitializeSpecificProfiles()
{
Mapper.Initialize(
cfg =>
{
cfg.AddProfile(new HistoryProfile());
});
}
I've upgraded Automapper from 4.2.1 to 5.0.0. I'm using the static API in a WebApi2 project and I'm trying to get the mapping to work, so I tried following this SO answer.
So I changed the code to the following:
public static class AutoMapping
{
public static void Config()
{
Mapper.Initialize(main =>
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.CreateMap<MyModel, MyDto>().ReverseMap();
});
config.AssertConfigurationIsValid();
});
}
}
The above is called from Global.asax.
However, I get exception:
Mapper not initialized. Call Initialize with appropriate configuration.
What is the correct way to initialize Automapper, and do I need to change all my controllers now for mapping?
EDIT1
Firstly, the code above must be:
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.CreateMap<MyModel, MyDto>().ReverseMap();
});
Mapper.Configuration.AssertConfigurationIsValid();
Secondly, the problem might be in the following method which I use to ignore missing properties:
public static IMappingExpression<TSource, TDestination> IgnoreUnmapped<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
var typeMap = Mapper.Configuration.FindTypeMapFor<TSource, TDestination>();
if (typeMap != null)
{
foreach (var unmappedPropertyName in typeMap.GetUnmappedPropertyNames())
{
expression.ForMember(unmappedPropertyName, opt => opt.Ignore());
}
}
return expression;
}
I'm assuming 'Mapper.Configuration' is not yet configured because the above method is called within Initialize which configures the mapping.
Is there an existing method within Automapper itself which I can use instead of the above?
EDIT2
Would the following syntax work?
cfg.CreateMap<MyModel, MyDto>().ReverseMap().ForAllMembers(opt => opt.Ignore());
Actually your code does nothing now. You have to change it like this:
public static class AutoMapping
{
public static void Config()
{
Mapper.Initialize(cfg =>
{
cfg.CreateMissingTypeMaps = true;
cfg.CreateMap<MyModel, MyDto>().ReverseMap();
});
Mapper.AssertConfigurationIsValid();
}
}
UPD (after EDIT1):
Try to use expression.TypeMap instead of Mapper.Configuration.FindTypeMapFor<TSource, TDestination>()
Maybe this helps:
Setup the Configuration:
var config = new MapperConfiguration(cfg => cfg.CreateMap<Order, OrderDto>());
Then where the mapping should take place:
var mapper = new Mapper(config);
OrderDto dto = mapper.Map<OrderDto>(order);
You could also expose the 'config' as a static property, and use that in your project. There's also an alternative to create a static 'Mapper' property that is configured. Then you can use that static 'Mapper' property in your project.
I am using auto mapping first time.
I am working on c# application and I want to use auto mapper.
(I just want to know how to use it, so I don't have asp.net app neither MVC app.)
I have three class library projects.
I want to write transfer process in the service project.
So I want to know how and where should I configure the Auto Mapper ?
So based on Bruno's answer here and John Skeet's post about singletons I came up with the following solution to have this run only once and be completely isolated in class library unlike the accepted answer which relies on the consumer of the library to configure the mappings in the parent project:
public static class Mapping
{
private static readonly Lazy<IMapper> Lazy = new Lazy<IMapper>(() =>
{
var config = new MapperConfiguration(cfg => {
// This line ensures that internal properties are also mapped over.
cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.GetMethod.IsAssembly;
cfg.AddProfile<MappingProfile>();
});
var mapper = config.CreateMapper();
return mapper;
});
public static IMapper Mapper => Lazy.Value;
}
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Source, Destination>();
// Additional mappings here...
}
}
Then in your code where you need to map one object to another you can just do:
var destination = Mapping.Mapper.Map<Destination>(yourSourceInstance);
NOTE: This code is based on AutoMapper 6.2 and it might require some tweaking for older versions of AutoMapper.
You can place the configuration anywhere:
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x =>
{
x.AddProfile<MyMappings>();
});
}
}
public class MyMappings : Profile
{
public override string ProfileName
{
get { return "MyMappings"; }
}
protected override void Configure()
{
......
}
But it has to be called by the application using the libraries at some point:
void Application_Start()
{
AutoMapperConfiguration.Configure();
}
Nobody outside of your library has to configure AutoMapper
I recommend that you use the instance based approach using an IMapper. That way no one outside your library has to call any configuration method. You can define a MapperConfiguration and create the mapper from there all inside the class library.
var config = new MapperConfiguration(cfg => {
cfg.AddProfile<AppProfile>();
cfg.CreateMap<Source, Dest>();
});
IMapper mapper = config.CreateMapper();
// or
IMapper mapper = new Mapper(config);
var dest = mapper.Map<Source, Dest>(new Source());
Marko's answer is correct.
We can also go by a below simple solution.
public static class ObjectMapper
{
public static IMapper Mapper
{
get
{
return AutoMapper.Mapper.Instance;
}
}
static ObjectMapper()
{
CreateMap();
}
private static void CreateMap()
{
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<SourceClass, DestinationClass>();
});
}
}
And we can use it like.
public class SourceClass
{
public string Name { get; set; }
}
public class DestinationClass
{
public string Name { get; set; }
}
SourceClass c1 = new SourceClass() { Name = "Mr.Ram" };
DestinationClass c2 = ObjectMapper.Mapper.Map<DestinationClass>(c1);
I have used the Patel Vishal's solution and customized it to my needs.
It's a generic class which makes sure only one instance of mapping is saved in memory per object mapping.
TModel - is a DTO object
TData - is a Database table object in Entity Framework
DTO.IBaseModel - is a base class for DTO object which has one property: ID
IBaseModel - is a base class for the entity framework database entity with ID property only
public static class ObjectMapper<TModel, TData>
where TModel : class, DTO.IBaseModel, new()
where TData : class, IBaseModel, new()
{
private static readonly MapperConfiguration _mapperConfiguration;
public static IMapper Mapper => new Mapper(_mapperConfiguration);
static ObjectMapper()
{
_mapperConfiguration ??= CreateMap();
}
private static MapperConfiguration CreateMap()
{
return new (cfg =>
{
cfg.CreateMap<TData, TModel>();
});
}
}
I am using this class in a BaseService<TData, TModel> (Service/Repository pattern) as such:
public virtual TModel Convert(TData t)
{
return ObjectMapper<TModel, TData>.Mapper.Map<TModel>(t);
}
As you can see, it's a virtual method. Mapping can be overwritten, if customization required by the inheriting Service.
I have come across this kind of requirement as well. What I have done in .Net 6.0 is, I create a library project and create the profile class:
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<Entity, Dto>();
CreateMap<Dto, Entity>();
......
}
}
while in the api or web project, I just create a child class to inherit from the profile above, and register it in startup.cs services.AddAutoMapper(typeof(Startup));.
Whats the best way to setup a mock expection for the Map function in AutoMapper.
I extract the IMapper interface so I can setup expects for that interface. My mapper has dependencies, so I have to pass those in to the mapper.
What happens when I create 2 instances of my mapper class, with 2 different dependency implementations? I asume that both mappers will use the same dependency instance, since the AutoMapper map is static. Or AutoMapper might even throw an exception because I try to setup 2 different maps with the same objects.?
Whats the best way to solve this?
public interface IMapper {
TTarget Map<TSource, TTarget>(TSource source);
void ValidateMappingConfiguration();
}
public class MyMapper : IMapper {
private readonly IMyService service;
public MyMapper(IMyService service) {
this.service = service
Mapper.CreateMap<MyModelClass, MyDTO>()
.ForMember(d => d.RelatedData, o => o.MapFrom(s =>
service.getData(s.id).RelatedData))
}
public void ValidateMappingConfiguration() {
Mapper.AssertConfigurationIsValid();
}
public TTarget Map<TSource, TTarget>(TSource source) {
return Mapper.Map<TSource, TTarget>(source);
}
}
You don't need to mock AutoMapper, you can just inject the real one as explained here:
var myProfile = new MyProfile();
var configuration = new MapperConfiguration(cfg => cfg.AddProfile(myProfile));
IMapper mapper = new Mapper(configuration);
You can inject this mapper in your unit tests. The whole point of using tools like AutoMapper is for you not having to write a lot of mapping code. If you mock AutoMapper you'll end up having to do that.
Whats the best way to setup a mock expection for the Map function in AutoMapper[?]
Here's one way:
var mapperMock = new Mock<IMapper>();
mapperMock.Setup(m => m.Map<Foo, Bar>(It.IsAny<Foo>())).Returns(new Bar());
http://richarddingwall.name/2009/05/07/mocking-out-automapper-with-dependency-injection/
Points out another way of handling dependencies to AutoMapper, which basically will replace my attempt to extract my own IMapper interface. Instead I will attempt to use the existing IMappingEngine as dependency for my classes, to see if that works.
What you need to do is setup AutoMapper like this (StructureMap is IoC). Then you can make your services dependent on IMappingEngine. From there mocking it should be very easy.
public class AutoMapperConfigurationRegistry : Registry
{
public AutoMapperConfigurationRegistry()
{
ForRequestedType<Configuration>()
.CacheBy(InstanceScope.Singleton)
.TheDefault.Is.OfConcreteType<Configuration>()
.CtorDependency<IEnumerable<IObjectMapper>>().Is(expr => expr.ConstructedBy(MapperRegistry.AllMappers));
ForRequestedType<ITypeMapFactory>().TheDefaultIsConcreteType<TypeMapFactory>();
ForRequestedType<IConfigurationProvider>()
.TheDefault.Is.ConstructedBy(ctx => ctx.GetInstance<Configuration>());
ForRequestedType<IConfiguration>()
.TheDefault.Is.ConstructedBy(ctx => ctx.GetInstance<Configuration>());
}
}
The reason you have to invoke automapper config is because, the UNIT Test cases instance runs outside of main application start up files/configs. Hence the auto mapper configuration has to be called and setup before any unit tests start to run. Ideally you place it in TestInitialize methods.
Here is an example of how I provide an instance of AutoMapper as default Mock for IMapper, using AutoFixture and Moq:
Thanks Lucian Bargaoanu for this hint: Actually you can use cfg.AddMaps(params Assembly[]) and Automapper will search for profiles
Create an ICustomization
public class MapperCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Register<IMapper>(() =>
{
var configuration = new MapperConfiguration(cfg =>
{
cfg.AddMaps(
Assembly.Load("BookSharing.Application"),
Assembly.Load("BookSharing.Infrastructure"));
});
return new Mapper(configuration);
});
}
}
Register the customization
fixture.Customize(new MapperCustomization());
following #Dorin Baba answer, I created a useful generic class that can be used to inject any custom mapper in the unit tests
public class MapperCustomization<T> : ICustomization
where T : class
{
public void Customize(IFixture fixture)
{
fixture.Register<IMapper>(() =>
{
var config = new MapperConfiguration(cfg =>
{
cfg.AddMaps(
typeof(T).Assembly);
});
return new Mapper(config);
});
}
}