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).
Related
I've upgraded to the latest version of AutoMapper (9.0) and I've changed the static configuration to:
public static IMapper RegisterAutoMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<MyModel MyDto>;
//etc...
});
var mapper = config.CreateMapper();
return mapper;
}
Using the previous static API I used to do the following in Global.asax:
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
AutoMapping.Map();
}
WebApiConfig.Register registers the routes and also Autofac
How do I register AutoMapper with Autofac because currently I'm getting compiler errors on such lines:
var myDto = Mapper.Map<MyModel>(model);
And the compiler error:
An object reference is required for the non-static field, method, or property 'Mapper.Map(object)'
Here's one I made earlier:
public class YourAutofacModule : Module
{
protected override void Load(ContainerBuilder builder)
{
//Also register any custom type converter/value resolvers
builder.RegisterType<CustomValueResolver>().AsSelf();
builder.RegisterType<CustomTypeConverter>().AsSelf();
builder.Register(context => new MapperConfiguration(cfg =>
{
cfg.CreateMap<MyModel MyDto>;
//etc...
})).AsSelf().SingleInstance();
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();
}
}
In the global.asax.cs
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
var builder = new ContainerBuilder();
builder.RegisterModule<MyAutofacModule>();
// Register anything else needed
var container = builder.Build();
// MVC resolver
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// API Resolver
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
}
Then all you need to do is inject IMapper
There is also a nuget-package that does all of that for you.
All you need to do is to call an extension method on the ContainerBuilder and pass in the assemblies, that should be scanned for AutoMapper types.
var containerBuilder = new ContainerBuilder();
containerBuilder.AddAutoMapper(typeof(MvcApplication).Assembly);
// more registrations here
You can find it here. You can find an official example in the AutoMapper docs as well.
Edit: There are samples for ASP.NET Core and Console-Applications here.
While I'm not familiar with Autofac myself, here is a recent article that descibes how to set up injection for automapper with Autofac.
Goodluck!
I have a new .NET Core Web API project that has the following projects structure:
API -> Business / Domain -> Infrastructure
The API is very thin with only the API methods. The Business / Domain layer has all my business logic. And finally, my Infrastructure layer has my DB classes using EF Core 2.0.
I know using .NET Core built-in Dependency Injection I can add a reference from the API project to the Infrastructure project, then add the following code in the StartUp.cs file:
services.AddDbContext<MyContext>(options => options.UseSqlServer(connectionString));
However, I would like to maintain a more traditional separation of concerns. So far I have added a module in my Infrastructure layer that attempts to make the registration like so:
builder.Register(c =>
{
var config = c.Resolve<IConfiguration>();
var opt = new DbContextOptionsBuilder<MyContext>();
opt.UseSqlServer(config.GetSection("ConnectionStrings:MyConnection:ConnectionString").Value);
return new MyContext(opt.Options);
}).AsImplementedInterfaces().InstancePerLifetimeScope();
The DBContext, however, is not getting registered. Any class that attempts to access the injected DBContext cannot resolve the parameter.
Is there a way to register the DBContext in a separate project using AuftoFac in a .NET Core Web API Project?
I use Autofac to register both HttpContextAccessor and DbContext.
builder
.RegisterType<HttpContextAccessor>()
.As<IHttpContextAccessor>()
.SingleInstance();
builder
.RegisterType<AppDbContext>()
.WithParameter("options", DbContextOptionsFactory.Get())
.InstancePerLifetimeScope();
DbContextOptionsFactory
public class DbContextOptionsFactory
{
public static DbContextOptions<AppDbContext> Get()
{
var configuration = AppConfigurations.Get(
WebContentDirectoryFinder.CalculateContentRootFolder());
var builder = new DbContextOptionsBuilder<AppDbContext>();
DbContextConfigurer.Configure(
builder,
configuration.GetConnectionString(
AppConsts.ConnectionStringName));
return builder.Options;
}
}
DbContextConfigurer
public class DbContextConfigurer
{
public static void Configure(
DbContextOptionsBuilder<AppDbContext> builder,
string connectionString)
{
builder.UseNpgsql(connectionString).UseLazyLoadingProxies();
}
}
I think that the problem is that you're trying to register MyContext() using AsImplementedInterfaces(). This is not how DbContext are getting registered usually. You should register and resolve class itself.
Another simple solution for Autofac version 4.8.1
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddControllersAsServices();
services.AddDbContext<MyContext>(options => options.UseSqlServer(Configuration.GetConnectionString("ConnectionStrings:MyConnection:ConnectionString")));
var builder = new ContainerBuilder();
builder.Populate(services);
//...
// Your interface registration
//...
builder.Build(Autofac.Builder.ContainerBuildOptions.None);
}
Here's an implementation I use - it mimics EF Core 3.1 registration with Autofac 4.9.4. Be sure to adjust scopes per your requirements.
public void RegisterContext<TContext>(ContainerBuilder builder)
where TContext : DbContext
{
builder.Register(componentContext =>
{
var serviceProvider = componentContext.Resolve<IServiceProvider>();
var configuration = componentContext.Resolve<IConfiguration>();
var dbContextOptions = new DbContextOptions<TContext>(new Dictionary<Type, IDbContextOptionsExtension>());
var optionsBuilder = new DbContextOptionsBuilder<TContext>(dbContextOptions)
.UseApplicationServiceProvider(serviceProvider)
.UseSqlServer(configuration.GetConnectionString("MyConnectionString"),
serverOptions => serverOptions.EnableRetryOnFailure(5, TimeSpan.FromSeconds(30), null));
return optionsBuilder.Options;
}).As<DbContextOptions<TContext>>()
.InstancePerLifetimeScope();
builder.Register(context => context.Resolve<DbContextOptions<TContext>>())
.As<DbContextOptions>()
.InstancePerLifetimeScope();
builder.RegisterType<TContext>()
.AsSelf()
.InstancePerLifetimeScope();
}
In the desired project you can create an extension method that adds the context to the collection
public static class MyDataExtensions {
public static IServiceCollection AddMyData(this IServiceCollection services) {
//...
services.AddDbContext<MyContext>(options => options.UseSqlServer(connectionString));
//...
}
}
with that then in your start up it is just a matter of calling the extension exposed from the other project
services.AddMyData();
//...other settings
The API project is the composition root, so it needs to know all the relevant dependencies anyway. At least with this extension you do not have to make direct reference of the used db context,
Has anyone tried to use the new View Injection from ASP.NET Core?
I'm trying to use straight forward as described on the documentation (https://docs.asp.net/en/latest/mvc/views/dependency-injection.html) but no success at all.
The unique diference from my implementation and the documentation is that I'm using AutoFac for DI.
When I try to use the injection on my view I get an exception that my Service has not been registered.
#inject Domain.Service.LevelService LevelService
Error Message:
ComponentNotRegisteredException: The requested service 'Domain.Service.LevelService' has not been registered. To avoid this exception, either register a component to provide the service, check for service registration using IsRegistered(), or use the ResolveOptional() method to resolve an optional dependency.
Btw, the service is correctly registered and can be accessed from the controller for example.
Edit to include Startup:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddMemoryCache();
services.AddSession();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
return new AutofacServiceProvider(DependencyInjection.RegisterServices(services));
}
Code of the method RegisterServices:
public static IContainer RegisterServices(IServiceCollection services)
{
// Create the container builder.
var builder = new ContainerBuilder();
var assembly = Assembly.GetExecutingAssembly();
assembly.GetTypes()
.Where(x => x.IsSubclassOf(typeof(ServiceInjectionModule)))
.ToList()
.ForEach(x =>
{
var t = (ServiceInjectionModule)Activator.CreateInstance(x, new object[] { true });
t.AddtoContainer(builder);
});
// Add automapper configurations
var mapperConfiguration = AutoMapperConfig.Configure();
var mapper = mapperConfiguration.CreateMapper();
builder.RegisterInstance(mapper).As<IMapper>();
// Populate default services
builder.Populate(services);
return builder.Build();
}
The problem is in the assembly scanning section you've written. It's much easier to use the built in functionality of AutoFac. Not sure your code is .Net Core just based on the fact you're not using GetTypeInfo. GetTypeInfo is backwards compatible so will work with .Net 4.x
public static IContainer RegisterServices(IServiceCollection services)
{
// Create the container builder.
var builder = new ContainerBuilder();
var assembly = Assembly.GetExecutingAssembly();
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.GetTypeInfo().IsSubclassOf(typeof(ServiceInjectionModule)))
.AsSelf();
// Add automapper configurations
var mapperConfiguration = AutoMapperConfig.Configure();
var mapper = mapperConfiguration.CreateMapper();
builder.RegisterInstance(mapper).As<IMapper>();
// Populate default services
builder.Populate(services);
return builder.Build();
}
OK, I solved the problem.
Well, I didn't paid attention and seems that no one too :p.
The problem is that I'm trying to inject an instance and not an interface. Just changed the implementation and everything started working.
Final code:
#inject Domain.Service.Interfaces.ILevelService LevelService
I'm applying asp.net identity with repository pattern and having some trouble.
In Unity, I see it register as below (it's runned):
container.RegisterType<IUserStore<IdentityUser, Guid>, UserStore>(new TransientLifetimeManager());
container.RegisterType<RoleStore>(new TransientLifetimeManager());
Now I want to register by using Autofac, especial the first register code but I can't find anything about this.
If you have another solution for apply asp net identity with repository pattern.
Some examples I found on the Internet.
1. Registering roles, users, associations
https://github.com/kirill-vinnichek/BerezovskyVinnichek.Wunderlist/blob/master/Wunderlist/Epam.Wunderlist.Web/App_Start/AutofacConfig.cs
public static class AutofacConfig
{
public static void Config()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly());
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
builder.RegisterFilterProvider();
builder.RegisterModule(new AutofacDataModule());
builder.RegisterModule(new AutofacServiceModule());
builder.RegisterType<WunderlistUserStore>().As<IUserStore<OwinUser,int>>().InstancePerRequest();
builder.RegisterType<WunderlistRoleStore>().As<IRoleStore<OwinRole,int>>().InstancePerRequest();
builder.RegisterType<WunderlistUserManager>().As<UserManager<OwinUser,int>>().InstancePerRequest();
IContainer container = builder.Build();
System.Web.Mvc.DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
}
2. Registering user store
https://github.com/cococrm/ZY.Web/blob/master/ZY.WebApi/Autofac/RepositoryModule.cs
protected override void Load(ContainerBuilder builder)
{
builder.RegisterGeneric(typeof(Repository<,>)).As(typeof(IRepository<,>));
builder.RegisterType<UserStore>().As<IUserStore<User,int>>();
}
Is it possible to register a component conditionally on an other component's state? Something like:
ContainerBuilder.RegisterConditionally<T>(
Func<IComponentContext, bool>,
Func<IComponentContext, T>);
I've found that prior to V2 of autofac one could use a "Register().OnlyIf()" construction that seemed like the one I'm looking for. I would like such feature to conditionally override a default registration.
class CommonRegistrations
{
public virtual void Register(ContainderBuilder builder)
{
builder.Register(ctx => LoadSettings()).As<ISettings>().SingleInstance();
builder.RegisterType<DefaultFoo>().As<IFoo>();
}
}
class SpecificRegistrations : CommonRegistrations
{
public virtual void Register(ContainerBuilder builder)
{
base.Register(builder);
builder.ConditionalyRegister(
ctx => ctx.Resolve<ISettings>().ReallyUseSpecificFoo,
ctx => new SpecificFoo()).As<IFoo>();
}
}
...
var builder = new ContainerBuilder();
var registrations = new SpecificRegistrations();
registrations.Register(builder);
var container = builder.Build();
IFoo foo = container.Resolve<IFoo>();
The foo will be according to ISettings.ReallyUseSpecificFoo either instance of DefaultFoo or instance of SpecificFoo.
Thank you.
There is no way to perform conditional registration at the container level based on container contents. The trouble is that you would need to resolve something in the container in order to determine what gets registered in the container, which then could technically affect whether you wanted to register the thing in the first place. Chicken/egg circular dependency problem.
You can, however, register things conditionally into nested lifetime scopes. Most integration points (like ASP.NET) resolve out of nested lifetime scopes (like an HTTP-request-length lifetime scope). You can register things on the fly into nested lifetime scopes and that might solve your problem.
var builder = new ContainerBuilder();
builder.Register(ctx => LoadSettings()).As<ISettings>().SingleInstance();
builder.RegisterType<DefaultFoo>().As<IFoo>();
var container = builder.Build();
var settings = container.Resolve<ISettings>();
using(var scope =
container.BeginLifetimeScope(b => {
if(settings.ReallyUseSpecificFoo)
{
b.RegisterType<SpecificFoo>().As<IFoo>();
}
})
{
// Resolve things from the nested lifetime scope - it will
// use the overrides. This will get the SpecificFoo if the
// configuration setting is true.
var foo = scope.Resolve<IFoo>();
}
Another option you have is to make the registration a lambda. It might make the registration itself more complex but it's an option you could consider.
var builder = new ContainerBuilder();
builder.Register(ctx => {
var settings = ctx.Resolve<ISettings>();
if(settings.ReallyUseSpecificFoo)
{
return new SpecificFoo();
}
return new DefaultFoo();
}).As<IFoo>();
If manual construction there isn't appealing, you could pass it through Autofac, too.
var builder = new ContainerBuilder();
// Register the IFoo types - but NOT "As<IFoo>"
builder.RegisterType<DefaultFoo>();
builder.RegisterType<SpecificFoo>();
// In the lambda use Resolve<T> to get the instances.
builder.Register(ctx => {
var settings = ctx.Resolve<ISettings>();
if(settings.ReallyUseSpecificFoo)
{
return ctx.Resolve<SpecificFoo>();
}
return ctx.Resolve<DefaultFoo>();
}).As<IFoo>();
Yet another option is to update an existing container after being built. In this case, you avoid the chicken/egg scenario by actually building the container, using it, and changing registrations after the fact.
var builder = new ContainerBuilder();
builder.Register(ctx => LoadSettings()).As<ISettings>().SingleInstance();
builder.RegisterType<DefaultFoo>().As<IFoo>();
var container = builder.Build();
var settings = container.Resolve<ISettings>();
if(settings.ReallyUseSpecificFoo)
{
var updater = new ContainerBuilder();
updater.RegisterType<SpecificFoo>().As<IFoo>();
updater.Update(container);
}
Finally, you might consider XML configuration. Given the registration is dependent on some sort of configuration setting, you might consider using Autofac's XML configuration support. That way, instead of trying to resolve something out of an un-built container to conditionally register something else, you could just specify the right thing to register using the XML configuration and register the correct thing the first time.