What is meant by the word "request" in the context of a transient service? - asp.net-core

Suppose a service meant to be dependency injected (DI) is as follows.
public interface IWorkService
{
Task<JobResult> DoWorkAsync(string JobId,
JobPostParameters jobParameters,
CancellationToken cancellationToken);
}
public sealed class WorkService : IWorkService
{
private readonly IJobService jobService;
private readonly IHttpClientFactory httpClientFactory;
public WorkService(
IJobService jobService,
IHttpClientFactory httpClientFactory)
{
this.jobService = jobService;
this.httpClientFactory = httpClientFactory;
}
public async Task<JobResult> DoWorkAsync(string jobId,
JobPostParameters jobParameters,
CancellationToken cancellationToken)
{
// omissions...
}
}
Suppose I intend to register the service as transient.
Transient lifetime services are created each time they're requested
from the service container.
That quote comes from this reference. In this situation does the "request" constitute construction followed by exactly one method call followed by destruction? In other words the service object should not contain any private data that survives from one method call to the next?
Edit
I am asking about IWorkService (not the IJobService).

The effective lifetime depends on how IWorkService is created / requested from the dependency injection container.
If there is, for example, a singleton CallerService which gets a IWorkService constructor-injected, that service will use the same IWorkService for each call.
On the other hand, if the CallerService uses an IServiceProvider (for example via an IServiceScopeFactory) - each time GetService is used to get the IWorkService - a new instance will be created.
If the CallerService itself is transient-scoped, the question is shifted to who creates that service at which time.
To sum up, the word "request" describes the act of calling the service provider's GetService method (directly or via some helper method). For each such call, a new instance will be created, if the desired service is transient-scoped.

Related

Confused about using the very same HTTPContextAccessor instance through the entire ASP.NET Core 3 application?

