Worker queue and user context - c#

We have a worker queue that a user can add work to. When the worker item is added the context is the users (HttpContext). But its a background thread that polls the queue and executes the items one by one in order.
I cant just store the User because when the HttpContext is disposed so will the Principal object
The code that can run in the worker needs the Principal to be correct for stuff like PrincipalPermissions etc.
Also, Lifetime management (IoC) uses the HttpContext for InRequest scopes, is it possible to recreate a HttpContext with the correct principal etc.
edit:
Faking HttpContext is just a nice to have feature for Life time management, this I can work around.
But our backend code heavily depends on having the correct user principal for the thread since we use this to validate if user has access to that part of the system. I would mark as answer if someone can answer how to store a user principal with identity, roles and IsAuthenticated state and later use that on another thread

Your best practice for consuming stateful data from the HttpContext is to create your own application specific context which accepts an HttpContext in the constructor (Dependency Injected).
Your business logic should never be dependent on an HttpContext but rather your new application specific context (which may have been created using info from an HttpContext).
This will not only solve your above problems, but also increase testability of your code.
Example:
public class MyApplicationContext
{
public IPrincipal ContextPrincipal { get; set; }
public MyApplicationContext(HttpContext httpContext)
{
// Store the current user principal & identity
ContextPrincipal = httpContext.User;
// Need to grab anything else from the HttpContext? Do it here!
// That could be cookies, Http request header values, query string
// parameters, session state variables, etc.
//
// Once you gather up any other stateful data, store it here in
// your application context object as the HttpRequest can't be passed
// to another thread.
}
}
public class MyHttpHandler : IHttpHandler
{
#region IHttpHandler Members
public bool IsReusable
{
// Return false in case your Managed Handler cannot be reused for another request.
// Usually this would be false in case you have some state information preserved per request.
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
// Do some work on another thread using the ThreadPool
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), new MyApplicationContext(context));
}
public void DoWork(object state)
{
// Grab our state info which should be an instance of an
// MyApplicationContext.
MyApplicationContext context = (MyApplicationContext) state;
// Assign this ThreadPool thread's current principal according
// to our passed in application context.
Thread.CurrentPrincipal = context.ContextPrincipal;
// Check if this user is authenticated.
if (context.ContextPrincipal.Identity.IsAuthenticated)
{
var userName = context.ContextPrincipal.Identity.Name;
}
// Check if this user is an administrator.
if (context.ContextPrincipal.IsInRole("Administrator"))
{
}
// Do some long-ish process that we need to do on the threadpool
// after the HttpRequest has already been responded to earlier.
//
// This would normally be some fancy calculation/math, data
// operation or file routines.
for (int i = 0; i < 30; i++)
{
Thread.Sleep(1000);
}
}
#endregion
}
Neither the IPrincipal nor IIdentity interface explicitly offer a dispose method. So they should both be ok to keep a reference to them. However, I haven't tested the above code, I wrote it just for this question.
If by some poor design they actually do depend on an underlying database connection to query the roles membership, you'd simply have to evaluate that earlier in the constructor of your application context while the HttpContext and asp.net forms authentication provider are still non disposed/closed.
You can always take apart the principal and identity and recreate a new instance of GenericPrincipal and GenericIdentity or even create your application Identity class which implements IIdentity. There is lots of room for customization/extension here.

public void TestMethod1()
{
System.Net.WebClient client = new System.Net.WebClient();
client.BaseAddress = "http://www.teejoo.com";
//Invoke your function here
client.OpenReadAsync(new Uri("http://www.teejoo.com/YourLogicalPage.aspx"));
//Pur your logical in your page, so you can use httpContext
client.OpenReadCompleted += new System.Net.OpenReadCompletedEventHandler(client_OpenReadCompleted);
}
void client_OpenReadCompleted(object sender, System.Net.OpenReadCompletedEventArgs e)
{
//to Check the response HERE
}

Why don't you use an auxiliar class to hold the information you need? You can create it during the web request with the appropriate values and pass it down as an argument to the background worker.
Cloning the HTTPContext object is not possible because of the internal server session state. Even if it were possible, using it outside of a real HTTP request just to check for values doesn't seem like a good solution.

Related

User session in background task in ASP.NET Core

