I'm trying to inject my controller with Autofac. Unfortunately I am unable to configure Autofac in away so that the 'DefaultControllerActivator` wont construct my controllers?
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddControllersAsServices();
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterModule<ServiceModule>();
containerBuilder.Populate(services);
containerBuilder.RegisterType<LoginController>().PropertiesAutowired();
ApplicationContainer = containerBuilder.Build();
return new AutofacServiceProvider(this.ApplicationContainer);
}
public class ServiceModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterModule(new DataProviderModule());
builder.RegisterType(typeof(LoginService)).As(typeof(ILoginService)).InstancePerRequest();
}
}
[Route("api/[controller]")]
public class LoginController : Controller
{
private readonly ILoginService _loginService;
public LoginController(ILoginService loginService)
{
_loginService = loginService;
}
}
I followed the documentation of Autofac as shown above. Unfortunately the LoginController will not be constructed because it requires an injection.
edit: If there is a way of using "Modules" without Autofac, I'd be very interesting for any suggestions :)
Thanks you in advance!
By default, ASP.NET Core will resolve the controller parameters from the container but doesn’t actually resolve the controller from the container. This usually isn’t an issue but it does mean:
The lifecycle of the controller is handled by the framework, not the request lifetime.
The lifecycle of controller constructor parameters is handled by the request lifetime.
Special wiring that you may have done during registration of the controller (like setting up property injection) won’t work.
You can change this by specifying AddControllersAsServices() when you register MVC with the service collection. Doing that will automatically register controller types into the IServiceCollection when you call builder.Populate(services).
public class Startup
{
public IContainer ApplicationContainer {get; private set;}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add controllers as services so they'll be resolved.
services.AddMvc().AddControllersAsServices();
var builder = new ContainerBuilder();
// When you do service population, it will include your controller
// types automatically.
builder.Populate(services);
// If you want to set up a controller for, say, property injection
// you can override the controller registration after populating services.
builder.RegisterType<MyController>().PropertiesAutowired();
this.ApplicationContainer = builder.Build();
return new AutofacServiceProvider(this.ApplicationContainer);
}
}
Use InstancePerLifetimeScope in ASP.NET Core. The differences between ASP.NET and ASP.NET Core like this are documented.
Related
I am using the repository method for a data access class. So the constructor looks something like this:
public class MongoDbUnitOfWork : IMongoDbUnitOfWork
{
private readonly ILogger _logger;
private readonly IConfiguration _config;
public MongoDbUnitOfWork(ILogger logger, IConfiguration config)
{
_logger = logger;
_config = config
//do other stuff here, create database connection, etc.
}
}
public interface IMongoDbUnitOfWork
{
// various methods go in here
}
The key thing is that the constructor relies on the fact that 2 services are parsed to it.
Then in startup.cs I tried to do the following:
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IMongoDbUnitOfWork>(sp =>
{
var logger = sp.GetRequiredService<ILogger>();
var config = sp.GetRequiredService<IConfiguration>();
return new MongoDbUnitOfWork(logger, config);
});
//add other services
}
This compiled but did not work when I tried to run an API route through a controller. I got an error stating:
System.InvalidOperationException: Unable to resolve service for type 'NamespaceDetailsHere.IMongoDbUnitOfWork' while attempting to activate 'NamespaceDetailsHere.Controllersv1.TestController'.
Then I ran a little Debug.WriteLine() script in startup.cs to see whether the ILogger and IConfiguration services existed. They did. I'm not sure what I'm doing wrong here.
The ASP.NET Core service container will automatically resolve the services dependencies that are injected through the constructor, so you dont need the action configuration at all. When the service is constructed, any dependencies in the constructor are automatically required (as you're able to see with the exception).
Simply register your services with
services.AddSingleton<IMongoDbUnitOfWork, MongoDbUnitOfWork>();
According to this tutorial I should:
Register your context with dependency injection
The Tutorial describes that I should locate the method
ConfigureServices() and put in there the code advised.
Here's my startup.cs:
using Microsoft.Owin;
using Owin;
[assembly: OwinStartupAttribute(typeof(MyProject.Startup))]
namespace MyProject
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
}
}
so I don't know where to correctly put the code.
Because the project isn't compatible with .net core 2.1, it was needed to change Project->Property to .Net Framework 4.6.1
and install packages Microsoft.EntityFrameworkCore.SqlServer and Microsoft.EntityFrameworkCore.Tools
I tried to add the dependency injection to global.asax.cs file as follows:
protected void Application_Start()
{
var services = new ServiceCollection();
ConfigureServices(services);
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
private void ConfigureServices(IServiceCollection services)
{
var connection = #"Server=(localdb)\mssqllocaldb;Database=Blogging;Trusted_Connection=True;ConnectRetryCount=0";
services.AddDbContext<BloggingContext>(options => options.UseSqlServer(connection));
}
I succeeded with the step and created the controller and it works, but I haven't chosen the right context (BloggingContext), so it created second database. So, I need to create a controller with BloggingContext, do you know how?
The version of the shown startup and the tutorial are in conflict with each other.
If this if for an Asp.Net Core MVC application then you can add the method your self. Startup class is part of the convention.
public partial class Startup {
//...
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//...
}
}
Reference App startup in ASP.NET Core
The ConfigureServices method
The ConfigureServices method is:
Optional.
Called by the host before the Configure method to
configure the app's services.
Where configuration options are set by convention.
If however you are trying to use .Net Core technologies in an Asp.Net MVC 5+ (which is what the GitHub project targets) then you will need to modify your approach to adapt to using .Net Core Dependency Injection with a non core platform.
First you will need a IDependencyResolver which is the DI framework used by that version of Asp.Net MVC, and a way to replace the default resolver with your own.
public sealed class CoreDependencyResolver : System.Web.Mvc.IDependencyResolver {
private readonly System.Web.Mvc.IDependencyResolver mvcInnerResolver;
private readonly IServiceProvider serviceProvider;
public CoreDependencyResolver(IServiceProvider serviceProvider, System.Web.Mvc.IDependencyResolver dependencyResolver) {
this.serviceProvider = serviceProvider;
mvcInnerResolver = dependencyResolver;
}
public object GetService(Type serviceType) {
object result = this.serviceProvider.GetService(serviceType);
if (result == null && mvcInnerResolver != null)
result = mvcInnerResolver.GetService(serviceType);
return result;
}
public IEnumerable<object> GetServices(Type serviceType) {
IEnumerable<object> result = this.serviceProvider.GetServices(serviceType);
if (result == null && mvcInnerResolver != null)
result = mvcInnerResolver.GetServices(serviceType);
return result ?? new object[0];
}
}
With the custom resolver in place, you can now configure the application to use it.
Using your current example as a starting point (review comments)
protected void Application_Start() {
var services = new ServiceCollection();
ConfigureServices(services);
//build service provider
IServiceProvider provider = services.BuildServiceProvider();
//Get the current resolver used by MVC
var current = DependencyResolver.Current;
//use that and the provider to create your custom resolver
var resolver = new CoreDependencyResolver(provider, current);
//now set the MVC framework to use the resolver that wraps the service provider
//that was created from .Net Core Dependency Injection framework.
DependencyResolver.SetResolver(resolver);
//...
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
private void ConfigureServices(IServiceCollection services) {
//... omitted for brevity (register dependencies as normal)
}
Here I am using Oracle, but you could do the same with SQL Server...
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFrameworkOracle()
.AddDbContext<OracleDbContext>(builder => builder.UseOracle(Configuration["Data:OracleDbContext"]),ServiceLifetime.Scoped)
.AddDbContext<AppsDbContext>(option => option.UseOracle(Configuration["Data:AppsDbConnection:ConnectionString"]), ServiceLifetime.Scoped);
Then in my appsettings.json, I include the connection strings...
"Data": {
"OracleDbContext": "your connection string" },
"AppsDbContext": "your connection string" }
}
It appears you're using .Net Framework, rather than .Net Core.
2 simple ideas here:
Injecting DbContext into service layer: this layer will be class Library for .Net Framework or (use .Net Standard Class library if your platform is .Net Core). This thread shows you how to perform it: Injecting DbContext into service layer
use Ninject as dependency injector if you're on .Net Framework platform. This thread shows a good example: How to handle DBContext when using Ninject
Whilst using .NET Core tooling with full framework works quite well, if you have to use MVC5 and full framework I would not try to work it that way round.
There are many .NET 4.6.1 dependency injection frameworks, in this example I will use Autofac.
Install the NuGet packages Autofac and Autofac.Mvc5.
Add an AutofacRegistration.cs class to the App_Start folder
In the Application_Start() method in Global.asax add the line AutofacRegistration.BuildContainer();
Your AutofacRegistration class is where you wire up all your dependencies for dependency injection. The full docs are here https://autofaccn.readthedocs.io/en/latest/integration/mvc.html
public class AutofacRegistration
{
public static void BuildContainer()
{
var builder = new ContainerBuilder();
// Register your MVC controllers
builder.RegisterControllers(typeof(MvcApplication).Assembly);
// Now grab your connection string and wire up your db context
var conn = ConfigurationManager.ConnectionStrings["BloggingContext"];
builder.Register(c => new BloggingContext(conn));
// You can register any other dependencies here
// Set the dependency resolver to be Autofac.
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
This is assuming your BloggingContext has a constructor that takes the connection string as a parameter and passes it to the base class. Something like
public class BloggingContext : DbContext
{
public BloggingContext(string connectionString) : base(connectionString)
{
}
}
There is loads more in the documentation about scope etc that is worth reading but this should be the nuts and bolts of it.
I have an API (eg: ItemController.cs) which would obtain the Authorization Token from the Request Header at run time. With the Token, then only I pass into my Service Class (eg: ServiceItem.cs).
Here's how I did.
At the Startup.cs, I register my ServiceItem
var builder = new ContainerBuilder();
builder.RegisterType<ServiceItem>();
container = builder.Build(); //Note that, my container is a static variable
In my API, I resolve it in this way:
[Authorize]
[Route("GetData")]
[HttpGet]
public IHttpActionResult GetData([FromUri] Filter filter)
{
using (var scope = Startup.container.BeginLifetimeScope())
{
var serviceItem = Startup.container.Resolve<ServiceItem>(
new NamedParameter("token", Request.GetHeader("Authorization"))
);
return Ok(serviceItem.getItem(filter)); //filter is a param from webAPI
}
}
Question:
Is this how the Autofac normally work in web API? First, i am using a global static IContainer. Second, the codes look repetitive if i expose a few more functions.
I was thinking to resolve the ServiceItem in the constructor of the API. But the authorization token is not available yet.
Any suggestion is appreciated.
P.S.:
Here's my ServiceItem which, in the constructor, has a param 'token'
public class ServiceItem
{
public string token;
public ServiceItem(string token)
{
this.token = token;
}
public void doSomething()
{
//based on token, do processing
}
}
It is a bad idea to refer to a static container within your startup class. That way, you introduce tight coupling between the controller and the startup. Your controller dependencies should be satisfied by constructor parameters. Take at http://docs.autofac.org/en/v4.0.0/integration/aspnetcore.html
The Startup.ConfigureServices method can optionally return a IServiceProvider instance, which allows you to plug-in Autofac into the ASP.NET Core Dependency Injection framework:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
var builder = new ContainerBuilder();
builder.RegisterType<MyType>().As<IMyType>();
builder.Populate(services);
this.ApplicationContainer = builder.Build();
return new AutofacServiceProvider(this.ApplicationContainer);
}
After initializing your container, constructor parameters will be automatically resolved by Autofac:
public class MyController
{
private readonly IMyType theType;
public MyController(IMyType theType)
{
this.theType = theType;
}
....
}
I have an ASP.NET Core project. I want to dynamically resolve a "one instance per request" dependency inside my other dependencies.
I have registered a dependency using Autofac as an InstancePerLifetimeScope dependency in my Startup class:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var builder = new ContainerBuilder();
builder.RegisterType<MyDependency>().AsImplementedInterfaces().InstancePerLifetimeScope();
return new AutofacServiceProvider(builder.Build());
}
When I use this dependency directly in controller's constructor, it works as expected - it is a new instance per request.:
public MyController(IMyDependency dependency)
{
}
I want to achieve the same in one of the dependent classes. Dependency is dynamic, so I want to resolve that from IServiceProvider:
public class MyDeepDeepDependency
{
public MyDeepDeepDependency(IServiceProvider serviceProvider)
{
var dep = serviceProvider.GetService(typeof(IMyDependency));
}
}
However, a "dep" instance is the same across all requests.
I assume there is a new scope created per request, and controller is resolved from a new scope. When resolving IServiceProvider, I always get a root IServiceProvider instead of a request one.
Is there a way to resolve IServiceProvider specific for a request? I think it's the same as HttpContext.RequestServices in controller, but I don't want to pass the reference down through all my classes.
Is there any other way to resolve a dynamic dependency once per request?
I ended up injecting IHttpContextAccessor:
public class MyDeepDeepDependency
{
public MyDeepDeepDependency(IHttpContextAccessor contextAccessor)
{
var dep = contextAccessor.HttpContext.RequestServices.GetService(typeof(IMyDependency));
}
}
AutoFac has recently been updated for MVC 5.1 but at the time of writing I find that the documentation is lacking (especially for a simple example).
I would like to inject dependencies into MVC Controllers and register my own implementations for e.g. e-mail (actual sending vs print to output window) as a basic example.
I'm do not know if I am missing a good resource for this and I am slight concerned that because it uses the OWIN specification that the implementation may differ for MVC5.1 (ASP.NET Identity uses OWIN and there is some special attributes used to properly instantiate OWIN) so need to check I am getting things right.
I have a working example with the below setup code - is this correct and good practice for a standard MVC5.1 web app?
Bonus question: do I need to add InstancePerHttpRequest to the RegisterControllers line?
i.e. builder.RegisterControllers(typeof(MvcApplication).Assembly).InstancePerHttpRequest();
(Note: I see the examples on GitHub from Autofac but cannot find an plain example appropriate to MVC5.1.)
public static void RegisterDependencies()
{
// Register MVC-related dependencies
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterModelBinders(typeof(MvcApplication).Assembly);
builder.RegisterModule<AutofacWebTypesModule>();
// Register e-mail service
builder.RegisterType<EmailSenderToDebug>().As<IEmailSender>().InstancePerHttpRequest();
builder.RegisterModelBinderProvider();
// Set the MVC dependency resolver to use Autofac
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
And my controller:
public class HomeController : Controller
{
public IEmailSender _email { get; set; }
public HomeController(IEmailSender es)
{
this._email = es;
}
//
// GET: /Home/
public ActionResult Index()
{
Email e = new Email();
e.To(new MailAddress("something#something.com", "mr. something"));
e.Subject("test");
e.Message("hello world");
e.From(new MailAddress("somethingElse#somethingElse.com", "mr. else"));
this._email.SendEmail(e);
return View();
}
}
Thanks!
Here's what I have - feedback welcome!
Global.asax.cs:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// Register Inversion of Control dependencies
IoCConfig.RegisterDependencies();
// Typical MVC setup
// ....
}
}
App_Start folder:
public class IoCConfig
{
/// <summary>
/// For more info see
/// :https://code.google.com/p/autofac/wiki/MvcIntegration (mvc4 instructions)
/// </summary>
public static void RegisterDependencies()
{
#region Create the builder
var builder = new ContainerBuilder();
#endregion
#region Setup a common pattern
// placed here before RegisterControllers as last one wins
builder.RegisterAssemblyTypes()
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces()
.InstancePerHttpRequest();
builder.RegisterAssemblyTypes()
.Where(t => t.Name.EndsWith("Service"))
.AsImplementedInterfaces()
.InstancePerHttpRequest();
#endregion
#region Register all controllers for the assembly
// Note that ASP.NET MVC requests controllers by their concrete types,
// so registering them As<IController>() is incorrect.
// Also, if you register controllers manually and choose to specify
// lifetimes, you must register them as InstancePerDependency() or
// InstancePerHttpRequest() - ASP.NET MVC will throw an exception if
// you try to reuse a controller instance for multiple requests.
builder.RegisterControllers(typeof(MvcApplication).Assembly)
.InstancePerHttpRequest();
#endregion
#region Register modules
builder.RegisterAssemblyModules(typeof(MvcApplication).Assembly);
#endregion
#region Model binder providers - excluded - not sure if need
//builder.RegisterModelBinders(Assembly.GetExecutingAssembly());
//builder.RegisterModelBinderProvider();
#endregion
#region Inject HTTP Abstractions
/*
The MVC Integration includes an Autofac module that will add HTTP request
lifetime scoped registrations for the HTTP abstraction classes. The
following abstract classes are included:
-- HttpContextBase
-- HttpRequestBase
-- HttpResponseBase
-- HttpServerUtilityBase
-- HttpSessionStateBase
-- HttpApplicationStateBase
-- HttpBrowserCapabilitiesBase
-- HttpCachePolicyBase
-- VirtualPathProvider
To use these abstractions add the AutofacWebTypesModule to the container
using the standard RegisterModule method.
*/
builder.RegisterModule<AutofacWebTypesModule>();
#endregion
#region Set the MVC dependency resolver to use Autofac
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
#endregion
}
}
Then you can register modules as you see fit which is really great if you have a modularised application and you don't know what you are going to include in your project. You can nuget setup your project and no manual intervention required (I wish!)....
public class RegisterApplicationIoC : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<EmailSenderToDebug>()
.As<IEmailSender>()
.InstancePerHttpRequest();
}
}