I'm investigating the topic of DI in ASP.NET 5, and I faced such a problem - I don't understand how to create a new instance of a service per request.
I use the code:
services.AddScoped<ValueStore>();
And inside my middlewares I grab the value:
var someValueStore = app.ApplicationServices.GetService<ValueStore>();
Full code is available here
And my problem is: while I expect this service to be renewed on each request, it doesn't happen, and it behaves as if it was registered as AddSingleton().
Am I doing anything wrong?
app.ApplicationServices does not provide a request-scoped IServiceProvider. It will return a singleton instance of ValueStore when you use GetService<>(). You have two options here:
Use HttpContext.RequestServices:
var someValueStore = context.RequestServices.GetService<ValueStore>();
Or inject ValueStore in the Invoke method of a middleware:
public async Task Invoke(HttpContext httpContext, ValueStore valueStore)
{
await httpContext.Response.WriteAsync($"Random value = {valueStore.SomeValue}");
await _next(httpContext);
}
I cloned your repo and this works.
Related
I am using ASP.Net Core 3.1 Web API. I have a need to perform an additional activity in the background, when I get a call to a controller action method, without adding too much delay in the processing of current action. Additional activity that I am performing is prepping some data into Cache so its available before user performs next step. So I want to simply trigger that activity, without waiting for it to complete. But at the same time, I don't want task to be disposed without completing, just because the action method completed it processing, returned response back to caller and the transient controller was disposed.
So I plan to use a Singleton service injected into controller to perform the task. But the task itself may involve needing to use a transient service. So instead of directly injecting transient service into Singleton Service, I am thinking injecting transient service into Controller. And then in the action method, I will pass the transient service as parameter to an async method on Singleton service and then within that method, call the required service.
public IActionResult GetSomething(string input1)
{
var resp = await _transientService1.GetSomethingElse(input1);
// I am not awaiting this task
var backgroundTask = _singletonService1.DoSomethingAsync(_transientService2, resp.someattr);
return Ok(resp);
}
Now within the singleton service, I will get the required data and write it into cache.
public async Task DoSomethingAsync(ITransientService2 myTransientService2, string someParam)
{
var temp1 = await myTransientService2.GetSomethingNew(someParam);
var temp2 = await _SingletonService2.WriteCache(temp1);
}
So I wanted to know first of all, if this approach will work. If it works, what are the pitfalls or gotchas, that I need to be aware of.
Currently, this is all conceptual. Else I would have tried it out directly:) Hence the questions.
That can work as long as you're happy with passing the dependency as an argument.
If you don't want to pass the transient dependency in as an argument, another option is to inject the IServiceProvider into the singleton service, and instantiate the transient service when it's needed.
class MySingleton
{
private readonly IServiceProvider _serviceProvider;
public MySingleton(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task ExecuteAsync()
{
// The scope informs the service provider when you're
// done with the transient service so it can be disposed
using (var scope = _serviceProvider.CreateScope())
{
var transientService = scope.ServiceProvider.GetRequiredService<MyTransientService>();
await transientService.DoSomethingAsync();
}
}
}
I am trying to add some items to the piranha sitemap using the delegate method OnGenerateSitemaps.
In this method I am calling to a service that gets data from entity framework context and then caches it. Whenever I try to use this service in the delegate-method I get a error that the dbContext has already been disposed.
System.AggregateException: 'One or more errors occurred. (Cannot
access a disposed context instance. A common cause of this error is
disposing a context instance that was resolved from dependency
injection and then later trying to use the same context instance
elsewhere in your application. This may occur if you are calling
'Dispose' on the context instance, or wrapping it in a using
statement. If you are using dependency injection, you should let the
dependency injection container take care of disposing context
instances.
I've tried making the service sync instead of async, I've tried awaiting the result and running the task sync, none of which works.
Any ideas on how to use my service in this delegate in Configure on startup?
services.AddScoped<ICachedSitemapService, CachedSitemapService>();
In startup I inject the service, which is scoped.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApi api, ICachedSitemapService cachedSitemapService)
{
App.Hooks.OnGenerateSitemap += (sitemap) =>
{
var items = await cachedSitemapService.GetCachedClinics().Result;
sitemap.AddRange(items);
return sitemap;
};
}
The service that is called is DbContext to get items:
private async Task<IEnumerable<SitemapItem>> GetSitemapClinics()
{
var spec = new ClinicSitemapSpecification();
//This throws error
var allClinics = await _clinicRepo.ListAsync(spec);
//code shortened for brevity, but returns a list.
}
I've tried below without any luck.
var items = await cachedSitemapService.GetCachedClinics().GetAwaiter().GetResult();
sitemap.AddRange(items);
We're (the Piranha team) planning on redesigning the hook system in version 10 (has to be a major version as it will break backward compatibility) to provide DI functionality in all hooks. However, in the meantime, the following should fix your issues.
using (var scope = app.ApplicationServices.CreateScope())
{
var service = scope.ServiceProvider.GetService< ICachedSitemapService>();
}
Since your service is registered to be scoped it can't be resolved directly from the root provider (like the error states). When resolving the service from a controller you are in the request scope, so the solution here is to just create a scope that will allow you to resolve it.
I have went through most of the documentation, and I am still unsure about specific usage of dependency scopes.
When my request hits my controller, I usually can use dependencies of the controller (provided via Constructor Injection) and not worry myself about it much.
However, I am writing a Delegating Handler:
public class MyHandler: DelegatingHandler
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
// I need IMyService here
return await base.SendAsync(request, cancellationToken);
}
I initially tried doing:
using(var scope = request.GetDependencyScope()){
var service = scope.GetService(typeof(IMyService));
}
But that - while it works - seems to close the scope and prevent my Controller from even initializing correctly.
I could do:
{
var requestScope = request.GetDependencyScope();
var scope = requestScope.GetRequestLifetimeScope();
var service = scope.Resolve<IMyService>();
// use service
return await base.SendAsync(request, cancellationToken);
}
but will that not create resource leak? Will the RequestLifetimeScope be disposed of when the request finishes?
If you could provide me with a sample of correct, best-practices style basic DelegatingHandler using Autofac-resolved service, that would help me greatly.
The request-level dependency scope is created for you and disposed for you. Just get it (not inside a using) and resolve from it if you need to. Of course, make sure the Autofac middleware executes before your middleware so the scope can be created for you; and if that's the case, it'll clean up after you, too. Automatically.
I'm currently using WebApiRequestLifestyle has the default scoped lifestyle. I want to inject a service in the OWIN Middleware and in one of the API controllers, and the service's scope should be still WebAPI i.e., for the entire request, there should be only one instance of the service.
public class TestMiddleware : OwinMiddleware
{
private readonly ITestService _testService;
public TestMiddleware(OwinMiddleware next, ITestService testService) : base(next)
{
_testService = testService;
}
public override async Task Invoke(IOwinContext context)
{
var test = _testService.DoSomething();
await Next.Invoke(context);
}
}
public class ValuesController : ApiController
{
private readonly ITestService _testService;
public ValuesController(ITestService testService)
{
_testService = testService;
}
}
ITestService instance should be same for the entire request. How should I register the middleware?
This is how I'm doing it now:
using (container.BeginExecutionContextScope())
{
var testService = container.GetInstance<ITestService>();
app.Use<TestMiddleware>(testService);
}
The problem with this approach is - one instance of ITestService is getting created for the middleware during registration and stays forever (like a singleton), and for every webapi request, a new instance gets created and shared across the controllers (webapi scope)
Please don't point me to these questions - WebApi + Simple Injector + OWIN
Injecting a dependency into OWIN Middleware and per web-request with Simple Injector
OWIN's Use<T> method registers the supplied T as singleton in the OWIN pipeline, no matter what lifetime you configured that type with in your container. So while you resolve the middleware in an active scope, you (implicitly) tell OWIN to cache this instance for ever.
You have two choices here:
Make sure that the middleware component can be used as singleton in the OWIN pipeline, or
Resolve the middleware component on each request.
Making sure the middleware component can be used as singleton is easy. Just register it as singleton in Simple Injector, and when you call Verify(), Simple Injector will detect whether or not this component can be used as singleton, or whether it has dependencies with a shorter lifestyle. This does mean however that all dependencies should be singletons and runtime data (like DbContext's and other data objects) should be passed through method calls after the object graph is built. I consider this good practice, but this might be quite a change in your application and probably quite a mind shift. Because of this I consider this to be out of scope for this question, so you should go for option 2.
In case your middleware component has dependencies with a shorter lifestyle, you are should resolve that middleware per request request. This means you should not use OWIN's Use<T>(middleware) method, because that would make it a singleton.
This is how to do it:
app.Use(async (context, next) =>
{
var middleware = container.GetInstance<TestMiddleware>();
await middleware.Invoke(context, next);
});
Note that the TestMiddleware is resolved on each request. This gives Simple Injector full control over the built object graph. This means however that you need to make a small adjustment to your TestMiddleware class. This is how it should look:
public sealed class TestMiddleware
{
private readonly ITestService _testService;
public TestMiddleware(ITestService testService)
{
_testService = testService;
}
public async Task Invoke(IOwinContext context, Func<Task> next)
{
var test = _testService.DoSomething();
await next();
}
}
Note that the OwinMiddleware parameter removed from the constructor, and replaced by a Func<Task> parameter in the Invoke method. This allows Simple Injector to construct the type, because its constructor won't contain any runtime parameters anymore. Remember: compile time dependencies through the constructor, runtime data through method calls.
Also note that the middleware doesn't inherit from OwinMiddleware anymore. Since the middleware didn't inject a wrapped middleware, inheriting from it became useless.
After reading Steven's answer, this is what I did:
Registered the middleware like this:
using (AsyncScopedLifestyle.BeginScope(ServiceLocator.Container))
{
app.Use<TestMiddleware>();
}
Inside the middleware, I used ServiceLocator to resolve the dependency as Steven suggested (I know ServiceLocator is an anti-pattern, but we are already using it in a few unavoidable places in the app)
public override async Task Invoke(IOwinContext context)
{
var testService = ServiceLocator.Current.GetInstance<ITestService>();
testService.DoSomething();
await Next.Invoke(context);
}
Note: I assume Simple Injector (or any DI library) uses CallContext to maintain the scoped instances; if so, just wanted to share that the CallContext doesn't flow properly after some middlewares. Here is my another question that I posted a while ago reporting the same - OWIN Middleware & CallContext
I am trying to set up my AutoFac registration in such a way that this test passes:
[Test]
public void Autofac_registration_test()
{
// Given
var builder = new ContainerBuilder();
RegisterServices(builder);
var container = builder.Build();
// When
var firstHub = container.Resolve<Hub>();
var secondHub = container.Resolve<Hub>();
// Then
firstHub.Should().NotBe(secondHub);
firstHub.FooRepo.Context.Should().Be(firstHub.BarRepo.Context);
firstHub.FooRepo.Context.Should().NotBe(secondHub.FooRepo.Context);
}
i.e. I want to use the same Context object all the way down within a single Hub, but use a different one when a new Hub is created.
RegisterServices is currently just:
private void RegisterServices(ContainerBuilder builder)
{
builder.RegisterType<MyHub>();
builder.RegisterType<FooRepo>();
builder.RegisterType<BarRepo>();
builder.RegisterType<Context>(); // How should I scope this?
}
Which fails at firstHub.FooRepo.Context.Should().Be(firstHub.BarRepo.Context); because Context is transiently scoped.
But scoping context per lifetime also fails, this time at firstHub.FooRepo.Context.Should().NotBe(secondHub.FooRepo.Context);.
It feels like this is a reasonable thing to want to do, so am I missing anything obvious out-of-the-box here?
Or will I have to do something manual to track Hub creation?
(For context, this is for a SignalR app. Hubs are created per SignalR request, so this was an attempt to match the unit-of-work lifetime of an HTTP request in normal webby situations).
What #Steven said in his comment was correct, I needed a per-object-graph lifestyle.
Castle.Windsor supports this, so I swicthed to using that for my dependency injection instead of AutoFac. The registration now looks like:
container.Register(Component.For<Hub>().LifestyleTransient());
container.Register(Component.For<FooRepo>().LifestyleTransient());
container.Register(Component.For<BarRepo>().LifestyleTransient());
container.Register(Component.For<Context>().LifestyleBoundTo<Hub>()); // Important bit
For more information, see: http://docs.castleproject.org/Windsor.LifeStyles.ashx?HL=scope#Bound_8