I learned that the preferred ASP.NET-Core-way for background tasks is a hosted service.
Is there any way to pass the (user) session to this background worker? Usually the session is part of the HttpContext which will not be available in the background task...
I know that I could persist data by writing to the database but I'd also like to cache some data in memory.
If this is all happening in-process, then you should be able to just pass the ISession object directly to your background service and have it interact with the user’s session without the user’s HttpContext having to be around.
The default session implementation is using the distributed cache to persist the information stored in the session. Each session object is then only interacted by with a session key that is stored in a user cookie. When the session object is being created, no direct or indirect reference to the HttpContext is being passed. The session store itself also does not access the HttpContext in some other way. The distributed cache itself is also independent of the context and also the DI service scope.
So at least in theory, this should work just fine. You can use the ISession object to modify the session and the next time the user makes a request with their session id, the updated information will be there waiting for them.
There is no guarantee the user session will exist when executing the background task. Encapsulate the data your task requires from the user session and pass it to the background task.
As you know session is based on this user request. User owns the session!
So when we are talking about session, actually we are pointing to the end-user-request received from the client! Imagine a situation that you have a background task in an aspnetcore-based micro-service with no user requests. Never ever you wont see any sessions to capture because there is no user to send any request.
In a normal sunny day, the probability of user session existence inside a background task is very low.
BUT!
If you have a background service that you want to use it as a cache, you should execute cache read/write operations per user request. I highly recommend you to avoid using HttpContext inside your background task, because your task will be non-extendable, tightly-coupled with http infrastructure.
There is a simple SAMPLE :D to be more clarified:
public interface ICache {
Task Write(string uniqueIdentifier, object data);
Task<object> Read(string uniqueIdentifier);
}
public class BackgroundTaskBasedCache : ICache {
public void Init()
{
//Initialize your background operations.
}
//IO bound
public async Task Write(string sessionId, object data)
{
//write inside your cache.
}
public async Task<object> Read(string sessionId)
{
//read from your cache.
return new object();//for test.
}
}
startup.cs:
//this will add a signleton background task. (you can do it manually or using other tools/strategies.
services.AddSingleton<ICache, BackgroundTaskBasedCache>();
inside your controller:
{
public TestController(ICache cache, IHttpContextAccessor){//fill properties}
public async Task<IActionResult> ExecuteSomeRequestAction()
{
await cache.Write(httpContextAccessor.HttpContext.SessionId, data);
}
}
hope to understand and best regards :)

HttpContext.Current property is null in Sqldependency event MVC

I am working on a multi-tenant project in which each tenant have its own customers. Currently i am implementing a real time stuff in which whenever any customer get registered or existing customer edit his profile info i want to notify the tenant of that customer in real time (using signalR).
For detecting changes in customer table i am using SqlDependency and an SqlTrigger. Trigger will do the entry in the notification table (which basically contains some Ids (like TenantId, CustomerId) which get affected and some other info) and in an event fired by Sqldependency i am calling the method which will further calls the client method using signalR context.
Now my problem is that i can't access the HttpContext.Current thread in Sqldependency event it is null because no request happen for its execution. Check this image:
Please correct me if i am wrong. I want to access this HttpContext.Current because i want to access the current Tenant to notify. And my TenantProvider implementation gets the tenant from the Url Host.
So my first question is, there is any way to access the context in which the event function code is executing ??
If not then i have another option with some doubts. The option is that i am saving the Changes in Notification table which contains the TenantId, CustomerId etc info. I can access the notification with the latest timestamp and from that entry i can get the tenantId to notify.
Now the doubt if 2 or more customer get registered simultaneously and before accessing the latest timestamp notification entry, trigger enter other notification also than in that case we missed some notification.
So my second question is whether i am over-think on this or this really be a problem. If this is really be a problem then please let me know the solution ??
The only thing that concerns me about the way you want to do it is the concurrency of your updates, for exactly the reason you describe, the solution is to make sure you are using a single instance of your tracking hub, and a concurrent dictionary for your data that you modify in your hub. Your using the notifications table, which is not actually necessary.
here are a couple of links for examples that might help you with what you are trying to do.
http://techbrij.com/database-change-notifications-asp-net-signalr-sqldependency
http://www.asp.net/signalr/overview/signalr-20/getting-started-with-signalr-20/tutorial-server-broadcast-with-signalr-20
the first is sqldependency example and the second is a central server hub style app, that show how to do thread safe centralized hub.
For the issue with the Httpcontext, I use a helper class that I keep in my datalayer, I use a repository pattern for development for a single thread hub.
Here is my class.
using System.Web;
namespace DataLayer.Common
{
public class ConnectionHelper : IConnectionHelper
{
private ApplicationDbContext _context;
public ApplicationDbContext Context
{
get
{
if (_context == null && HttpContext.Current.Items["DbActiveContext"] != null)
{
_context = (ApplicationDbContext)HttpContext.Current.Items["DbActiveContext"];
}
else if (_context == null && HttpContext.Current.Items["DbActiveContext"] == null)
{
_context = new ApplicationDbContext();
HttpContext.Current.Items.Add("DbActiveContext", _context);
}
return _context;
}
set { _context = value; }
}
}
}
Add these two steps so you can access HttpContext.Current.Session from inside Customer_OnChange, which I hope will get you close enough to the user context you need.
Firstly, when you instantiate this class, store HttpContext.Current.Session in a property. This helps your instance remember the current session.
HttpSessionState session;
public EntityChangeNotifier()
{
session = HttpContext.Current.Session;
}
Secondly, after you instantiate this class, store your new instance as a session variable. This keeps the instance alive throughout the user session.
EntityChangeNotifier ecn = new EntityChangeNotifier();
HttpContext.Current.Session["ecn"] = ecn;
Now you can reference the "session" property from within the Customer_OnChange method.

