Given an IHttpAsyncHandler instance A, will A be the instance that receives the EndProcessRequest callback? If so, it is guaranteed? Does the IsReusable property alter the behavior at all?
It's complicated enough to try to test that I wanted to reach out and see if someone else had already been down this road.
I currently have a IHttpAsyncHandler that uses a state object that gets sent to EndProcessRequest and everything is working perfectly. However, I could clean the code considerably if I could preserve state in an instance level variable like a property or field.
Thoughts?
Yes, the same instance will be called in the EndProcessRequest method.
If you are putting per request state on the handler though, the isReusable getter should return 'false', otherwise the ASP.NET run time will not re-instantiate your handler from request to request instead of recreating a whole new instance per request.
Related
I have an existing service that notifies a large number of clients when an event occurs. It uses a long polling mechanism that I rolled myself. I'm exploring replacing that mechanism with a signalr hub, and have a prototype working. But it has a pretty significant inefficiency that feels like there should be a solution to, but I'm not finding it.
I understand the idea of groups in signalr, and groups are obviously intended to prevent this inefficiency, but there is a reason that I cannot use groups. I hope it suffices to say that I need to call the same client method, with the same parameter values, on many clients using each client's ConnectionId. I can explain why if necessary, but it's really beside the point.
Assume I have a list of 200 ConnectionId's and I need to call the same method on each of them passing the same object parameter. If I simply iterate through the ConnectionId's calling Clients.Client(ConnectionId).clientMethod(param), I presume that the param object would be serialized 200 times.
Is there a way to serialize the parameter(s) one time, then invoke the client method using the already-serialized parameters?
UPDATE
I've found a github issue that sounds related (maybe even this exact issue) at Allow to Send Json Strings without duplicate Serialization. It appears that the functionality was added to signalr, but the github issue doesn't say anything about how to do it, and I can't find anything regarding it in the signalr docs.
UPDATE 2
In the github issue referenced above, the new functionality was implemented for PersistentConnection only -- not hubs. With persistent connections, when sending a parameter of type ArraySegment, signalr assumes it to be pre-serialized and sends it as-is without serializing it.
For some reason, this was not implemented for hubs, although it would be useful for hubs for the same reason it was useful for persistent connections.
Store all connectionId's in a Static List<string> atOnConnected` event and use the following,
Static List<string> allconnections = new List<string>();
public override Task OnConnected()
{
allconnections.Add(Context.ConnectionId);
return base.OnConnected();
}
Public void YourServerMethod(params)
{
Clients.Clients(allConnections).clientMethod(params)
}
I'm developing an app with VS2013, using EF6.02, and Web API 2. I'm using the ASP.NET SPA template, and creating a RESTful api against an entity framework data source backed by a sql server. (In development, this resides on the SQL Server local instance.)
I've got two API methods so far (one that just reads data, one that writes data), and I'm testing them by calling them in the javascript. When I only call a single method in my script, either one works perfectly. But if I call both in script (without waiting for either's callback to fire), I get bad results and different exceptions in the debugger. Some exceptions state that the save can't be completed because there are pending transactions. Another exception stated something about a conflict with other threads. And sometimes, the read operation fails with a null pointer exception when trying to read a result set.
"New transaction is not allowed because there are other threads running in the session."
This makes me question if I'm correctly getting a new DBContext per request. My code for this looks like:
static Startup()
{
context = new Data.SqlServer.AppDbContext();
...
}
and then whenever instantiating a unit of work, I access Startup.context.
I've tried to implement the unit of work pattern, and each request shares a single UOW object which has a single DBContext object.
My question: Do I have additional responsibility to ensure that web requests "play nicely" with eachother? I hope that this is a problem that others have already dealt with. Perhaps the errors that I'm seeing are legitimate in the sense that if one user's data is being touched, it is temporarily in an invalid state and if other requests come in at that exact moment, they indeed will fail (and I should code anticipating these failures). I guess that even if each request has its own DBContext, they still share the same underlying SQL data source so perhaps that's causing issues.
I can try to put together a testcase, but I get differing behavior depending on where I put breakpoints and how long I spend on them, reaffirming to me that this is timing related.
Thanks for any help or suggestions...
-Ben
Your problem is where you are setting your context. The Startup method is for when the entire application starts, thus any request made will all use the same context. This is not a per request setup, but rather a per application setup. As to why you are getting the errors, EntityFramework is NOT thread-safe. Since IIS spawns many threads to handle concurrent request, your single context is being used across multiple threads.
As for a solution, you can look into
-Dependency Injection frameworks (such as Ninject or Unity)
-place a using statement in your UnitOfWork classes
using(var context = new Data.SqlServer.AppDbContext()){//do stuff}
-Or, I have seen instances of people creating a class that gets the context for that request and stores it in the HttpContext.Cache[] element (using a unique name so you can retrieve it in another class easily), making it so that you will reuse the same context for the same request. Something like this:
public AppDbContext GetDbContext()
{
var httpContext = HttpContext.Current;
if (httpContext == null) return new AppDbContext();
const string contextTypeKey = "AppDbContext";
if (httpContext.Items[contextTypeKey] == null)
{
httpContext.Items.Add(contextTypeKey, new AppDbContext());
}
return httpContext.Items[contextTypeKey] as AppDbContext;
}
To use the above method, make a simple call var context = GetDbContext();
Note
We have all of the above methods, but this is specifically to the third method. It seems to work well with two caveats. First, do not use this in a using statement as it will not be available to any other classes during the scope of the request (you dispose it). And secondly, ensure that you have a call on Application_EndRequest that does actually dispose of it. We saw these little buggers hanging around after the request ended in memory causing a huge spike in memory usage.
I have an ASP.NET site and I've been doing some work refactoring code to try to remove some long running processes (in the order of an hour) from the actual http Request by creating a BackgroundWorker and sending the work off to that to process. This was running fine on cutdown tests but when I applied the logic to the real code I found problems accessing Session variables from the code running in the Background Worker. It seems that the HttpContext object that was passed has a null session and if I ask for HttpContext.Current I get null back.
I'm assuming that this is because they are in a different thread and that the session and HttpContext.Current are both reliant on being in the same thread. Is there any way I can get access to the Session from the background worker or am I stuck with finding all the variables I need from session and putting them in an usable data structure and then putting them back in session (if appropriate) afterwards? It obviously complicates the refactor massively if I need to do this so I'd rather not.
Thanks for any thoughts you might have. I'm open to other suggestions on how I might do this other than BackgroundWorker processes (which were suggested to me in another question).
I'm not sure of all of your requirements, but you may be able to get away with using the Application Cache instead of the Session if you're not looking for the long process to be tied to an individual user's request.
If so, I would try swapping out your use of Session to:
HttpRuntime.Cache.Set("CacheKeyName");
HttpRuntime.Cache.Get("CacheKeyName");
Here's an MSDN link that sheds some light on this.
The text in particular is :
If an asynchronous action method calls a service that exposes methods by using the BeginMethod/EndMethod pattern, the callback method (that is, the method that is passed as the asynchronous callback parameter to the Begin method) might execute on a thread that is not under the control of ASP.NET. In that case, HttpContext.Current will be null, and the application might experience race conditions when it accesses members of the AsyncManager class such as Parameters. To make sure that you have access to the HttpContext.Current instance and to avoid the race condition, you can restore HttpContext.Current by calling Sync() from the callback method.
In my web application, I do something like this to read the session variables:
if (HttpContext.Current.Session != null && HttpContext.Current.Session["MyVariable"] != null)
{
string myVariable= (string)HttpContext.Current.Session["MyVariable"];
}
I understand why it's important to check why HttpContext.Current.Session["MyVariable"] is null (the variable might not have been stored in the Session yet or the Session has been reset for various reasons), but why do I need to check if HttpContext.Current.Session is null?
My understanding is that the session is created automatically by ASP.NET therefore HttpContext.Current.Session should never be null. Is this assumption correct? If it can be null, does it mean I should also check it before storing something in it:
if (HttpContext.Current.Session != null)
{
HttpContext.Current.Session["MyVariable"]="Test";
}
else
{
// What should be done in this case (if session is null)?
// Is it possible to force the session to be created if it doesn't exist?
}
Yes, the Session object might be null, but only in certain circumstances, which you will only rarely run into:
If you have disabled the SessionState http module, disabling sessions altogether
If your code runs before the HttpApplication.AcquireRequestState event.
Your code runs in an IHttpHandler, that does not specify either the IRequiresSessionState or IReadOnlySessionState interface.
If you only have code in pages, you won't run into this. Most of my ASP .NET code uses Session without checking for null repeatedly. It is, however, something to think about if you are developing an IHttpModule or otherwise is down in the grittier details of ASP .NET.
Edit
In answer to the comment: Whether or not session state is available depends on whether the AcquireRequestState event has run for the request. This is where the session state module does it's work by reading the session cookie and finding the appropiate set of session variables for you.
AcquireRequestState runs before control is handed to your Page. So if you are calling other functionality, including static classes, from your page, you should be fine.
If you have some classes doing initialization logic during startup, for example on the Application_Start event or by using a static constructor, Session state might not be available. It all boils down to whether there is a current request and AcquireRequestState has been run.
Also, should the client have disabled cookies, the Session object will still be available - but on the next request, the user will return with a new empty Session. This is because the client is given a Session statebag if he does not have one already. If the client does not transport the session cookie, we have no way of identifying the client as the same, so he will be handed a new session again and again.
The following statement is not entirely accurate:
"So if you are calling other functionality, including static classes, from your page, you should be fine"
I am calling a static method that references the session through HttpContext.Current.Session and it is null. However, I am calling the method via a webservice method through ajax using jQuery.
As I found out here you can fix the problem with a simple attribute on the method, or use the web service session object:
There’s a trick though, in order to access the session state within a web method, you must enable the session state management like so:
[WebMethod(EnableSession = true)]
By specifying the EnableSession value, you will now have a managed session to play with. If you don’t specify this value, you will get a null Session object, and more than likely run into null reference exceptions whilst trying to access the session object.
Thanks to Matthew Cosier for the solution.
Just thought I'd add my two cents.
Ed
If your Session instance is null and your in an 'ashx' file, just implement the 'IRequiresSessionState' interface.
This interface doesn't have any members so you just need to add the interface name after the class declaration (C#):
public class MyAshxClass : IHttpHandler, IRequiresSessionState
In my case ASP.NET State Service was stopped. Changing the Startup type to Automatic and starting the service manually for the first time solved the issue.
ASP.NET Technical Articles
SUMMARY: In ASP.NET, every Web page
derives from the System.Web.UI.Page
class. The Page class aggregates an
instance of the HttpSession object for
session data. The Page class exposes
different events and methods for
customization. In particular, the
OnInit method is used to set the
initialize state of the Page object.
If the request does not have the
Session cookie, a new Session cookie
will be issued to the requester.
EDIT:
Session: A Concept for Beginners
SUMMARY: Session is created when user
sends a first request to the server
for any page in the web application,
the application creates the Session
and sends the Session ID back to the
user with the response and is stored
in the client machine as a small
cookie. So ideally the "machine that
has disabled the cookies, session
information will not be stored".
When writing a http handler/module, there is an interface member to implement called - bool IsReusable.
What is the significance of this member? If I set it to false (or true), what does this mean for the rest of the web app?
The normal entry point for a handler is the ProcessRequest method. However you may have code in the class constructor which puts together some instance values which are expensive to build.
If you specify Reusable to be true the application can cache the instance and reuse it in another request by simply calling its ProcessRequest method again and again, without having to reconstruct it each time.
The application will instantiate as many of these handlers as are need to handle the current load.
The downside is that if the number of instances needed is larger than the instances currently present, they cause more memory to be used. Conversely they can also reduce apparent memory uses since their instance value will survive GC cycles and do not need to be frequently re-allocated.
Another caveat is you need to be sure that at the end of the ProcessRequest execution the object state is as you would want for another request to reuse the object.
Further to AnthonyWJones's answer, if your HTTP handler returns true for IsReusable then you should ensure that it is fully thread-safe.
There's nothing in the documentation to indicate that reusable handlers can't be reused concurrently, although the current Microsoft implementations only appear to reuse them consecutively. But, at least in theory, a single handler instance could be reused simultaneously by multiple requests, so you shouldn't rely on any data which might be modified by other concurrent threads.
If you don't store any state in that instance (i.e.: you don't have any fields (aka "class variables")) then you should be safe reusing it.
It's by default false to be on the safe side.