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!
Related
I'm trying to inject a SignalR IHubContext into a Web API 2.x controller in an ASP.NET MVC 5 app Framework 4.72 (not .NET Core). It's throwing this exception when calling the Web API controller MyController:
An error occurred when trying to create a controller of type 'MyController'. Make sure that the controller has a parameterless public constructor
The inner exception says:
None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'MyController' can be invoked with the available services and parameters: Cannot resolve parameter 'Microsoft.AspNet.SignalR.IHubContext[MyHub] context' of constructor 'Void .ctor(Microsoft.AspNet.SignalR.IHubContext [MyHub])'.
I don't mind doing this using property injection but haven't had any luck getting that to work. So I'm doing injection into the c'tor of the controller.
I've followed these answers for help:
https://stackoverflow.com/a/37913821/177416 --> c'tor injection
https://stackoverflow.com/a/29793864/177416 --> c'tor injection
https://stackoverflow.com/a/26810399/177416 --> property injection
https://stackoverflow.com/a/15600493/177416 --> property injection
Here's the Web API controller:
public class MyController : WebApiController
{
public IHubContext<MyHub> Context { get; set; }
public MyController(IHubContext<MyHub> context)
{
Context = context;
}
}
And here's the pertinent part of the Startup.cs:
public void Configuration(IAppBuilder app)
{
// Other code...
var builder = new ContainerBuilder();
var config = new HttpConfiguration();
builder.RegisterHubs(Assembly.GetExecutingAssembly());
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.InstancePerRequest();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly())
.InstancePerRequest();
builder.RegisterType<AutofacDependencyResolver>()
.As<IDependencyResolver>()
.SingleInstance();
builder
.Register(c => c.Resolve<IConnectionManager>().GetHubContext<MyHub>())
.Named<IHubContext>("MyHub");
builder.RegisterType<MyController>()
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(IHubContext),
(pi, ctx) => ctx.ResolveNamed<IHubContext>("MyHub")
)
);
var container = builder.Build();
app.UseAutofacMiddleware(container);
DependencyResolver.SetResolver(new Autofac.Integration.Mvc.AutofacDependencyResolver(container));
config.DependencyResolver = new AutofacWebApiDependencyResolver((IContainer)container);
app.Map("/signalr", map =>
{
var hubConfiguration = new HubConfiguration
{
Resolver = new AutofacDependencyResolver(container),
};
map.RunSignalR(hubConfiguration);
});
}
What am I missing? Thanks.
Your first problem is that typeof(IHubContext) is not the same as typeof(IHubContext<MyHub>). You can get around that by using:
pi.ParameterType == typeof(IHubContext).MakeGenericType(typeof(MyHub))
However, old versions of SignalR don't support the generic interfaces very well, so it would probably work better if you left the comparison as is, and inject an IHubContext rather than an IHubContext<MyHub> in MyController.
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,
I am using Automapper and StructureMap in my Web API application.
I am getting the following error message during compilation. I have installed the following:
StructureMap 4.5
StructureMap.MVC5 3.1.1.134
StructureMap.web 4.0.0.315
StructureMap.webapi2 3.0.4.125
There is no implicit conversion from MapperConfiguration to IMapperConfiguration
at the following line of code:
For<IMapperConfiguration>().Use(config);
The full code is below
public class DefaultRegistry : Registry
{
#region Constructors and Destructors
public DefaultRegistry()
{
var profiles = from t in typeof(DefaultRegistry).Assembly.GetTypes()
where typeof(Profile).IsAssignableFrom(t)
select (Profile) Activator.CreateInstance(t);
var config = new MapperConfiguration(cfg =>
{
foreach (var profile in profiles)
{
cfg.AddProfile(profile);
}
});
//Create a mapper that will be used by the DI container
var mapper = config.CreateMapper();
//Register the DI interfaces with their implementation
For<IMapperConfiguration>().Use(config);
For<IMapper>().Use(mapper);
//Register the UserRepository and pass instance of Mapper to its constructor
For<IMovieBusiness>().Use<MovieBusiness>().Ctor<IMapper>().Is(mapper);
For<IConnectionFactory>().Use<ConnectionFactory>();
For<IMovieRepository>().Use<MovieRepository>();
For<IUnitOfWork>().Use<UnitOfWork>();
For<IMovieService>().Use<MovieService>();
For<IMovieBusiness>().Use<MovieBusiness>();
}
MapperConfiguration implements IConfigurationProvider only in Automapper 6.1.0.
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>>();
}
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).