I've read about configuring IHttpContextAccessor as services.AddSingleton scope, but I also read about it is working "async local", and I am also aware of the sophisticated working of async in ASP.Net, I mean for example if a controller action method is async, and it await for an async call, then it may continue with an other thread, but magically some thread bound things with maintained (like HttpContext)
My concrete use case: I have to inject a MyConverter class to my EF Core DbContext which uses it in OnModelCreating. However this model is cached by the DbContext, so any subsequent request, even it will have a brand new instance of DbContext will use this very same model so the very same MyConverter instance. (even it has configured services.AddTransient). This MyConverter has a constructor and an injected IHttpContextAccessor, so based on the very similar reasons, it effectively will be also a singleton for all DbContext/MyConverter usages.
Question
This particular HttpContextAccessor instance which is created in the very first request will serve all the subsequent requests in the Web app lifecycle. Will it work correctly? Is there any (concurrency) trap here?
(Do I suppose correctly that it does not really matter if we use a single or multiple HttpContextAccessor instances, because its implementation of getting HttpContext will use the correct way including async local thread switch traps etc to return with the correct HttpContext?)
Short answer: register as services.AddHttpContextAccessor() and then you can inject IHttpContextAccessor wherever you want and it'll work as long as you're using it in the request's execution context. For instance you can't read HTTP request headers for code that was not initiated by a HTTP request.
You're right that the IHttpContextAccessor should be registered as a singleton. Instead of doing it yourself, the recommendation is to use AddHttpContextAccessor() extension method. See source code here. It internally registers an HttpContextAccessor as a singleton.
The code for HttpContextAccessor can be found here, which I'm also pasting below:
public class HttpContextAccessor : IHttpContextAccessor
{
private static AsyncLocal<HttpContextHolder> _httpContextCurrent = new AsyncLocal<HttpContextHolder>();
public HttpContext HttpContext
{
get
{
return _httpContextCurrent.Value?.Context;
}
set
{
var holder = _httpContextCurrent.Value;
if (holder != null)
{
// Clear current HttpContext trapped in the AsyncLocals, as its done.
holder.Context = null;
}
if (value != null)
{
// Use an object indirection to hold the HttpContext in the AsyncLocal,
// so it can be cleared in all ExecutionContexts when its cleared.
_httpContextCurrent.Value = new HttpContextHolder { Context = value };
}
}
}
private class HttpContextHolder
{
public HttpContext Context;
}
}
Since the HttpContext getter property returns from an async local field, you always get the HttpContext local to the execution context.
The HttpContext field is set in HttpContextFactory.Create() only if IHttpContextAccessor was registered with the DI. Source.
And HttpContextFactory.Create() is invoked from [HostingApplication](https://github.com/aspnet/AspNetCore/blob/v2.2.5/src/Hosting/Hosting/src/Internal/HostingApplication.cs) where the context is setup.

Container.GetInstance(Type) when using WcfOperationLifestyle throws ActivationException

I have a WebAPI service using SimpleInjector. I have this set up using AsyncScopedLifestyle for my scoped dependencies, and one of these dependencies is my Entity Framework DataContext. Many things in my service depend on the DataContext, and it is generally injected in to my MediatR handlers using constructor injection - this works well. Separately I have a few areas where I need to create an instance of an object given its type (as a string), so I have created a custom activator class (ResolvingActivator) that is configured with a reference to Container.GetInstance(Type):
In my container bootstrap code:
ResolvingActivator.Configure(container.GetInstance);
I can then create objects by using methods such as:
ResolvingActivator.CreateInstance<T>(typeName)
When I'm using WebAPI, the above is working perfectly.
A further part of the project is a legacy API that uses WCF. I have implemented this as a translation layer, where I translate old message formats to new message formats and then dispatch the messages to the Mediator; I then translate the responses (in new format) back to old format and return those to the caller. Because I need access to the Mediator in my WCF services, I'm injecting this in their constructors, and using the SimpleInjector.Integration.Wcf package to let SimpleInjector's supplied SimpleInjectorServiceHostFactory build instances of the services. I've also created a hybrid lifestyle, so I can use the same container for my both my WebAPI and WCF services:
container.Options.DefaultScopedLifestyle = Lifestyle.CreateHybrid(
new AsyncScopedLifestyle(),
new WcfOperationLifestyle());
This works well for some calls, but when a call ultimately calls my ResolvingActivator class, I get an ActivationException thrown, with the following message:
The DataContext is registered as 'Hybrid Async Scoped / WCF Operation' lifestyle, but the instance is requested outside the context of an active (Hybrid Async Scoped / WCF Operation) scope.
As I only receive this error when making WCF calls, I'm wondering if I have something wrong in my configuration. In a nutshell, this will work:
public class SomeClass
{
private readonly DataContext db;
public SomeClass(DataContext db)
{
this.db = db;
}
public bool SomeMethod() => this.db.Table.Any();
}
But this will not:
public class SomeClass
{
public bool SomeMethod()
{
// Code behind is calling container.GetInstance(typeof(DataContext))
var db = ResolvingActivator.CreateInstance<DataContext>();
return db.Table.Any();
}
}
Any ideas where I'm going wrong?
Edit: here is the stack trace from the ActivationException:
at SimpleInjector.Scope.GetScopelessInstance[TImplementation](ScopedRegistration`1 registration)
at SimpleInjector.Scope.GetInstance[TImplementation](ScopedRegistration`1 registration, Scope scope)
at SimpleInjector.Advanced.Internal.LazyScopedRegistration`1.GetInstance(Scope scope)
at lambda_method(Closure )
at SimpleInjector.InstanceProducer.GetInstance()
at SimpleInjector.Container.GetInstance(Type serviceType)
at Service.Core.ResolvingActivator.CreateInstance(Type type) in Service.Core\ResolvingActivator.cs:line 43
at Service.Core.ResolvingActivator.CreateInstance(String typeName) in Service.Core\ResolvingActivator.cs:line 35
at Service.Core.ResolvingActivator.CreateInstance[TService](String typeName) in Service.Core\ResolvingActivator.cs:line 69
With a full stack trace here: https://pastebin.com/0WkyHGKv
After close inspection of the stack trace, I can conclude what's going on: async.
The WcfOperationLifestyle under the covers depends on WCF's OperationContext.Current property, but this property has a thread-affinity and doesn't flow with async operations. This is something that has to be fixed in the integration library for Simple Injector; it simply doesn't support async at the moment.
Instead, wrap a decorator around your handlers that start and end a new async scope. This prevents you from having to use the WcfOperationLifestyle all together. Take a look at the ThreadScopedCommandHandlerProxy<T> implementation here to get an idea how to do this (but use AsyncScopedLifestyle instead).

Autofac.Multitenant in an aspnet core application does not seem to resolve tenant scoped dependencies correctly

I'm in the process of upgrading a Multitenant dotnet core solution which utilises the Autofac.Multitenant framework. I'm not having a lot of luck getting tenancy resolution working correctly. I've created a simple demonstration of the problem here: https://github.com/SaltyDH/AutofacMultitenancy1
This repo demonstrates registering a InstancePerTenant scoped dependency TestMultitenancyContext which is resolved in the Home Controller. Due to issues with using IHttpContextAccessor, I'm using a custom RequestMiddleware class to capture the current HttpContext object so that I can perform logic on the current HttpContext request object in the MultitenantIdentificationStrategy.
Finally, TestFixture provides a simple xUnit test which, at least on my machine returns "tenant1" for both tenants.
Is there something I've missed here or is this just not currently working?
UPDATE 10/6/2017: We released Autofac.AspNetCore.Multitenant to wrap up the solution to this in a more easy to consume package. I'll leave the original answer/explanation here for posterity, but if you're hitting this you can go grab that package and move on.
I think you're running into a timing issue.
If you pop open the debugger on the HttpContext in the middleware you can see that there's a RequestServicesFeature object on a property called ServiceProvidersFeature. That's what's responsible for creating the per-request scope. The scope gets created the first time it's accessed.
It appears that the order goes roughly like this:
The WebHostBuilder adds a startup filter to enable request services to be added to the pipeline.
The startup filter, AutoRequestServicesStartupFilter, adds middleware to the very beginning of the pipeline to trigger the creation of request services.
The middleware that gets added, RequestServicesContainerMiddleware, basically just invokes the RequestServices property from the ServiceProvidersFeature to trigger creation of the per-request lifetime scope. However, in its constructor is where it gets the IServiceScopeFactory that it uses to create the request scope, which isn't so great because it'll be created from the root container before a tenant can be established.
All that yields a situation where the per-request scope has already been determined to be for the default tenant and you can't really change it.
To work around this, you need to set up request services yourself such that they account for multitenancy.
It sounds worse than it is.
First, we need a reference to the application container. We need the ability to resolve something from application-level services rather than request services. I did that by adding a static property to your Startup class and keeping the container there.
public static IContainer ApplicationContainer { get; private set; }
Next, we're going to change your middleware to look more like the RequestServicesContainerMiddleware. You need to set the HttpContext first so your tenant ID strategy works. After that, you can get an IServiceScopeFactory and follow the same pattern they do in RequestServicesContainerMiddleware.
public class RequestMiddleware
{
private static readonly AsyncLocal<HttpContext> _context = new AsyncLocal<HttpContext>();
private readonly RequestDelegate _next;
public RequestMiddleware(RequestDelegate next)
{
this._next = next;
}
public static HttpContext Context => _context.Value;
public async Task Invoke(HttpContext context)
{
_context.Value = context;
var existingFeature = context.Features.Get<IServiceProvidersFeature>();
using (var feature = new RequestServicesFeature(Startup.ApplicationContainer.Resolve<IServiceScopeFactory>()))
{
try
{
context.Features.Set<IServiceProvidersFeature>(feature);
await this._next.Invoke(context);
}
finally
{
context.Features.Set(existingFeature);
_context.Value = null;
}
}
}
}
Now you need a startup filter to get your middleware in there. You need a startup filter because otherwise the RequestServicesContainerMiddleware will run too early in the pipeline and things will already start resolving from the wrong tenant scope.
public class RequestStartupFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return builder =>
{
builder.UseMiddleware<RequestMiddleware>();
next(builder);
};
}
}
Add the startup filter to the very start of the services collection. You need your startup filter to run before AutoRequestServicesStartupFilter.
The ConfigureServices ends up looking like this:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.Insert(0, new ServiceDescriptor(typeof(IStartupFilter), typeof(RequestStartupFilter), ServiceLifetime.Transient));
services.AddMvc();
var builder = new ContainerBuilder();
builder.RegisterType<TestMultitenancyContext>().InstancePerTenant();
builder.Populate(services);
var container = new MultitenantContainer(new MultitenantIdentificationStrategy(), builder.Build());
ApplicationContainer = container;
return new AutofacServiceProvider(container);
}
Note the Insert call in there to jam your service registration at the top, before their startup filter.
The new order of operations will be:
At app startup...
Your startup filter will add your custom request services middleware to the pipeline.
The AutoRequestServicesStartupFilter will add the RequestServicesContainerMiddleware to the pipeline.
During a request...
Your custom request middleware will set up request services based on the inbound request information.
The RequestServicesContainerMiddleware will see that request services are already set up and will do nothing.
When services are resolved, the request service scope will already be the tenant scope as set up by your custom request middleware and the correct thing will show up.
I tested this locally by switching the tenant ID to come from querystring rather than host name (so I didn't have to set up hosts file entries and all that jazz) and I was able to switch tenant by switching querystring parameters.
Now, you may be able to simplify this a bit. For example, you may be able to get away without a startup filter by doing something directly to the web host builder in the Program class. You may be able to register your startup filter right with the ContainerBuilder before calling builder.Populate and skip that Insert call. You may be able to store the IServiceProvider in the Startup class property if you don't like having Autofac spread through the system. You may be able to get away without a static container property if you create the middleware instance and pass the container in as a constructor parameter yourself. Unfortunately, I already spent a loooot of time trying to figure out the workaround so I'm going to have to leave "optimize it" as an exercise for the reader.
Again, sorry this wasn't clear. I've filed an issue on your behalf to get the docs updated and maybe figure out a better way to do this that's a little more straightforward.
I have an alternate solution, related to work I've done on a pending PR on the Autofac DI extension. The solution there can't be used exactly, because it depends on classes that are (rightly) internal. It can be adapted by providing shims that reproduce the functionality in those classes. Since they are compact, this doesn't require the addition of a lot of code. Until the functionality is fixed, this is the solution I'm using.
The other aspect of the solution is to eschew the custom middleware and instead make the ITenantIdentificationStrategy a service that can take any dependency required to do what it needs to.
Fixing the DI
The "DI" side of the problem is that the Autofac DI extension uses resolution to supply IServiceProvider and IServiceScopeFactory implementations. This is possible, because under the hood these are IComponentContext and ILifetimeScope (which are themselves different interfaces for the same thing). In most cases this works fine, but ASP.NET Core proceeds by resolving a singleton IServiceScopeFactory very early in the application cycle. In a multi-tenant scenario this resolution will return the ILifetimeScope for either the first tenant requested, or for the "default" tenant, and that will be the root scope (as far as MS DI is concerned) for the application lifetime. (See the PR for further discussion.)
The classes below implement an alternate behavior: instead of resolving the DI interfaces, it builds (news-up) the initially-requested ones from the IContainer directly. With the initial IServiceScopeFactory based directly on IContainer, further scope requests will resolve correctly.
public class ContainerServiceProvider : IServiceProvider, ISupportRequiredService
{
private readonly IContainer container;
public ContainerServiceProvider(IContainer container)
{
this.container = container;
}
public object GetRequiredService(Type serviceType)
{
if (TryGetContainer(serviceType, out object containerSvc)) return containerSvc;
else return container.Resolve(serviceType);
}
public object GetService(Type serviceType)
{
if (TryGetContainer(serviceType, out object containerSvc)) return containerSvc;
else return container.ResolveOptional(serviceType);
}
bool TryGetContainer(Type serviceType, out object containerSvc)
{
if (serviceType == typeof(IServiceProvider)) { containerSvc = this; return true; }
if (serviceType == typeof(IServiceScopeFactory)) { containerSvc = new ContainerServiceScopeFactory(container); return true; }
else { containerSvc = null; return false; }
}
}
// uses IContainer, but could use copy of AutofacServiceScopeFactory
internal class ContainerServiceScopeFactory : IServiceScopeFactory
{
private IContainer container;
public ContainerServiceScopeFactory(IContainer container)
{
this.container = container;
}
public IServiceScope CreateScope()
{
return new BecauseAutofacsIsInternalServiceScope(container.BeginLifetimeScope());
}
}
// direct copy of AutofacServiceScope
internal class BecauseAutofacsIsInternalServiceScope : IServiceScope
{
private readonly ILifetimeScope _lifetimeScope;
/// <summary>
/// Initializes a new instance of the <see cref="AutofacServiceScope"/> class.
/// </summary>
/// <param name="lifetimeScope">
/// The lifetime scope from which services should be resolved for this service scope.
/// </param>
public BecauseAutofacsIsInternalServiceScope(ILifetimeScope lifetimeScope)
{
this._lifetimeScope = lifetimeScope;
this.ServiceProvider = this._lifetimeScope.Resolve<IServiceProvider>();
}
/// <summary>
/// Gets an <see cref="IServiceProvider" /> corresponding to this service scope.
/// </summary>
/// <value>
/// An <see cref="IServiceProvider" /> that can be used to resolve dependencies from the scope.
/// </value>
public IServiceProvider ServiceProvider { get; }
/// <summary>
/// Disposes of the lifetime scope and resolved disposable services.
/// </summary>
public void Dispose()
{
this._lifetimeScope.Dispose();
}
}
Fixing Identification Strategy
As for making the identification-strategy a service, I would rework your implementation like so:
public class MultitenantIdentificationStrategy : ITenantIdentificationStrategy
{
public const string DefaultTenantId = null;
private readonly IHttpContextAccessor contextaccessor;
public MultitenantTenantIdentificationStrategy(IHttpContextAccessor contextaccessor)
{
this.contextaccessor = contextaccessor;
}
public bool TryIdentifyTenant(out object tenantId)
{
var context = contextaccessor.HttpContext;
// after this is unchanged
.
.
}
.
.
}
Use in Startup.ConfigureServices
This shows the fragment of how these last few pieces are registered and fed to MS DI for ASP.NET.
. . .
builder.RegisterType<MultitenantIdentificationStrategy>().AsImplementedInterfaces(); // tenant identification
// register do Autofac DI integration
builder.Populate(services);
var underlyingcontainer = builder.Build();
ApplicationContainer = new MultitenantContainer(underlyingcontainer.Resolve<ITenantIdentificationStrategy>(), underlyingContainer);
return new ContainerServiceProvider(ApplicationContainer);
If you find this solution workable, please give a thumbs up to DI PR 10--or PR 11, if after reviewing you think that is the better/more elegant solution. Either will save having to add the "shim" code above.

How to access Request.Properties outside of Web API Controller

I'm setting a Property on Request.Properties inside a DelegatingHandler after I pluck some data out of a header on an incoming request to a Web API.
This all works fine. I can also access Request.Properties from within the controller as well as in my Action and Exception filters. However, I also need to access this data from outside of the controller (I call a business layer class from the controller). It is data I want to include in some logs in other places,
I can see HttpContext.Current from this class, and I can see the original header from here, so I guess I could pluck it out again, but since I have already done this and put it in the Properties it seems to make more sense to get it from there. However, I don't seem to have access to the Request.Properties from anywhere else.
If this isn't the right way to do this, how else would I pass around this per-request data so that it was accessible from anywhere on the stack in Web API?
I also need to access [Request.Properties] data from outside of the controller (I call a business layer class from the controller). It is data I want to include in some logs in other places... However, I don't seem to have access to the Request.Properties from anywhere else. If this isn't the right way to do this, how else would I pass around this per-request data so that it was accessible from anywhere on the stack in Web API?
You can get it from HttpContext.Current, though it is less than ideal. Keep in mind that if any other non-web applications consume the same business layer, then HttpContext.Current would be null. HttpContext.Current is only non-null when you are running in IIS, and an IIS thread is handling the execution of the request stack. If you ever plan to self-host the web api using OWIN without IIS, there will be no HttpContext.Current.
Personally, if the data really is important enough to be passed into the business layer to be logged, then I would just pass it to the business layer method:
public Task<HttpResponseMessage> SomeAction(SomeModel model) {
... other code
someBusinessLayerObject.SomeMethod(arg1, arg2, Request.Properties["myHeaderKey"]);
}
...If you need other values from Request.Properties, then you can just pass the whole dictionary to the methods that will end up using its values.
A third option if you are using an inversion of control container would be to add some kind of scoped object dependency class and put the data in there. Then constructor inject it into your business layer class:
public interface IHaveRequestData {
IDictionary<string, object> Properties { get; set; }
}
public class RequestData : IHaveRequestData {
public IDictionary<string, object> Properties { get; set; }
}
// ioc registration pseudocode
iocContainer.Register<IHaveRequestData, RequestData>(Lifetime
.WhateverYouNeedSoThatOneOfTheseGetsCreatedForEachWebRequest);
public class SomeController : ApiController {
private readonly IHaveRequestData RequestData;
public SomeController(IHaveRequestData requestData) {
RequestData = requestData;
}
public Task<HttpResponseMessage> SomeAction() {
// you may even be able to do this part in an action filter
RequestData.Properties = Request.Properties;
}
}
public class SomeBusinessLayerComponent {
private readonly IHaveRequestData RequestData;
private readonly ILog Log;
public SomeBusinessLayerComponent(IHaveRequestData requestData, ILog log) {
RequestData = requestData;
Log = log;
}
public Task SomeMethod() {
Log.Info(RequestData["myHeader"]);
}
}

HttpContextBase.Request exception when using Ninject MVC3

I have a service that takes a dependency on HttpContextBase.
Ninject is injecting this for me already as it's set up in the MvcModule to return new HttpContextWrapper(HttpContext.Current) when HttpContextBase is requested
I want to use this service in Application_AuthenticateRequest, so i'm using property injection so that Ninject resolves it for me
When I try and access Request.UserHostAddress on the HttpContextBase I get a Value does not fall within the expected range exception
If I call HttpContext.Current.Request.UserHostAddress directly it works without problems
ExampleService.cs
public class ExampleService : IExampleService {
HttpContextBase _contextBase;
public ExampleService(HttpContextBase contextBase) {
_contextBase = contextBase;
}
public void DoSomething() {
var ip = HttpContext.Current.Request.UserHostAddress; <== this works
ip = _contextBase.Request.UserHostAddress; <== this fails
}
}
Global.asax
[Inject]
public IExampleService ExampleService { get; set; }
public void Application_AuthenticateRequest() {
ExampleService.DoSomething();
}
I'm missing something here, but I can't see what
Dependencies that are injected into classes live as long as the the class they get injected into, because the class holds a reference to them. This means that in general you should prevent injecting dependencies that are configured with a lifetime that is shorter than the containing class, since otherwise their lifetime is 'promoted' which can cause all sorts of (often hard to track) bugs.
In the case of an ASP.NET application, there is always just one HttpApplication instance that lives as long as the AppDomain lives. So what happens here is that the injected ExampleService gets promoted to one-per-appdomain (or singleton) and since the ExampleService sticks around, so does its dependency, the HttpContextBase.
The problem here of course is that an HTTP context -per definition- can't outlive a HTTP request. So you're storing a single HttpContextBase once, but it gets reused for all other requests. Fortunately ASP.NET throws an exception, otherwise you would probably be in much more trouble. Unfortunately the exception isn't very expressive. They could have done better in this case.
The solution is to not inject dependencies in your HttpApplication / MvcApplication. Ever! Although it's fine to do so when you're injecting singletons that only depend on singletons recursively, it is easy to do this wrong, and there's no verification mechanism in Ninject that signals you about this error.
Instead, always resolve IExampleService on each call to AuthenticateRequest. This ensures that you get an ExampleService with the right lifetime (hopefully configured as per-web-request or shorter) and prevents this kind of error. You can either call into the DependencyResolver class to fetch an IExampleService or call directly into the Ninject Kernel. Calling into the Kernel is fine, since the Application_AuthenticateRequest can be considered part of the Composition Root:
public void Application_AuthenticateRequest() {
var service = DependencyResolver.Current.GetService<IExampleService>();
service.DoSomething();
}