Making an object accessible by Service Layer without passing as Parameter in MVC4 App

I'm building a multi-tenant MVC app where there's a single app pool and single database. I have a Tenant table, and each of my models has a TenantId identified.
Each Tenant has a string "Url" that identifies the full URL used to access that tenant's data.
I can access this from my BaseController with the following (rough approximation):
HttpRequest request = HttpContext.Current.Request;
Uri requestUrl = request.Url;
_tenant = _tenantService.GetTenantByUrl(requestUrl);
Now, I'm at a point where I need to pass the Tenant into the service layer to perform business logic. One way I can do this is to go across every single method across all services (~200 methods) and add a Tenant parameter. I'd have to touch every call to the service layer, and every service layer method. This would work, but it's tedious and muddles the code.
For example, one of my methods before:
public void DeleteUserById(int userId)
{
using (var db = CreateContext())
{
var user = db.Users.FirstOrDefault(u => u.UserId.Equals(userId));
InternalDeleteUser(db, user);
}
}
After (if I pass in the Tenant):
public void DeleteUserById(Tenant tenant, int userId)
{
using (var db = CreateContext())
{
var user = tenant.Users.FirstOrDefault(u => u.UserId.Equals(userId));
InternalDeleteUser(db, user);
}
}
What I'm trying to achieve (by setting the tenant from my BaseController, one layer up):
public void DeleteUserById(int userId)
{
using (var db = CreateContext())
{
var user = _tenant.Users.FirstOrDefault(u => u.UserId.Equals(userId));
InternalDeleteUser(db, user);
}
}
Is there any way I can use my BaseService (all other services inherit from this) or some other pattern to define the Tenant from the Controller, and have the service methods pick it up, without passing it as a parameter to each one? This way I need only touch the base controller (or maybe even global.asax), and nothing else.
Put simply: How can I make an object accessible to all services by defining it from an MVC controller, without passing it directly to the service?
I guess what you´re saying about having a base service (see Layer Supertype) makes sense. That base class will have a dependency on an interface defined in the same service layer (e.g. IUserSession, IContext or whatever) and that interface will have a method or property that will return your Tenant.
The implementation of this interface will reside in your web application and it will do something as what you described, obtaining the data from the HttpContext.
If you have a background process, console application or whatever that does not run on a web context, you will have a different implementation that will create the Tenant based on any other criteria that you want.
So to summarize, you will have in your service layer:
abstract class BaseService
{
protected IContext Context {get; private set;}
public BaseService(IContext context)
{
Context = context;
}
}
public interface IContext
{
Tenant GetTenant();
}
Then in your web layer you´ll have:
public IWebContext : IContext
{
public Tenant GetTenant()
{
//your code to return create the tenant based on the url.
}
}
Hope this helps.
I have the same 'problem' since I'm building a multi tenant app as well. However, I solved it quite simple, IMO: every repository/service has defined a TenantId property, that must be set when that service is used. TenantId is a value object and it will throw if null.
Now, the point is any of the repos/services can be used outside the request, for example in a background thread or app. I am using a message driven approach so any required info (like tenant id) is part of the message and thus available for the consumer of the service (the message handler). Another benefit is testability.
I advice against coupling your service to a request specific object like HttpContext, Session or Cache.

