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.
Related
I'm in a situation where the classic functionality of vnext's DI container is not enough to provide me with the correct functionality. Let's say I have a DataService that gets data from a database like this:
public class DataService : IDataService, IDisposable {
public List<MyObject> GetMyObjects()
{
// do something to fetch the data...
return myObjects;
}
}
I can then register this service in the DI container during the configuration phase in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped(typeof(IDataService), typeof(DataService));
}
This ensures the correct lifecylce of the service (one per request scope), however, I need the service to access a different database when a different request is made. For simplicity reasons, let's say the following scenario applies:
when a request to my Web API is made, the DataService will access the currently logged in user, which contains a claim called Database which contains the information which database to use.
the DataService is then instantiated with the correct database connection.
In order to get the second step to work, I have created a constructor for the DataService like this:
public DataService(IHttpContextAccessor accessor)
{
// get the information from HttpContext
var currentUser = accessor.HttpContext.User;
var databaseClaim = currentUser.Claims.SingleOrDefault(c => c.Type.Equals("Database"));
if (databaseClaim != null)
{
var databaseId = databaseClaim.Value;
// and use this information to create the correct database connection
this.database = new Database(databaseId);
}
}
By using the currently logged in user and his claims, I can ensure that my own authentication middleware takes care of providing the necessary information to prevent attackers from trying to access the wrong database.
Of course adding the IDisposable implementation is required to cleanup any database connections (and gets called correctly using the scope lifecycle).
I can then inject the DataService into a controller like this
public MyController : Controller
{
private IDataService dataService;
public MyController(IDataService dataService)
{
this.dataService = dataService;
}
}
This all works fine so far.
My questions now are:
Is there another way to create the instance other than using the constructor of the DataService? Maybe accessing the object the IServiceCollection provides in a different place other than during the configration phase which runs only once? Maybe using my own OWIN middleware?
Is this method really safe? Could two requests made at the same time accidentally end up with the DataServiceintended for the other request and therefore end up giving out the wrong data?
What you have is fine.
Is there another way to create the instance other than using the constructor of the DataService? Maybe accessing the object the IServiceCollection provides in a different place other than during the configration phase which runs only once? Maybe using my own OWIN middleware?
Not really. You can use delegate registration but it's the same problem.
Is this method really safe?
Yes
Could two requests made at the same time accidentally end up with the DataServiceintended for the other request and therefore end up giving out the wrong data?
Nope. The IHttpContextAcessor uses AsyncLocal (http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html) to provide access to the "current" http context.
I'm trying to figure out the best way to manage the DbContext. I've seen code samples that don't dispose and I've seen people say that that is a bad idea. Is it appropriate for me to do something like below? Also, should I put every transaction, including reads, in a new DbContext? This might be another question, but is the part about the EntityState necessary?
public abstract class GenericRepository<T> where T : EntityData
{
protected MyDbContext Context
{
get { return new MyDbContext(); }
}
public T Save(T obj)
{
T item;
using (var context = Context)
{
var set = context.Set<T>();
if (String.IsNullOrEmpty(obj.Id))
item = set.Add(obj);
else
{
item = set.Find(obj.Id);
item = obj;
}
// taken from another code sample
var entry = context.Entry(item);
if (entry.State == EntityState.Detached)
{
//Need to set modified so any detached entities are updated
// otherwise they won't be sent across to the db.
// Since it would've been outside the context, change tracking
//wouldn't have occurred anyways so we have no idea about its state - save it!
set.Attach(item);
context.Entry(item).State = EntityState.Modified;
}
context.SaveChanges();
}
return item;
}
}
EDIT
I also have an extended class that implements this function below. The context is not being wrapped in a using statement in this query, so I'm a little suspicious of my code.
public IQueryable<T> FindByAccountId(string accountId)
{
return from item in Context.Set<T>()
let user = UserRepository.FindByAccountId(accountId).FirstOrDefault()
where item.UserId == user.Id
select item;
}
Contexts should really be on a per request basis. The request comes in and a new context is created. This context is used for the remainder of the request then disposed of at the end of the request accordingly. This gives you the benefit of request long transactions, and as highlighted by HamidP, you also have the added benefit of cached entities; meaning that any entities loaded into the context can be loaded by retrieved without Entity Framework needing to query the database.
If you're using any kind of inversion of control container such as StructureMap then you can easily create HTTP request bound contexts by a configuration such as:
this.For<DbContext>().HybridHttpOrThreadLocalScoped().Use<DbContext>();
You're then able to inject your DbContext (or a derivative of it) into your repository and leave your IOC container of choice to dispose of the context at the end of the request. If you were to inject the same context into another repository then you'd receive the same instance of the context.
I hope this helps!
No, it should not
Best approach here is to assign a context just for a request. you should attach a context to an incoming request and dispose your context when request is finished. In this approach you save the overhead of creating a context for every transaction and also benefit from caching mechanism of context because each context has it's inside cache and a request may access the data it had access recently.
Creating a context for each transaction is not as bad as having a long life context!! Don't ever do that, long life contexts result in many concurrency issue and the cache becomes stale and memory consumption grows high and higher and you should maintain your application in future by miracles.
To keep this question simple, I'll describe the higher level problem and then go into any implementation details if needed.
I use the ASP.NET Identity in my application under development. In a specific scenario on a series of requests, the UserManager first get the current user(at least one FindById request), where the user is fetched. On a subsequent request, I update information on this user that is saved by UserManager.Update and I can see the change persisted in the database.
The problem is here that on further subsequent requests, the user object gotten from FindById is not updated. That is strange, but could be something about caching in UserManager I do not understand. However, when I trace the database calls, I see that UserManager indeed is sending the sql-requests to the database for getting the user.
And this is where it gets really strange - even though the database is confirmed to be up to date, UserManager still somehow returns an old object from this process. When I myself run exactly the same query traced directly to the database, I get updated data as expected.
What is this black magic?
Obviously, something is cached somewhere, but why does it make a query to the database, just to disregard the updated data it gets?
Example
This below example updates everything as expected in the db for each request to the controller action, and when GetUserDummyTestClass is calling findById on the other instance of UserManager I can trace the sql requests, and can test these directly to the db and verify that they return updated data. However, the user object returned from that very same line of code still has the old values (in this scenario, the first edit after the application was started, regardless of how many time the Test action is invoked).
Controller
public ActionResult Test()
{
var userId = User.Identity.GetUserId();
var user = UserManager.FindById(userId);
user.RealName = "name - " + DateTime.Now.ToString("mm:ss:fff");
UserManager.Update(user);
return View((object)userId);
}
Test.cshtml
#model string
#{
var user = GetUserDummyTestClass.GetUser(Model);
}
#user.RealName;
GetUserDummyTestClass
public class GetUserDummyTestClass
{
private static UserManager<ApplicationUser> _userManager;
private static UserManager<ApplicationUser> UserManager
{
get { return _userManager ?? (_userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()))); }
}
public static ApplicationUser GetUser(string id)
{
var user = UserManager.FindById(id);
return user;
}
}
Update
As Erik pointed out, I should not use static UserManagers. However, if I keep the UserManager in GetUserDummyTest bound to the HttpContext (persisting it per HttpRequest) in case I want to use it several times during a request, it is still caching the first User object it gets by a Id, and disregarding any updates from another UserManager. Thus suggesting that the real issue is indeed that I'm using two different UserManagers as trailmax suggested, and that it's not designed for this kind of usage.
In my example above, if I keep the UserManager in GetUserDummyTestClass persistent over the HttpRequest, add a Update-method and only use this in the controller, everything works fine as expected.
So if going to a conclusion, would it be correct to state that if I want to use logic from a UserManager outside of the scope of the controller, I have to globalize the UserManager instance in an appropriate class where I can bind the instance to the HttpContext, if I want to avoid creating and disposing instances for one-time usage?
Update 2
Investigating a little further, I realized that I am indeed intended to use one instance per request, and that this already actually is set up for the OwinContext in Startup.Auth and later accessed like this:
using Microsoft.AspNet.Identity.Owin;
// Controller
HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>()
// Other scopes
HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>()
This is actually embarrassingly obvious looking at the setup of the default AccountController provided, but I guess the rather strange and unexpected behavior described above proved quite distracting. Still, it would be interesting to understand the reason for this behavior, even though it will not be a problem anymore using OwinContext.GetUserManager.
Your problem is that you're using two different UserManager instances, and it looks like they're both statically defined (which is a huge no-no in Web applications, since these are shared between all threads and users of the system and are not thread safe, you can't even make them thread safe by locking around them because they contain state specific to a user)
Change your GetUserDummyTestClass to this:
private static UserManager<ApplicationUser> UserManager
{
get { return new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(new ApplicationDbContext())); }
}
public static ApplicationUser GetUser(string id)
{
using (var userManager = UserManager)
{
return UserManager.FindById(id);
}
}
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.
I have a method in my generic repository:
public IQueryable<T> Query<T>() where T : class, IEntity
{
return _context.Set<T>();
}
This is method for getting user:
public User GetUser(string email)
{
return _repository.Query<User>().FirstOrDefault(u => u.Email == email);
}
Finally, I put the user to session:
AppSession.CurrentUser = UserService.GetUser(email);
In my action I need to get the current user and get collection of objects Notifications (one-to-many):
AppSession.CurrentUser.Notifications.OfType<EmailNotification>().FirstOrDefault();
But, here I get the error:
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
I know that Notifications not loaded when I getting User from DB.
How to say EF to load Notifications objects? I know about Include, but I cannot use it in GetUser method.
When the first HttpRequest ends after looking up your CurrentUser object, your _repository reference that the CurrentUser is expecting for additional lookups like EmailNotifications isn't available.
The exception is thrown because CurrentUser doesn't have the original object context, so you either have to attach the CurrentUser object to the new objectContext that your _repository is using, or use the easier solution of simply reloading the user through the new context that was created for your current request in the repository.
Before attempting to find the notifications in your action, add the following line:
AppSession.CurrentUser = UserService.GetUser(AppSession.CurrentUser.Email);
AppSession.CurrentUser.Notifications.OfType<EmailNotification>().FirstOrDefault();
As #Ryan said it is due to the fact that the object context is not available to lazy load in the associated notifications.
What I'd suggest is turn off lazy loading (if possible) as can cause lots of issues later and then do something like ...
var user = UserService.GetUser(AppSession.CurrentUser.Email);
user.Notifications = NotificationService.GetUserNotifications(user.Id /* or another identifier */);
AppSession.CurrentUser = user;
To do this you will require a new NotificationService, this can load (as suggested above) but also handle the execution of notifications (sending emails etc).
You should now have the notifications for that user in your Application session cache.
HTH