I have a problem with Autofac and Automapper.
In my WebApi I have this code to register my classes:
public static void Initialize(HttpConfiguration config, IContainer container)
{
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
_container = container;
}
private static IContainer RegisterServices(ContainerBuilder builder)
{
//Register your Web API controllers.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterModule<MapperInstaller>();
builder.RegisterType<VurpEntities>();
builder.RegisterType<EntityFrameworkUnitOfWork>().As<IUnitOfWork>();
builder.RegisterType<AppService>().As<IAppService>();
builder.RegisterType<AppRepository>().As<IAppRepository>();
builder.RegisterType<BusinessService>().As<IBusinessService>();
builder.RegisterType<BusinessRepository>().As<IBusinessRepository>();
//...
//all my types are registered here
//...
//Set the dependency resolver to be Autofac.
_container = builder.Build();
return _container;
}
I created a Resolver to map a special field of my object:
public class ProductTypeResolver : IMemberValueResolver<ProductDto, Product, string, Enumeration.ProductType>
{
public ProductType Resolve(ProductDto source, Product destination, string sourceMember, ProductType destMember, ResolutionContext context)
{
Enum.TryParse(sourceMember, out destMember);
return destMember;
}
}
I have a Profile that map DTO object in Entity object:
CreateMap<ProductDto, Product>()
.ForMember(dest => dest.Category, opt => opt.Ignore())
.ForMember(dest => dest.Feature, opt => opt.Ignore())
opts.ResolveUsing<ProductTypeResolver, string>(src => src.type));
When I try to map the object Product p = _mapper.Map<Product>(productDto); I receive the error:
An exception of type 'System.ObjectDisposedException' occurred in
Autofac.dll but was not handled in user code
Additional information: This resolve operation has already ended. When
registering components using lambdas, the IComponentContext 'c'
parameter to the lambda cannot be stored. Instead, either resolve
IComponentContext again from 'c', or resolve a Func<> based factory to
create subsequent components from.
Anyone can help me?
I solved with this:
builder.Register(c =>
{
//This resolves a new context that can be used later.
var context = c.Resolve<IComponentContext>();
var config = context.Resolve<MapperConfiguration>();
return config.CreateMapper(context.Resolve);
})
.As<IMapper>()
.InstancePerLifetimeScope();
Related
I am trying to map from a db-model to a view model. For one property I need a custom value resolver.
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<Model.Db.Kontoauszug, KontoauszugDetailViewModel>()
.ForMember(dest => dest.IsTeamleiter, opt => opt.MapFrom<KontoauszugIsTeamleiterResolver>());
});
var mapper = new Mapper(config);
return mapper.Map<KontoauszugDetailViewModel>(kontoauszug);
The custom value resolver is dependent on a service as you can see here:
public class KontoauszugIsTeamleiterResolver : IValueResolver<Model.Db.Kontoauszug, KontoauszugDetailViewModel, bool>
{
private readonly ISysParamService sysParamService;
public KontoauszugIsTeamleiterResolver(ISysParamService sysParamService)
{
this.sysParamService = sysParamService;
}
public bool Resolve(Model.Db.Kontoauszug source, KontoauszugDetailViewModel destination, bool destMember, ResolutionContext context)
{
var teamleiter = this.sysParamService.GetParamValueAs<string>(KontoauszugSysParamConst.KONTOAUSZUG_TEAMLEITER_MANUMMERN).Split(";").ToList();
return teamleiter.Contains(source.MitarbeiterNr);
}
}
Unfortuantely, when running this code throws a exception that the valueresolver does not have a parameterless constructor.
I'm using the standard .net core dependency injection and in my Startup.cs I'm registering the automappers via
services.AddAutoMapper(typeof(Startup));
I've also tried to explicitly register the value resolver:
services.AddScoped<IValueResolver<Model.Db.Kontoauszug, KontoauszugDetailViewModel, bool>, KontoauszugIsTeamleiterResolver>();
But it does not work.
What am I doing wrong that this exception is thrown?
Thank in advance
After previous question I have a simple implementation of IValueResolver
public class FileLinkResolver : IValueResolver<Configuration, ConfigurationDto, string>
{
private readonly IFileStorage _fileStorage;
public FileLinkResolver(IFileStorage fileStorage)
{
_fileStorage = fileStorage;
}
public string Resolve(Configuration source, ConfigurationDto destination, string destMember, ResolutionContext context)
{
return _fileStorage.GetShortTemporaryLink(source.Path);
}
}
and simple mapping profile
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Configuration, ConfigurationDto>()
.ForMember(dest => dest.FilePath, opt => opt.MapFrom<FileLinkResolver>());
}
}
For production it works as expected when the following setup
services.AddTransient<IFileStorage>(...);
services.AddAutoMapper();
is used and then in controller IMapper is injected.
In unit test I try to stub the mapper
var mapperStub = new Mapper(new MapperConfiguration(map => map.AddProfile(new MappingProfile())));
and when I run tests for method witch should return mapped dto, I got
AutoMapper.AutoMapperMappingException : Error mapping types.
Mapping types:
Configuration -> ConfigurationDto
DataAccess.Models.Configuration -> Dto.ConfigurationDto
Type Map configuration:
Configuration -> ConfigurationDto
DataAccess.Models.Configuration -> Dto.ConfigurationDto
Destination Member:
FilePath
---- System.MissingMethodException : No parameterless constructor defined for this object.
I've tried to add parameterless constructor to FileLinkResolver but then, NullReferenceException
This is the question: how to resolve dependencies for ValueResolver
In the current example, the mapper is unable to resolve IFileStorage dependency when testing.
Change the way the mapper is created to more closely match how it is done at run-time.
IServiceCollection services = new ServiceCollection();
//mocking service using MOQ
var mock = Mock.Of<IFileStorage>(_ =>
_.GetShortTemporaryLink(It.IsAny<string>()) == "fake link"
);
//adding mock to service collection.
services.AddTransient<IFileStorage>(sp => mock);
//adding auto mapper with desired profile;
services.AddAutoMapper(typeof(MappingProfile));
//...add other dependencies as needed to service collection.
//...
//build provider
IServiceProvider serviceProvider = service.BuilderServiceProvider();
//resolve mapper
IMapper mapperStub = serviceProvider.GetService<IMapper>();
//Or resolve subject under test where mapper is to be injected
SubjectClass subject = serviceProvider.GetService<SubjectClass>();
//assuming ctr: public SubjectClass(IMapper mapper, .....) { ... }
Now technically this is not mocking the value resolver. It mocks the dependencies of the resolver, and uses an actual resolver from the profile. But this should provide the desired behavior when testing the target.
I have the following AutoMapper profile:
public class AutoMapperBootstrap : Profile
{
protected override void Configure()
{
CreateMap<Data.EntityFramework.RssFeed, IRssFeed>().ForMember(x => x.NewsArticles, opt => opt.MapFrom(y => y.RssFeedContent));
CreateMap<IRssFeedContent, Data.EntityFramework.RssFeedContent>().ForMember(x => x.Id, opt => opt.Ignore());
}
}
And I am initializing it like this:
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new AutoMapperBootstrap());
});
container.RegisterInstance<IMapper>("Mapper", config.CreateMapper());
When I try to inject it in my constructor:
private IMapper _mapper;
public RssLocalRepository(IMapper mapper)
{
_mapper = mapper;
}
I recieve the following error:
The current type, AutoMapper.IMapper, is an interface and cannot be
constructed. Are you missing a type mapping?
How can I initialize the AutoMapper profile properly with Unity, so that I can use the mapper anywhere through DI?
In your example you are creating named mapping:
// named mapping with "Mapper name"
container.RegisterInstance<IMapper>("Mapper", config.CreateMapper());
But how your resolver will know about this name?
You need to register you mapping without name:
// named mapping with "Mapper name"
container.RegisterInstance<IMapper>(config.CreateMapper());
It will map your mapper instance to IMapper interface and this instance will be returned on resolving interface
You can register it like so:
container.RegisterType<IMappingEngine>(new InjectionFactory(_ => Mapper.Engine));
Then you can inject it as IMappingEngine.
private IMappingEngine_mapper;
public RssLocalRepository(IMappingEnginemapper)
{
_mapper = mapper;
}
More information found here:
https://kalcik.net/2014/08/13/automatic-registration-of-automapper-profiles-with-the-unity-dependency-injection-container/
I'm trying to use automapper in combination with ninject and a generic interface, abstract class. However it doesn't seem to work.
Below you will find the code which I'm trying to use. What am I missing?
IMapper
public interface IMapper<in TTypeFrom, TTypeTo>
{
TTypeTo Map(TTypeFrom typeFrom);
List<TTypeTo> Map(IEnumerable<TTypeFrom> itemToMap);
}
public abstract class Mapper<TTypeFrom, TTypeto> : IMapper<TTypeFrom, TTypeto>
{
private readonly IMappingEngine _mappingEngine;
private readonly IConfiguration _configuration;
protected Mapper(IMappingEngine mappingEngine, IConfiguration configuration)
{
_mappingEngine = mappingEngine;
_configuration = configuration;
_configuration.CreateMap<TTypeFrom, TTypeto>();
}
public TTypeto Map(TTypeFrom typeFrom)
{
return Map<TTypeFrom, TTypeto>(typeFrom);
}
protected TTo Map<TFrom, TTo>(TFrom itemToMap)
{
return _mappingEngine.Map<TFrom, TTo>(itemToMap);
}
public List<TTypeto> Map(IEnumerable<TTypeFrom> itemToMap)
{
return Map<TTypeFrom, TTypeto>(itemToMap);
}
protected List<TTo> Map<TFrom, TTo>(IEnumerable<TFrom> itemsToMap)
{
return itemsToMap.Select(Map<TFrom, TTo>).ToList();
}
}
CategoryRepresentationMapper
public interface ICategoryRepresentationMapper : IMapper<CategoryRepresentation, CategoryRepresentationDto>
{
}
public class CategoryRepresentationMapper : Mapper<CategoryRepresentation, CategoryRepresentationDto>, ICategoryRepresentationMapper
{
public CategoryRepresentationMapper(IMappingEngine mappingEngine, IConfiguration configuration) : base(mappingEngine, configuration)
{
}
}
Setting things up with ninject
IKernel kernel = new StandardKernel();
Mapper.Initialize(map =>
{
map.ConstructServicesUsing(f => kernel.Get(f));
});
kernel.Bind<IMappingEngine>().ToMethod(x => Mapper.Engine);
kernel.Bind<IConfigurationProvider>().ToMethod(x => Mapper.Engine.ConfigurationProvider);
kernel.Bind<IConfiguration>().ToMethod(x => Mapper.Configuration);
kernel.Bind(
x =>
x.FromAssemblyContaining(typeof(IMapper<,>))
.SelectAllClasses()
.InheritedFrom(typeof(IMapper<,>))
.BindAllInterfaces());
var categoryRepresentationMapper = kernel.Get<ICategoryRepresentationMapper>();
I get the following error:
Ninject.ActivationException occurred
HResult=-2146233088
Message=Error activating ICategoryRepresentationMapper
No matching bindings are available, and the type is not self-bindable.
Activation path:
1) Request for ICategoryRepresentationMapper
Suggestions:
1) Ensure that you have defined a binding for ICategoryRepresentationMapper.
2) If the binding was defined in a module, ensure that the module has been loaded into the kernel.
3) Ensure you have not accidentally created more than one kernel.
4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name.
5) If you are using automatic module loading, ensure the search path and filters are correct.
Edit
If i do this, it will work, BUT I don't want to bind ICategoryRepresentationMapper explicitly. It must be generic since I will have numerous mappers.
IKernel kernel = new StandardKernel();
Mapper.Initialize(map =>
{
map.ConstructServicesUsing(f => kernel.Get(f));
});
kernel.Bind<IMappingEngine>().ToMethod(x => Mapper.Engine);
kernel.Bind<IConfigurationProvider>().ToMethod(x => Mapper.Engine.ConfigurationProvider);
kernel.Bind<IConfiguration>().ToMethod(x => Mapper.Configuration);
// This line below will actualy work but it isn't generic
kernel.Bind(
x =>
x.FromAssemblyContaining< ICategoryRepresentationMapper>()
.SelectAllClasses()
.InheritedFrom(typeof(IMapper<,>))
.BindAllInterfaces());
It seems I found an answer to this problem. The assemblies were to look at where wrong.
Instead of doing this:
kernel.Bind(
x =>
x.FromAssemblyContaining(typeof(IMapper<,>))
.SelectAllClasses()
.InheritedFrom(typeof(IMapper<,>))
.BindAllInterfaces());
I needed to do this and It works:
var codeBase = Assembly.GetExecutingAssembly().CodeBase;
var uri = new UriBuilder(codeBase);
var path = Uri.UnescapeDataString(uri.Path);
_kernel.Bind(
x =>
x.FromAssembliesInPath(Path.GetDirectoryName(path))
.SelectAllClasses()
.InheritedFrom(typeof(IMapper<,>))
.BindAllInterfaces());
What is the proper way to inject AutoMapper to other layers?
I read this blog post , but this code cause exception below
An exception of type 'AutoMapper.AutoMapperMappingException' occurred in AutoMapper.dll but was not handled in user code
when try mapping in service layer.
List<StudentViewModel> list2 = _mapper.Map<List<StudentViewModel>>(list);
My AutoFac configuration like below:
public static class DependencyRegistration
{
public static void Config()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterType<TypeMapFactory>().As<ITypeMapFactory>();
builder.RegisterType<ConfigurationStore>().As<ConfigurationStore>().WithParameter("mappers", MapperRegistry.Mappers).SingleInstance();
builder.Register((ctx, t) => ctx.Resolve<ConfigurationStore>()).As<IConfiguration>().As<IConfigurationProvider>();
builder.RegisterType<MappingEngine>().As<IMappingEngine>();
//...
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
.netcore 3
Autofac 5.1.2
AutoMapper 9.0.0
AutoMapperProfiles -> My profile name
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<AutoMapperProfiles>().As<Profile>();
builder.Register(c => new MapperConfiguration(cfg =>
{
foreach (var profile in c.Resolve<IEnumerable<Profile>>())
{
cfg.AddProfile(profile);
}
})).AsSelf().SingleInstance();
builder.Register(c => c.Resolve<MapperConfiguration>().CreateMapper(c.Resolve)).As<IMapper>().InstancePerLifetimeScope();
}
It seems that you need to use the IConfiguration object that is registered in the container to create the maps like this:
var configuration = container.Resolve<IConfiguration>();
configuration.CreateMap<Student, StudentViewModel>();
I think that you should be doing this at the start of your application.
Here is a better way (IMO) to configure things in the Config method:
public static void Config()
{
var configuration_store = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers);
var mapping_engine = new MappingEngine(configuration_store);
configuration_store.CreateMap<Student, StudentViewModel>();
var builder = new ContainerBuilder();
builder.RegisterInstance(mapping_engine).As<IMappingEngine>();
//...
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
I am assuming in the last example, that your classes need access only to IMappingEngine (and not IConfiguration), since your should already setup all mappings in the Config method (or some other configuration method at application startup).