HTTPContext across threads

I need to instantiate a singleton object per web request, so that the data is processed once and is valid throughout the request, I was using HttpContext.Current.Items to share data during HTTP request, everything was fine until we needed the singleton object instance across multiple threads, the first thing that I came up with was to pass the HttpContext instance to the new thread:
HttpContext context = HttpContext.Current;
ThreadPool.QueueUserWorkItem(callback =>
{
HttpContext.Current = context;
// blah blah
});
Which I don't think is a thread-safe approach as noted here.
Using Reflector I figured HttpContext.Current.Items actually uses CallContext to store objects in each logical thread. So I changed the singleton interface to this:
public static SingletonType SingletonInstance
{
get { return CallContext.GetData(key) as SingletonType; }
set { CallContext.SetData(key, value); }
}
And simply overwrite SingletonInstance when starting any new thread! The code works fine, however it seems that somehow under heavy load, CallContext.GetData(key) returns null and the application crashes with with a null reference exception!
I was thinking, if CallContext.GetData is atomic? But it just doesn't seem right, the CallContext is thread specific data storage and must be atomic or I am missing the point!
My other guess is that setting the SingletonInstance (CallContext.SetData) happens in one thread while CallContext.GetData executes in another as noted here but I don't know how/why?
update:
We are keeping an instance of each online user in an array on the server. The singleton object is actually a reference to the object representing current user. Current user must be unique and available in each thread for database querying, logging, error handling and more, this is how it is done:
public static ApplicationUser CurrentUser
{
get { return CallContext.GetData("ApplicationUser") as ApplicationUser ; }
set { CallContext.SetData("ApplicationUser", value); }
}
ASP.NET may migrate request between threads if it's under load. Once request is received page constructor may execute on one thread and page load on another. In this thread switch CallContext and ThreadStatic are not migrated, but luckaly HttpContext is.
This may be misleading as HttpContext is call context, but this is a little quirk in ASP.NET, probably due to cutting corners to improve performance.
You'll have to remove dependencies to CallContext and use HttpContext entire way through.
You can read more details in this terrific blog post by Piers7.
This was resolved during a chat session.
In essence it involves long-running tasks and a suggestion of using an external service (Web, or regular Windows Service) was decided as the best solution to the problem.
Thread-safing your second method is the best approach.
This is thread-safe version of your singletone:
public sealed class SingletonType
{
#region thread-safe singletone
private static object _lock = new object();
private SingletonType() { }
public static SingletonType SingletonInstance
{
get
{
if (CallContext.GetData(key) == null)
{
lock (_lock)
{
if (CallContext.GetData(key) == null)
CallContext.SetData(key, new SingletonType());
}
}
return CallContext.GetData(key) as SingletonType;
}
}
#endregion
//
//
// SingletoneType members
//
//
}
NOTE : using a lock { } block is the key.

Access the current InstanceContext in a WCF UsernamePasswordValidator

