I've got an app in asp.net core using the built-in DI framework. I'd like to add a per-request (i.e. transient) service to the provider, but I'd like to actually make use of the request in its construction.
services.AddTransient<IMyService>(provider => { ... });
That's the closest overload I can find, but the provider object doesn't have anything about the current request. Is there a way to achieve what I'm trying to do, without upgrading to a more robust DI framework?
As posted in the comments, you can inject the IHttpContextAccessor into your services and access it, if the HttpContext is the only thing you need.
public class MyService : IMyService
{
private readonly HttpContext context;
public MyService(IHttpContextAccessor httpContextAccessor)
{
if(IHttpContextAccessor==null)
throw new ArgumentNullException(nameof(httpContextAccessor));
context = httpContextAccessor.HttpContext;
}
}
However, if you need something that's only available in the controller or outside of HttpContext, you can create a factory and pass the parameters to the factory
public class MyServiceFactory : IMyServiceFactory
{
// injecting the HttpContext for request wide service resolution
public MyServiceFactory(IHttpContextAccessor httpContextAccessor) { ... }
public IMyService Create(IDependency1 dep1, IDependency2 dep 2, string someRuntimeConfig)
{
IServiceProvider provider = this.context.RequestServices;
var myService = new MyService(provider.GetService<ISomeRepository>(), dep1, dep2, someRuntimeConfig);
return myService;
}
}
and then inject the IMyServiceFactory to your classes where you'd need IMyService.
Related
Seems crazy that something like this is causing me such a headache. But here it is:
How do you use the built-in dependency injection for net core for a non-controller class? Please provide an example with includes instantiation.
Thanks.
Just make the class a service.
In startup.cs
services.AddScoped<AccountBusinessLayer>();
Then in controller, same as you do for other services:
private readonly AccountBusinessLayer _ABL;
Include in constructor as you do for other services:
public AccountController(
UserManager<ApplicationUser> userManager,
SignInManager<ApplicationUser> signInManager,IOptions<IdentityCookieOptions> identityCookieOptions,
IEmailSender emailSender,
ISmsSender smsSender,
ILoggerFactory loggerFactory,
RoleManager<IdentityRole> roleManager,
AccountBusinessLayer ABL
)
{
_userManager = userManager;
_signInManager = signInManager;
_externalCookieScheme = identityCookieOptions.Value.ExternalCookieAuthenticationScheme;
_emailSender = emailSender;
_smsSender = smsSender;
_logger = loggerFactory.CreateLogger<AccountController>();
_roleManager = roleManager;
_ABL = ABL;
}
You can easily define a static class with one property like:
public static class StaticServiceProvider
{
public static IServiceProvider Provider { get; set; }
}
after defined class you have to scope the service in the Startup.ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
//TODO: ...
services.AddScoped<IUnitOfWork, HttpUnitOfWork>();
services.AddSingleton<ISomeInterface, ISomeImplementation>();
}
then inside the Startup.Configure method on startup you can set the provider as static class property:
public void Configure(IApplicationBuilder app, ...)
{
StaticServiceProvider.Provider = app.ApplicationServices;
//TODO: ...
}
Now you can easily call StaticServiceProvider.Provider.GetService method almost everywhere in your application:
var unitOfWork = (IUnitOfWork)StaticServiceProvider.Provider.GetService(typeof(IUnitOfWork));
I'm not sure this is the best answer, but the way I decided to do it is to do the following:
1) Per the answer by #BrunoLM at on this question Resolving instances with ASP.NET Core DI suggested by #SystemCrash, I created a new project called UnderstandingDependencyInjection and pasted in the code examples.
Important: What I describe next see next will not make sense unless you visit the referenced link above (#1). What you see below is a partial solution that builds on the answer another user provided in a another SO question.
2) Next, I created another class called OtherService. I added a method DoSomething() that took a dependency on the TestService.
3) In the constructor of OtherService, I requested IServiceProvider in order to get a concrete implementation of ITestService so I could call its GenerateRandom() method.
4) Back in the HomeController.cs, I merely passed along the IServiceProvider reference to the constructor of OtherService.
So, this is what I have:
OtherService.cs
using System;
using Microsoft.Extensions.DependencyInjection;
namespace UnderstandingDependencyInjection.Services
{
public class OtherService
{
private readonly ITestService _testService;
public OtherService(IServiceProvider serviceProvider)
{
_testService = serviceProvider.GetService<ITestService>();
}
public int DoSomething()
{
var rnd = _testService.GenerateRandom();
return rnd * 2;
}
}
}
HomeController.cs
using Microsoft.Extensions.DependencyInjection;
using UnderstandingDependencyInjection.Services;
namespace UnderstandingDependencyInjection.Controllers
{
public class HomeController : Controller
{
private readonly ITestService _testService;
private readonly IServiceProvider _serviceProvider;
public HomeController(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
_testService = serviceProvider.GetService<ITestService>();
}
public IActionResult Index()
{
// This works!
// var rnd = _testService.GenerateRandom();
// What if I need to reference the TestService
// from another service? I.e., OtherService?
var otherService = new OtherService(_serviceProvider);
var rnd = otherService.DoSomething();
ViewBag.RandomNumber = rnd;
return View();
}
So, to summarize, the key to this technique is to pass around the concrete reference of IServiceProvider that your controller receives ... passing from the controller into any other custom classes that will also need any services that are registered into ASP.NET Core's DI framework.
What about static methods that depend on the TestService?
But, I may not want / need to create an instance of OtherService. I may want to merely call a method statically, but that method takes a dependency on a service managed by ASP.NET Core MVC's Dependency Injection framework. What now?
In this case, the best I can figure out, you would need to pass in the reference ON THE METHOD CALL to the static method. It looks nasty, and I'm hoping there's a more elegant way ... but here's what I figured out.
5) Building on the previous steps (above) I added a new class called StaticService.
6) I created a method DoSomething that takes IServiceProvider as a parameter.
7) I use the concrete instance of the IServiceProvider to get a concrete instance of the ITestService. I use this to call GenerateRandom().
8) From the controller, call the StaticService.DoSomething() method passing it the concrete instance of IServiceProvider that I'm holding on to.
StaticService.cs
using Microsoft.Extensions.DependencyInjection;
namespace UnderstandingDependencyInjection.Services
{
public class StaticService
{
// No constructors
public static int DoSomething(IServiceProvider serviceProvider)
{
var testService = serviceProvider.GetService<ITestService>();
var rnd = testService.GenerateRandom();
return rnd * 3;
}
}
}
HomeController.cs
public IActionResult Index()
{
// This works!
// var rnd = _testService.GenerateRandom();
// What if I need to reference the TestService
// from another service? I.e., OtherService?
//var otherService = new OtherService(_serviceProvider);
//var rnd = otherService.DoSomething();
// What if I need to reference the TestService
// from another service with a STATIC method?
// Best I can tell, you have to pass the
// ServiceProvider in on the method call.
var rnd = StaticService.DoSomething(_serviceProvider);
ViewBag.RandomNumber = rnd;
return View();
}
But isn't passing around ServiceProvider an anti-pattern?
In short, yes. You wind up passing ServiceProvider around everywhere in code. Some would argue that this gives every controller and ever class access to every service registered in ASP.NET Core's DI. That's true, and that seems bad.
But what are your alternatives? Should every class that has a dependency on your service ALSO be defined as a service and registered with the DI? In other words, should I create IOtherService, and then pass it a concrete ITestService in its constructor?
I could do that, HOWEVER now my controller's constructor needs BOTH ITestService AND IOtherService. In other words, in order to work correctly, the Controller needs to know how OtherService does its job and that it uses ITestService internally. That seems bad, too.
What to do?
What's the Best Answer?
Frankly, I think the best answer is found here:
Passing Services using Dependency Injection and Factory Pattern in ASP.NET
#Steven says in his answer:
It does mean however that you might need to move away from the built-in DI container of ASP.NET Core to a more feature rich DI library, because the built-in container is not capable of making a context aware registration for ILogger while having the library auto-wire other constructor dependencies as well.
There are actually many ways to inject your dependency, the most common one you will find on controllers. There is also this variant
var someService = (ISomeService)HttpContext.RequestServices.GetService(typeof(ISomeService));
I have a stateless service in Azure Service Fabric, and I'm using Microsoft.Extensions.DependencyInjection, although the same issue exists for any other DI frameworks. In my Program.cs, I create a ServiceCollection, add all (but one) of my registrations, create the service provider, and pass it to my service's constructor. Any service method with external entry will create a new service scope and call the main business logic class. The issue is that one of the classes I want to have scoped lifetime needs a value that is an input parameter on the request itself. Here's a code snippet of what I would like to achieve.
internal sealed class MyService : StatelessService, IMyService
{
private IServiceProvider _serviceProvider;
private IServiceScopeFactory _scopeFactory;
public MyService(StatelessServiceContext context, IServiceProvider serviceProvider)
: base(context)
{
_serviceProvider = serviceProvider;
_scopeFactory = _serviceProvider.GetRequiredService<IServiceScopeFactory>();
}
public async Task<MyResponse> ProcessAsync(MyRequest request, string correlationId, CancellationToken cancellationToken)
{
using (var scope = _scopeFactory.CreateScope())
{
var requestContext = new RequestContext(correlationId);
//IServiceCollection serviceCollection = ??;
//serviceCollection.AddScoped<RequestContext>(di => requestContext);
var businessLogic = scope.ServiceProvider.GetRequiredService<BusinessLogic>();
return await businessLogic.ProcessAsync(request, cancellationToken);
}
}
}
The cancellation token is already passed around everywhere, including to classes that don't use it directly, just so it can be passed to dependencies that do use it, and I want to avoid doing the same with the request context.
The same issue exists in my MVC APIs. I can create middle-ware which will extract the correlation id from the HTTP headers, so the API controller doesn't need to deal with it like my service fabric service does. One way I can make it work is by giving RequestContext a default constructor, and have a mutable correlation id. However, it's absolutely critical that the correlation id doesn't get changed during a request, so I'd really like the safety of having get-only property on the context class.
My best idea at the moment is to have a scoped RequestContextFactory which has a SetCorrelationId method, and the RequestContext registration simply calls the factory to get an instance. The factory can throw an exception if a new instance is requested before the id is set, to ensure no id-less contexts are created, but it doesn't feel like a good solution.
How can I cleanly register read-only objects with a dependency injection framework, where the value depends on the incoming request?
I only had the idea for a RequestContextFactory as I was writing the original question, and I finally made time to test the idea out. It actually was less code than I expected, and worked well, so this will be my go-to solution now. But, the name factory is wrong. I'm not sure what to call it though.
First, define the context and factory classes. I even added some validation checks into the factory to ensure it worked the way I expect:
public class RequestContext
{
public RequestContext(string correlationId)
{
CorrelationId = correlationId;
}
public string CorrelationId { get; }
}
public class RequestContextFactory
{
private RequestContext _requestContext;
private bool _used = false;
public void SetContext(RequestContext requestContext)
{
if (_requestContext != null || requestContext == null)
{
throw new InvalidOperationException();
}
_requestContext = requestContext;
}
public RequestContext GetContext()
{
if (_used || _requestContext == null)
{
throw new InvalidOperationException();
}
_used = true;
return _requestContext;
}
}
Then, add registrations to your DI container:
services.AddScoped<RequestContextFactory>();
services.AddScoped<RequestContext>(di => di.GetRequiredService<RequestContextFactory>().GetContext());
Finally, the Service Fabric service method looks something like this
public async Task<MyResponse> ProcessAsync(MyRequest request, string correlationId, CancellationToken cancellationToken)
{
using (var scope = _scopeFactory.CreateScope())
{
var requestContext = new RequestContext(correlationId);
var requestContextFactory = scope.ServiceProvider.GetRequiredService<RequestContextFactory>();
requestContextFactory.SetContext(requestContext);
var businessLogic = scope.ServiceProvider.GetRequiredService<BusinessLogic>();
return await businessLogic.ProcessAsync(request, cancellationToken);
}
}
Kestrel middleware could look something like this
public async Task Invoke(HttpContext httpContext)
{
RequestContext requestContext = new RequestContext(Guid.NewGuid().ToString());
var factory = httpContext.RequestServices.GetRequiredService<RequestContextFactory>();
factory.SetContext(requestContext);
httpContext.Response.Headers["X-CorrelationId"] = requestContext.CorrelationId;
await _next(httpContext);
}
Then just do the normal thing and add a RequestContext parameter to the constructor of any class that needs to get the correlation id (or any other info you put in the request context)
I'm wanting to configure registrations in a Unity container being used by ASP.NET Web API 2 based on properties of a HTTP request. For example, a request to /api/database1/values should result in a Unity container configuration with an IDbContext configured for database1, while a request to /api/database4/values will get an IDbContext configured for database4.
I've gotten so far as using UnityHierarchicalDependencyResolver as the dependency resolver, so types registered with HierarchicalLifetimeManager last only for the lifetime of the request. This works well for getting types resolved per request. But how to get them registered per request using OWIN middleware is beyond me.
In my middleware, a call to System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(IUnityContainer)) gets an instance of IUnityContainer, but it's the same container for all requests, including any registrations from previous requests.
By encapsulating UnityHierarchicalDependencyResolver with my own implementation of IDependencyResolver I can see that IDependencyResolver.BeginScope isn't called until much later in the process. So the problem would seem to be that the child container isn't created until Web API wakes up, long after my middleware calls Next(..).
Is there a way I can get the scope of my dependency resolver to start sooner? Is there some other strategy that I'm missing. In case it makes any difference, I'm hosting in IIS, but favouring the OWIN middleware approach.
Update
This isn't an answer, and it's too big for a comment, but after struggling to solve this with Unity I decided to switch to Autofac and it all just fell into place.
The Autofac OWIN packages (Autofac.Mvc5.Owin, Autofac.Owin, Autofac.WebApi2.Owin) make it dead easy to use Autofac within the OWIN pipeline and ensure appropriate lifetime management in ASP.NET MVC and Web API. This was the missing link.
I couldn't find a way to reconfigure the container per-request, but it did at least make it possible to configure a factory per-request (so yes, #Haukinger and #alltej, you were right to push in that direction.
So I register a factory like:
builder.RegisterType<DataDependencyFactory>().InstancePerRequest();
And register the create method of that factory like:
builder
.Register(c => c.Resolve<DataDependencyFactory>().CreateDataDependency())
.As<IDataDependency>()
.InstancePerRequest();
Registering the factory this way is particularly useful, because downstream dependents don't need to be aware of the factory. I like this because my dependents don't need a factory, they need an instance. The container bends to the needs of my dependents, not the other way around :)
Then, in a piece of OWIN middleware, I resolve the factory, and set a property on it according to the properties of the request. Subsequent resolution of IDataDependency in an MVC or Web API controller, or anything else later in the OWIN pipeline, will get an instance configured according to the property on the factory.
Based on your api URL ("/api/database4/values"), I suggest that you create a filter attribute(e.g. DbIdFilter) so that you can reuse the filter attribute to other controller methods that follow similar url path/segment like this below:
[HttpGet]
[DbIdFilter]
[Route("{databaseId}/values")]
public IHttpActionResult GetValues()
{
return Ok();
}
[HttpGet]
[DbIdFilter]
[Route("{databaseId}/products")]
public IHttpActionResult GetProducts()
{
return Ok();
}
First, create the filter attribute:
public class DbIdFilterAttribute : ActionFilterAttribute
{
private readonly string _routeDataId;
private const string defaultRouteName = "databaseId";
public DbIdFilterAttribute():this(defaultRouteName)
{}
public DbIdFilterAttribute(string routeDataId)
{
_routeDataId = routeDataId;
}
public override void OnActionExecuting(HttpActionContext actionContext)
{
var routeData = actionContext.Request.GetRouteData();
var dbId = routeData.Values[_routeDataId] as string;
//here we create the db instance at the filter level.
DbInstanceFactory.RegisterDbInstance(dbId);
}
}
Next, create an instance factory that will register/resolve the db instance during runtime:
public class DbInstanceFactory : IDbInstanceFactory
{
public static IDbInstance RegisterDbInstance(string databaseId)
{
var factory = UnityConfig.GetConfiguredContainer().Resolve<IDbInstanceFactory>();
return factory.CreateInstance(databaseId);
}
public IDbInstance CreateInstance(string databaseId)
{
var container = UnityConfig.GetConfiguredContainer();
//container.RegisterType<IDbInstance, DbInstance>();
container.RegisterType<IDbInstance, DbInstance>(new InjectionConstructor(databaseId));
var dbInstance = container.Resolve<IDbInstance>();
return dbInstance;
}
public IDbInstance GetInstance()
{
var container = UnityConfig.GetConfiguredContainer();
var dbInstance = container.Resolve<IDbInstance>();
return dbInstance;
}
}
public interface IDbInstanceFactory
{
IDbInstance CreateInstance(string databaseId);
IDbInstance GetInstance();
}
Register this factory class in UnityConfig.cs (or wherever you currently register the types):
container.RegisterType<IDbInstanceFactory, DbInstanceFactory>
(new ContainerControlledLifetimeManager());
It's registered ContainerControlledLifetimeManager since this factory does not have to be a per request.
So just a basic DbInstance class below(for clarity) that takes a parameter in the constructor (this parameter can be your connection string or a named connection):
public class DbInstance : IDbInstance
{
public string DbId { get; }
public DbInstance(string databaseId)
{
DbId = databaseId;
}
}
public interface IDbInstance
{
string DbId { get; }
}
In controller class, you can use it like this:
....
private IDbInstanceFactory _dbFactory;
public MyController(IDbInstanceFactory dbFactory)
{
_dbFactory = dbFactory;
}
// Alternate, if you want to use property injection instead of constructor injection
//[Dependency]
//public IDbInstanceFactory DbFactory { get; set; }
[HttpGet]
[DbIdFilter]
[Route("{databaseId}/test")]
public IHttpActionResult Test()
{
var db = _dbFactory.GetInstance();
return Ok(db.DbId);
}
...
I'm trying to specify a connection string dynamically based of the url using ninject.
I'm using the ninject.mvc nuget package that uses the webActivator.
My code is as follows:
my injection:
kernel.Bind<IUnitOfWork>().To<UnitOfWork>()
.WithConstructorArgument("connectionString", MvcApplication.GetConnectionStringName());
my global.asax
private static HttpContext _context;
public static string GetConnectionStringName() {
var subDomain = String.Empty;
if (_context != null) {
subDomain = _context.Request.Url.SubDomain();
}
return String.Format("{0}ConnectionString", subDomain);
}
The problem is the _context (which is set in my Application_BeginRequest) is always null because the WebActivator runs before the application_start.
Is it possible in ninject to specify to call MvcApplication.GetConnectionStringName() when a IUnitOfWork is required rather than on application start?
Is there a better approach to what I'm doing?
Thanks
You should use the Ninject binding like this.
kernel.Bind<IUnitOfWork>().To<UnitOfWork>()
.WithConstructorArgument("connectionString", context => MvcApplication.GetConnectionStringName());
Note that context here is of type Ninject's IContext and so has nothing to do with HttpContext.
Anyway I think you approach is suitable for this.
Sometimes (especially when there are multiple related parameters to be injected) I prefer creating an interface and specific implementations for the configurations and let them injected by standard bindings like this.
public interface IUnitOfWorkConfiguration {
string ConnectionString { get; }
}
public class AppConfigUnitOfWorkConfiguration : IUnitOfWorkConfiguration {
public string ConnectionString { get { ... } }
}
public class UnitOfWork {
public UnitOfWork(IUnitOfWorkConfiguration configuration) {
}
}
Bind<IUnitOfWorkConfiguration>().To<AppConfigUnitOfWorkConfiguration>();
Using this approach you can avoid specifying parameter names as string literals.
One more note about using HttpContext. I do not recommend using it that way because of thread safety issues. You should either mark your private static field _context with the [ThreadStatic] atribute or as a better choice simply use HttpContext.Current everywhere.
My repositories all take ISession in the constructor:
protected Repository(ISession session)
{
this.session = session;
}
private readonly ISession session;
In an Asp.Net MVC application, using StructureMap, how would I go about setting up ISession in my the StructureMap Registry? Would I need to to add SessionFactory to the container also? Does FluentNHibernate change things?
You should register ISession using a factory method.
Another options (not always the best, but easy to use) is to:
Implement ISession and ISessionFactory interfaces (SessionProxy and SessionFactoryProxy).
public class SessionAggregator : ISession {
protected ISession session;
public SessionAggregator(ISessionFactory theFactory) {
if (theFactory == null)
throw new ArgumentNullException("theFactory", "theFactory is null.");
Initialise(theFactory);
}
protected virtual void Initialise(ISessionFactory factory) {
session = factory.OpenSession();
}
// the ISession implementation - proxy calls to the underlying session
}
public class SessionFactoryAggregator : ISessionFactory {
protected static ISessionFactory factory;
private static locker = new object();
public SessionFactoryAggregator() {
if (factory == null) {
lock(locker) {
if (factory == null)
factory = BuildFactory();
}
}
}
// Implement the ISessionFactory and proxy calls to the factory
}
This way you can just register ISession (implemented by SessionAggregator) and ISessionFactory (SessionFactoryAggreagator) and any DI framework will resolve ISession easily.
This is good if your DI does not support factory method (I don't know if Structure Map does).
I have added these implementation to my Commons assembly so I should not reimplement it every time.
EDIT: Now, to make use of ISession in web application:
Register SessionFactoryAggregator in the structure map (life time can be singleton).
Register SessionAggregator in Snstrucure map and set its lifetime to InstanceScope.Hybrid.
At the end of each request you need to dispose the session by calling HttpContextBuildPolicy.DisposeAndClearAll()
The code can look like:
// The Registry in StructureMap
ForRequestedType<ISessionFactory>()
.CacheBy(InstanceScope.Singleton)
.TheDefaultIsConcreteType<SessionFactoryAggregator>();
ForRequestedType<ISession>()
.CacheBy(InstanceScope.Hybryd)
.TheDefaultIsConcreteType<SessionAggregator>();
// Then in EndRequest call
HttpContextBuildPolicy.DisposeAndClearAll()
This question & answers might help you.
One way to go - steal nhibernate session management from "S#arp Architecture". Works great.