I don't quite fully understand this situation, where AsyncLocal instance is set at a certain point in the AuthenticationHandler, but does not reach the controller, when it is injected into the constructor.
I've made it similar to how IHttpContextAccessor works, but still nowhere near. However, if I set the AsyncLocal from a Middleware, it reaches the controller. Also, setting the HttpContext.Items property from AuthenticationHandler works just fine.
Question: How is HttpContext able to retain Items property contents all the way, and is ASP.NET runtime disposing the captured ExecutionContext of my DomainContextAccessor for some security reason because of where it is being set at?
I've made a sample app to demo this use case. I'd really appreciate someone shedding the light on this problem.
You already have a good answer on "how should I fix this?" Here's more of a description of why it's behaving this way.
AsyncLocal<T> has the same semantics as logging scopes. Because it has those same semantics, I always prefer to use it with an IDisposable, so that the scope is clear and explicit, and there's no weird rules around whether a method is marked async or not.
For specifics on the weird rules, see this. In summary:
Writing a new value to an AsyncLocal<T> sets that value in the current scope.
Methods marked async will copy their scope to a new scope the first time it's written to (and it's the new scope that is modified).
I've made it similar to how IHttpContextAccessor works, but still nowhere near.
I don't recommend copying the design of IHttpContextAccessor. It works... for that very specific use case. If you want to use AsyncLocal<T>, then use a design like this:
static class MyImplicitValue
{
private static readonly AsyncLocal<T> Value = new();
public static T Get() => Value.Value;
public static IDisposable Set(T newValue)
{
var oldValue = Value.Value;
Value.Value = newValue;
return new Disposable(() => Value.Value = oldValue);
}
}
usage:
using (MyImplicitValue.Set(myValue))
{
// Code in here can get myValue from MyImplicitValue.Get().
}
You can wrap that into an IMyImplicitValueAccessor if desired, but note that any "setter" logic should be using the IDisposable pattern as shown.
AsyncLocal instance is set at a certain point in the AuthenticationHandler, but does not reach the controller
That's because your AuthenticationHandler sets the value but doesn't call the controller after setting that value (and it shouldn't).
However, if I set the AsyncLocal from a Middleware, it reaches the controller.
That's because middleware is calls the next middleware (eventually getting to the controller). I.e., middleware is structured like this:
public async Task InvokeAsync(HttpContext context)
{
using (implicitValue.Set(myValue))
{
await _next(context);
}
}
So the controllers are in the scope of when that AsyncLocal<T> value was set.
How is HttpContext able to retain Items property contents all the way
Items is just a property bag. It doesn't have anything to do with AsyncLocal<T>. It exists because it's a property on HttpContext, and it persists because the same HttpContext instance is used throughout the request.
is ASP.NET runtime disposing the captured ExecutionContext of my DomainContextAccessor for some security reason because of where it is being set at?
Not exactly. The AsyncLocal<T> is being set just fine; it's just that the controllers are not called within the scope of that AsyncLocal<T> being set.
So what must be happening is there is a execution context change which wipes that value out. It works with in the middleware because your controller is in the same execution context as your middleware.
Change your code to this:
private static void DomainContextChangeHandler(AsyncLocalValueChangedArgs<DomainContextHolder> args)
{
Trace.WriteLine($"ThreadContextChanged: {args.ThreadContextChanged}");
Trace.WriteLine($"Current: {args.CurrentValue?.GetHashCode()}");
Trace.WriteLine($"Previous: {args.PreviousValue?.GetHashCode()}");
Trace.WriteLine($"Thread Id: {Thread.CurrentThread.ManagedThreadId}");
}
Now you can see when the context changes.
Here is something you could do:
private static void DomainContextChangeHandler(AsyncLocalValueChangedArgs<DomainContextHolder> args)
{
if (args.ThreadContextChanged && (args.PreviousValue != null) && (args.CurrentValue == null))
{
Trace.WriteLine(
"***** Detected context change with a previous value but setting current " +
"value to null. Resetting value to previous.");
_domainContextCurrent.Value = args.PreviousValue;
return;
}
Trace.WriteLine($"ThreadContextChanged: {args.ThreadContextChanged}");
Trace.WriteLine($"Current: {args.CurrentValue?.GetHashCode()}");
Trace.WriteLine($"Previous: {args.PreviousValue?.GetHashCode()}");
Trace.WriteLine($"Thread Id: {Thread.CurrentThread.ManagedThreadId}");
}
But, that kinda defeats the purpose of using AsyncLocal as your backing store.
My suggestion is you drop the AsyncLocal and use normal class-scoped storage:
namespace WebApp.Models
{
public interface IDomainContextAccessor
{
DomainContext DomainContext { get; set; }
}
public sealed class DomainContextAccessor : IDomainContextAccessor
{
public DomainContext DomainContext { get; set; }
}
}
And inject it as scoped instead of singleton:
services.AddScoped<IDomainContextAccessor, DomainContextAccessor>();
It will do exactly what you want without any kludges -- AND, the future you (or devs) will absolutely understand what's going on and why it is the way it is.
No middleware, no AsyncLocal funny-business. It just works.
Your answer is here:
.net core AsyncLocal loses its value
In your DomainContextAccessor class when you set new value in this line: _domainContextCurrent.Value = new DomainContextHolder { Context = value };
you create NEW ExecutionContext in current thread and child threads.
So I suppose that mvc runs like this:
Middleware thread => you set value => some child thread with Controller execution which sees parent changes
But for UserAuthenticationHandler it feels it works like this:
Some controller factory creates controller with injected IDomainContextAccessor (1 context) => mvc executes auth handler in child task where you set value and create 2 context. But it's value does not go UP to parent (where controller 1 context exists) because you create new context when you set value. Even more your code gets parents context, gets reference to its value and makes property Context = null, so you will get null in Controller.
So to fix this you need to change your code:
public class DomainContext
{
private static AsyncLocal<DomainContext> _contextHolder = new AsyncLocal<DomainContext>();
public static DomainContext Current
{
get
{
return _contextHolder.Value;
}
}
public Job JobInfo { get; set; }
public static void InitContext()
{
_contextHolder.Value = new DomainContext();
}
}
//using in middleware:
DomainContext.InitContext();
//using in auth handler:
DomainContext.Current.JobInfo = ...
In example above you don't change DomainContext reference in _contextHolder.Value;
It remains the same but you only change value of JobInfo in it later in auth handler
Related
I'm writing an ASP.NET Core API in which the dependency injection container provides me with instances of my DbContext class. I set the service lifetime of the DbContext to transient (which means that the container will create a new instance for every class and every request).
builder.Services.AddDbContext<ComparisonInfoContext>(dbContextOptions =>
dbContextOptions.UseSqlServer("Connection string"), ServiceLifetime.Transient, ServiceLifetime.Transient);
Here is my DbContext class:
public class ComparisonInfoContext : DbContext
{
public DbSet<ComparisonInfo> Comparisons { get; set; } = null!;
public ComparisonInfoContext(DbContextOptions<ComparisonInfoContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ComparisonInfo>().OwnsMany(c => c.DiscrepancyInfos);
}
}
I also have a BackgroundService class called JobChecker which I added by calling:
builder.Services.AddHostedService<JobChecker>();
In JobChecker, I have a function that runs every minute and the first thing that function does is call context.Comparisons.ToListAsync(). The first time the function runs everything works fine, but the second time, this call returns outdated values for some reason (The values it returns used to be in the database but were changed prior to this call being made).
What I think is happening is the following:
JobChecker (the BackgroundService class) receives it's instance of DbContext at the very start of the program.
The function inside JobChecker calls context.Comparisons.ToListAsync() and gets the correct values from the database (no problems here).
Some controller somewhere changes the values in the database using it's own instance of the DbContext.
The function inside JobChecker runs and calls context.Comparisons.ToListAsync(). It sees that it is already tracking instances with the same id which came from the previous call (step 2). These instances were not effected by the changes made by the controller in step 3. It does not materialize the values it got from the database, instead it returns the instances that already exist. This is explained more here: The life of a query
Does anyone know whether this is in fact what is happening? Is it even correct to set the service lifetime of the DbContext class to transient?
Your DB Context might be transient, but the BackgroundService is not. In effect you only have a single instance of the context in the BackgroundService.
I would look at injecting in an DB Context Factory:
services.AddDbContextFactory<ApplicationDbContext>(...)
Then inject one into your background service:
public class MyBackgroundService(IDbContextFactory<ApplicationDbContext> contextFactory)
Then every iteration of your loop in the background service (it sounds like it is running on a timer), you can use this to instantiate a new context.
using (var context = _contextFactory.CreateDbContext())
{
// ...
}
Docs:
https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/#using-a-dbcontext-factory-eg-for-blazor
I once faced the same problem with you ,and finally fixed it follow the document:
public SomeBackgroundService( IServiceProvider services)
{
......
}
using (var scope = Services.CreateScope())
{
var context = scope.ServiceProvider.GetService<BackgroundServiceProjContext>();
var personlist1 = context?.Person.ToList();
}
I modified the name and the context injected directly into backgroundservice still read the former value:
Hey Guys i'm very new in software development,I still no idea when to use which,whats the meaning of service lifetime!it may seem stupid but please help me,i have an interface :
public interface IAccessInfo
{
public IEnumerable<AccessInfo> getResult();
}
what it supposed to do is to returns me the information about my Turbines;here is the implementation of it :
public class AcessInfoData:IAccessInfo
{
private DbContextClass db;
public AcessInfoData(DbContextClass context)
{
db = context;
}
public IEnumerable<AccessInfo> getResult()
{
var turbines = (from c in db.accessinf
where s.user_id == "i0004912"
select new AccessInfo
{
InfoType = c.type,
TurbineId = c.m_plc_id.ToString(),
TurbineIP = c.turbine_ip.ToString(),
TurbineIdSorting = c.turbine_id,
Blade = c.blade,
Certification = c.certification,
}).Distinct();
return turbines;
}
}
it gets an instance of my DB and gets the data;and in my controller i use it like this:
public class AcessInfoController : ControllerBase
{
private IAccessInfo _acess;
public AcessInfoController(IAccessInfo access)
{
_acess = access;
}
[HttpGet]
public IActionResult Index()
{
var rsult = _acess.getResult();
return Ok( rsult);
}
}
now in the Startup i registered it :
services.AddScoped<IAccessInfo, AcessInfoData>();
it works,but if you sk me why i user Scoped and not Singleton or transient i have no idea why,really,any one can make it clear for me?
I will try to explain a little about the mentioned cases:
scoped : For all needs of an object during the life of an operation (such as a request from the client) a single instance of the object is created. (It means that only one instance of the object is sent for all requirements during life time of a request)
Singleton: Creates only one instance of object and sends it for all requirements in the application scope.(For all needs everywhere in the program, only one instance of the object is sent, a bit like static objects).
Transient: Ioc container, makes an instance of object whenever code needs it, that is, it makes an instance for each requirement anywhere in the program and at any time, which means that if the program needs an object 3 times, it makes an independent instance for each.
Instance: In this case, each time an object is needed, only one instance of it is provided to the program, which you defined it in the startup section. (when defining it in the startup section, you specify how to create an instance).
I hope to reduce some of the ambiguities.
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.
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"]);
}
}
I am looking for some help creating an implementation of IScopeAccessor, or finding a new solution, that will allow me to provide an NHibernate session per ViewModel.
I know that Windsor now supports scoped lifestyles as seen (here). However the example creates the special scope with a using block and calling container.resolve within the using.
_container.Register(Component.For<A>().LifestyleScoped());
using (_container.BeginScope())
{
var a1 = _container.Resolve<A>();
var a2 = _container.Resolve<A>();
Assert.AreSame(a1, a2);
}
I can't think of a way to make this work because I don't want to pass around the container and I want the scope to be tied to the ViewModel that gets created, which will happen dynamically as they are needed.
As an alternative it looks like I can create an implementation of IScopeAccessor which, according to Krzysztof Koźmic (here) would allow me to
"... provide any scope you like. Scope is an abstract term here and it can be anything."
Unfortunately I cannot find an implementation of IScopeAccessor that isn't specific to a web based scenario and I am struggling to understand exactly what I need to do to turn "anything" into a valid scope.
I have found an example of exactly what I want to do using Ninject (http://www.emidee.net/index.php/2010/08/23/ninject-use-one-database-session-per-view-model/):
Bind<ISession>().ToMethod(ctx =>
{
var session = ctx.Kernel.Get<....>().BuildSessionFactory().OpenSession();
return session;
})
.InScope(context =>
{
var request = context.Request;
if (typeof(IViewModel).IsAssignableFrom(request.Service))
return request;
while ((request = request.ParentRequest) != null)
if (typeof(IViewModel).IsAssignableFrom(request.Service))
return request;
return new object();
});
In Ninject, the InScope indicates that any instances created by the binding should be reused as long as the object returned by the call back remains alive. Essentially, this call back returns the root level ViewModel (since ViewModels can be nested).
Any thoughts on how I can do the same thing or get the same result using Windsor?
The problem seems to be the place of creation.
If it's all about dependencies of viewmodels being constructed, you could maybe use boud lifestyle, as described in What's new...
Or you could alternatively use your own scope accessor, that is sensitive to viewmodels. for example like this:
public class ViewModelScopeAccessor : IScopeAccessor
{
private IDictionary<Guid, ILifetimeScope> scopes = new Dictionary<Guid, ILifetimeScope>();
private ILifetimeScope defaultScope;
public ViewModelScopeAccessor()
: this(new DefaultLifetimeScope())
{ }
public ViewModelScopeAccessor(ILifetimeScope defaultScope)
{
this.defaultScope = defaultScope;
}
public ILifetimeScope GetScope(CreationContext context)
{
var creator = context.Handler.ComponentModel.Implementation;
var viewModel = creator as IViewModel;
if (viewModel != null)
{
ILifetimeScope scope;
if (!scopes.TryGetValue(viewModel.UID, out scope))
{
scope = new DefaultLifetimeScope();
scopes[viewModel.UID] = scope;
}
return scope;
}
else
{
return defaultScope;
}
}
public void Dispose()
{
foreach (var scope in scopes)
{
scope.Value.Dispose();
}
defaultScope.Dispose();
scopes.Clear();
}
}
for the following viewmodel interface :
public interface IViewModel
{
string DisplayName { get; }
Guid UID { get; }
}
You of course could compare the viewmodels in other ways, it's just an example.
The drawback of both, the bound lifestyle and that scope accessor, is, that it won't work, if you use a typed factory inside your viewmodel, to lazily construct objects, since the scope accessor has no idea, from which object/method its factory method was called. But I think is is a general .NET issue, since a method does actually never know, from where it has been called.
So, you could then use your own factories, that produce only one instance per factory instance and make them scoped to your viewmodels too.
Hope this helps.