I have a WCF service that is using a custom UsernamePasswordValidator. The validator needs to access my entity framework context.
I would like to create one ObjectContext for the entire service call and then destroy/dispose it at the end of the call. So I created a singleton static class that provided this functionality, however, what's happening now is that if two service calls happen concurrently, one of the calls disposes the singleton.
I either keep a local reference to the ObjectContext, in which case the second service to use it sees it as disposed and throws and error, or, I put a wrapper property around the Singleton class wherever I need it and then all my changes get thrown away because I'm getting a new instance of the object if another call has disposed it.
So basically my question is how do I instantiate an ObjectContext per service call?
NOTE: The instance needs to be accesible in both the service code AND the custom UsernamePasswordValidator code.
I can't just do it in the constructor or use a using statement because then the custom UsernamePasswordValidator doesn't have access to it. Is there a way to have a static class per call? It does sound impossible, but what's the way around this? Should I be caching the object in a session?
My service is hosted in IIS.
UPDATE:
So I've nailed this down to storing state in the InstanceContext using an IExtension object. But How do I access the current InstanceContext in a UsernamePasswordValidator?
Ok, so in the end I solved it by using the following static class and relying on ASP.NET to cache the context for me.
I'm not sure if this is the best way to do things, but this allows me to use one ObjectContext per request so I'm not spinning up too many and this also means I don't have to use a lock on the object which would become a nightmare if many users were using the service.
public static class MyContextProvider
{
public static MyModel Context
{
get
{
if (HttpContext.Current.Items["context"].IsNull())
{
HttpContext.Current.Items["context"] = new MyModel();
}
return HttpContext.Current.Items["context"] as MyModel;
}
}
}
Then wherever I need an ObjectContext in the app I just call
var context = MyContextProvider.Context;
You have one instance per call, you also have 1 call per instance.
So it should be very simple, use a using () { } block in the toplevel of your OperationContract method.
Ok, here is the class with thread-safe static method that provides single ObjectContext entity model object for any WCF service call and automatically dispose it at the end of call:
public static class EntityModelProvider
{
private static readonly Dictionary<OperationContext, MyEntityModel> _entityModels = new Dictionary<OperationContext, MyEntityModel>();
public static MyEntityModel GetEntityModel()
{
if (OperationContext.Current == null)
throw new Exception("OperationContext is missing");
lock (_entityModels)
{
if (!_entityModels.ContainsKey(OperationContext.Current))
{
_entityModels[OperationContext.Current] = new MyEntityModel();
OperationContext.Current.OperationCompleted += delegate
{
lock (_entityModels)
{
_entityModels[OperationContext.Current].Dispose();
_entityModels.Remove(OperationContext.Current);
}
};
}
return _entityModels[OperationContext.Current];
}
}
For your service, you can specify a service behaviour which details the instance mode of the service:
[ServiceBehaviour(InstanceContextMode = InstanceContextMode.PerCall)]
public class MyService : IMyService {
ObjectContext context;
}
A cleaner way may be to use the ServiceAuthenticationManager, which is in .NET 4.
http://msdn.microsoft.com/en-us/library/system.servicemodel.serviceauthenticationmanager.aspx
From the Authenticate method (which you'll override) you can access the Message object and set properties on it. I've not used it in anger, so YMMV :)
EDIT the problem with this approach is that you don't have the Username and Password, so will still need the custom Authentication.
Take a look at the UsernameSecurityTokenAuthenticator...
http://msdn.microsoft.com/en-us/library/system.identitymodel.selectors.usernamesecuritytokenauthenticator(v=vs.90).aspx
Further reading from my research:
Answers to this question gives some hints about how to use it:
Custom WCF authentication with System.ServiceModel.ServiceAuthenticationManager?
If you can read (or ignore) the Russian, I found useful hints at:
http://www.sql.ru/forum/actualthread.aspx?tid=799046
This rather good CodeProject article goes further (encryption and compression as well as custom authorization)
http://www.codeproject.com/Articles/165844/WCF-Client-Server-Application-with-Custom-Authenti
Why not pass in the context into your CustomValidator when you assign to the service - store your object context in your validator, and in the overridden validation method new it up if need be. Then you still have access to the object through the Services CutomUserNameValidator ..
Depending on what you are asking :
Create your separate ObjectContext class as a dynamic object - add that as a property to you CustomValidator.
In your custom Validator - you can now check if the object is disposed and create the object again if need be.
Otherwise if this is not what you are after - just store the Context in the validator - you still have access on server side.
The code here is just generalized idea - I am just posting it as a frame of reference so you can have an idea of what I talking about.
public DynamicObjectContextObjectClass
{
ObjectContext internalObjectContext;
}
public class ServiceUserNamePasswordValidator : UserNamePasswordValidator
{
public DynamicObjectContextObjectClass dynamiccontext;
public override void Validate(string userName, string password)
{
if(dynamiccontext.internalObjectContext.isdisposed)
{
dynamiccontext.internalObjectContext = new Context;
}
try
{
if (string.IsNullOrEmpty(userName) || password == null)
{
//throw new ArgumentNullException();
throw new FaultException("Username cannot be null or empty; Password cannot be null and should not be empty");
}
}
}
}

Categories