1

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:

enter image description here

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 ??

Gaurav
  • 8,367
  • 14
  • 55
  • 90
  • FYI, I am registering the query dependency at Application_Start so the solution proposed by the answer in http://stackoverflow.com/questions/8834677/determine-the-url-hostname-without-using-httpcontext-current this post will not work for me. – Gaurav Apr 17 '14 at 14:04
  • If each customer is associated to exactly one tenant, can't you get from the `CustomerId` to the `TenantId` through one of your tables? – zimdanen Apr 17 '14 at 14:04
  • Right.. What about the other relationship? Can a customer be part of multiple tenants? Or is there a direct link from a customer to a single tenant? – zimdanen Apr 17 '14 at 15:46
  • Exactly. So one Customer has one Tenant. So.. where is that relationship held? In a table? – zimdanen Apr 17 '14 at 15:52
  • And you have the CustomerID at the time you are trying to register or edit the Customer? – zimdanen Apr 17 '14 at 15:55
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/50896/discussion-between-zimdanen-and-gaurav) – zimdanen Apr 17 '14 at 15:55

2 Answers2

0

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; }
        }
    }
}
Kelso Sharp
  • 972
  • 8
  • 12
  • Your code snippet assumes that HttpContext.Current is not null. This is, by principle, *never* true in a notification callback. – usr May 29 '14 at 17:47
  • Well, I think you missed something there, I am talking about a single hub thread, the only way that httpcontext.current would be null, is if you are accessing it from a different thread than the request, and if that is the case you have done something wrong with your tracking hub. – Kelso Sharp May 29 '14 at 17:58
  • Maybe you're right. I have trouble understanding how your answer solves the problem the OP has, though. (I cannot take back the -1 without you editing the post. This is an SO enforced rule. Please make a dummy edit.) – usr May 29 '14 at 18:03
  • His issue is that he needs to get the current context and if he has this class he can "create" new instance, that will map back to the current context if it exists, and be able to use it in the event. – Kelso Sharp May 29 '14 at 18:14
  • One last thing, I found out later... If you are running in visual studio 2013, on windows 8 or 8.1 the HttpContext.Current is always null. I am not sure if its a bug in win 8, or if its been deprecated and is no longer used. – Kelso Sharp Oct 14 '14 at 14:24
-1

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.

René Vogt
  • 43,056
  • 14
  • 77
  • 99