STA MVC application doesnt end long running requests - c#

I need to run my ASP.NET MVC application in STA mode. For this purpose I developed some custom classes based on Programming ASP.NET MVC 4: Developing Real-World Web Applications with ASP.NET MVC. Here they are:
public class StaThreadRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
if (requestContext == null)
throw new ArgumentNullException("requestContext");
return new StaThreadHttpAsyncHandler(requestContext);
}
}
public class StaThreadHttpAsyncHandler : Page, IHttpAsyncHandler, IRequiresSessionState
{
private readonly RequestContext _requestContext;
public StaThreadHttpAsyncHandler(RequestContext requestContext)
{
if (requestContext == null)
throw new ArgumentNullException("requestContext");
_requestContext = requestContext;
}
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
return this.AspCompatBeginProcessRequest(context, cb, extraData);
}
protected override void OnInit(EventArgs e)
{
var controllerName = _requestContext.RouteData.GetRequiredString("controller");
var controllerFactory = ControllerBuilder.Current.GetControllerFactory();
var controller = controllerFactory.CreateController(_requestContext, controllerName);
if (controller == null)
throw new InvalidOperationException("Could not find controller: " + controllerName);
try
{
controller.Execute(_requestContext);
}
finally
{
controllerFactory.ReleaseController(controller);
}
this.Context.ApplicationInstance.CompleteRequest();
}
public void EndProcessRequest(IAsyncResult result)
{
this.AspCompatEndProcessRequest(result);
}
public override void ProcessRequest(HttpContext httpContext)
{
throw new NotSupportedException("STAThreadRouteHandler does not support ProcessRequest called (only BeginProcessRequest)");
}
}
Also adjusted RouteConfig:
routes.Add(new Route("{controller}/{action}/{id}", new StaThreadRouteHandler ()))
Everything works fine for majority of my actions, but I have two that take somewhere between 10 and 20 seconds to finish. For these two actions, method EndProcessRequest is never executed, even though no exception is thrown during their execution and this.Context.ApplicationInstance.CompleteRequest(); is called within OnInit without problems. As a result, such request ends in IIS with state ExecuteRequestHandler and is stuck there forever and also blocks new requests from being served.
Why is this happening? Why breakpoint in EndProcessRequest is never hit for long running actions, while it works fine for shorter actions (1-5 seconds).

Problem was not related to execution time of particular actions at all. Not really sure what was happening under hood but all the problematic actions had one in common - they were creating one particular COM object, which I guess was somehow not properly destroyed and as a result EndProcessRequest was not called.
The issue was happening for any .NET COM object, native COM objects are fine and EndProcessRequest is called.

Related

Benchmarking ASP.net Web API Actions

I am working on a ASP.net Web API 2 project and would like to benchmark the time of each controller action. My idea was to use ActionFilterAttribute and add a http header on the response containing the time taken.
Controller:
[Timing]
public class MyController : ApiController
{
[Route("get")]
public IHttpActionResult Get() {
System.Threading.Thread.Sleep(1000);
return Ok();
}
}
ActionFilterAttribute:
public class TimingAttribute : ActionFilterAttribute
{
private System.Diagnostics.Stopwatch timer;
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
timer = System.Diagnostics.Stopwatch.StartNew();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
timer.Stop();
if (actionExecutedContext.Response != null && actionExecutedContext.Response.Content !=null)
{
actionExecutedContext.Response.Content.Headers.TryAddWithoutValidation("Execution-time", timer.ElapsedMilliseconds.ToString());
}
}
}
When I run this, I get execution times of less than 1 second even though I have a 1s delay in the controller. Does anyone know why this is the case or if there is a better way to benchmark Web API 2?
There are lots of things that are executed along the pipeline before OnActionExecuting is hit. Also, there are other things that are executed after OnActionExecuted, the most obvious being the HttpActionResult. Therefore It would be better to use HttpApplication.BeginRequest event to start mesasuring time, and HttpApplication.EndRequest to end measuring time.
However, I think there are better ways to benchmark a web application than log elapsed times at the server side. Fiddler has lots of nice features related to benchmarking:
You can capture the http traffic of interest by using filters.
The statistics view, where you can see lots of statistics of the selected sessions.
The time line view which shows you a nice chart about time taken by selected sessions.
You can save the sessions into a sessions archive, this is a zip file containing all information about the sessions, including xml files with timing information, that you can analize later to build you own reports.
I ran into the same problem, and found that the solution on this blog worked:
public class StopwatchAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
{
base.OnActionExecuting(actionContext);
actionContext.Request.Properties[actionContext.ActionDescriptor.ActionName] = Stopwatch.StartNew();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
Stopwatch stopwatch = (Stopwatch)actionExecutedContext.Request.Properties[actionExecutedContext.ActionContext.ActionDescriptor.ActionName];
if (actionExecutedContext.Response != null && actionExecutedContext.Response.Content != null)
{
actionExecutedContext.Response.Content.Headers.TryAddWithoutValidation("Execution-Time", stopwatch.ElapsedMilliseconds.ToString());
}
}
}

OnActionExecuting Not Getting Executed

I am troubleshooting a ASP.NET MVC application and on one server the OnActionExecuting is not firing. It has been a long time since I looked at filters. What can keep the OnActionExecuting from running? The effect in our application is the user context never really gets set up (Initialize)... so everything redirects the user back to the login page.
Here is the code of the filter. Note "Jupiter" was the codename of the project
public class JupiterAuthenticationFilter : IActionFilter
{
private readonly IJupiterContext _jupiterContext;
public JupiterAuthenticationFilter(IJupiterContext jupiterContext)
{
if (jupiterContext == null)
{
throw new ArgumentNullException("jupiterContext");
}
_jupiterContext = jupiterContext;
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
_jupiterContext.Initialize();
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
}
}
It can happen when your Controller has System.Web.MVC implementation, but ActionFilter has System.Web.Http.

ASP.NET MVC 4: Only allow one request at a time

In my ASP.NET MVC Application, I want to handle all requests sequentially; no action/controller code should be executed concurrently with another. If two requests come in at similar times, it should run the first one first, then the second one when the first one is done.
Is there a better way of doing this besides using a global lock variable?
EDIT: The application is more of a batch/service over the web that performs web service calls and cleans up a database. Different URLS in the site lead to different batch operations. This is not a site for end-users. Thus, I need to make it so that only one request to a URL (which will do some batch operations) will be done at a time, otherwise the batch operation could be corrupted if code for it runs concurrently with itself, or other batch operations. In fact, if another request comes when one is currently executing, it should not be run at all, even after the previous one finishes; it should just give an error message.
I would like to know if there was a way to do this in IIS instead of code. If I have a global lock variable, it would make the code more complicated, and I might run in a deadlock where the lock variable is set to true but never can be set to false.
EDIT: Sample code of implementation plan
[HttpPost]
public ActionResult Batch1()
{
//Config.Lock is a global static boolean variable
if(Config.Lock) { Response.Write("Error: another batch process is running"); return View(); }
Config.Lock = true;
//Run some batch calls and web services (this code cannot be interleaved with ExecuteBatchCode2() or itself)
ExecuteBatchCode();
Config.Lock = false;
return View();
}
[HttpPost]
public ActionResult Batch2()
{
if(Config.Lock) { Response.Write("Error: another batch process is running"); return View(); }
Config.Lock = true;
//Run some batch calls and web services (this code cannot be interleaved with ExecuteBatchCode1() or itself)
ExecuteBatchCode2();
Config.Lock = false;
return View();
}
Would I need to be worried about a case where the code does not reach Config.Lock = false, resulting in Config.Lock = true forever, causing no more requests to be served?
You have accept request as much as you can, people don't like waiting in front of browser.
But after, on serve side, yuo can push them into (say) Queue<T> and process them in sequence.
In short:
accept in async way
process, on the server, in sequence
You could write an attribute:
public class ExclusiveActionAttribute : ActionFilterAttribute
{
private static int isExecuting = 0;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Interlocked.CompareExchange(ref isExecuting, 1, 0) == 0)
{
base.OnActionExecuting(filterContext);
return;
}
filterContext.Result =
new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
Interlocked.Exchange(ref isExecuting, 0);
}
}
then use it on the controllers/methods that you want to control:
[ExclusiveAction] //either here, for every action in the controller
public class MyController : Controller
{
[ExclusiveAction] //or here for specific methods
public ActionResult DoTheThing()
{
//foo
return SomeActionResult();
}
}
the above code does not work probably because when request 1 is running and send request 2, app return service unavailable, it's good but if request 1 doesn't completed and again send request 2 to app, app running both request at same time. I'm reviewed code and change it.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class ExclusiveActionAttribute : ActionFilterAttribute
{
private static int _isExecuting = 0;
private static int _isDuplicated = 0;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Interlocked.CompareExchange(ref _isExecuting, 1, 0) == 0)
{
base.OnActionExecuting(filterContext);
return;
}
Interlocked.Exchange(ref _isDuplicated, 1);
filterContext.Result = new StatusCodeResult((int)HttpStatusCode.ServiceUnavailable);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
base.OnResultExecuted(filterContext);
if (_isDuplicated == 1)
{
Interlocked.Exchange(ref _isDuplicated, 0);
return;
}
Interlocked.Exchange(ref _isExecuting, 0);
}

Returning a value in MVC Web API, and then run log code afterwards

Below is a very simple HelloWorld API Method
[HttpGet]
[Route("api/helloworld")]
public string SayHello()
{
try
{
return "Hello World - API Version 1 - " + DateTime.Now.ToLongTimeString();
}
finally
{
Log("I'd like logging to not hold up the string from getting returned");
}
}
Unfortunately, the finally code doesn't work this way, so the log method in this case would block the string from getting returned until after the log was complete.
Is it possible to return a value in the MVC Web API, and then run code afterwards? In my particular case I'd like to log afterwards, but there is no reason for the database logging to take up time for the client to receive a response, as it will not effect the response.
Yes, but you'd need to run it on a separate thread.
While WebAPI does not have a OnRequestExecuted method on filters, which is what you are probably looking for it, I think filters are still the correct approach.
What you will need is a filter combined with a derivative ObjectContent class that defers your post-request logic to after the response is written. I use this approach to automatically create NHibernate session and transaction at the beginning of a request, and commit them when the request is complete, which is similar to what you described in a comment. Please keep in mind this is greatly simplified to illustrate my answer.
public class TransactionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
// create your connection and transaction. in this example, I have the dependency resolver create an NHibernate ISession, which manages my connection and transaction. you don't have to use the dependency scope (you could, for example, stuff a connection in the request properties and retrieve it in the controller), but it's the best way to coordinate the same instance of a required service for the duration of a request
var session = actionContext.Request.GetDependencyScope().GetService(typeof (ISession));
// make sure to create a transaction unless there is already one active.
if (!session.Transaction.IsActive) session.BeginTransaction();
// now i have a valid session and transaction that will be injected into the controller and usable in the action.
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
var session = actionExecutedContext.Request.GetDependecyScope().GetService(typeof(ISession));
var response = actionExecutedContext.Response;
if (actionExecutedContext.Exception == null)
{
var content = response.Content as ObjectContent;
if (content != null)
{
// here's the real trick; if there is content that needs to be sent to the client, we need to swap the content with an object that will clean up the connection and transaction AFTER the response is written.
response.Content = new TransactionObjectContent(content.ObjectType, content.Value, content.Formatter, session, content.Headers);
}
else
{
// there is no content to send to the client, so commit and clean up immediately (in this example, the container cleans up session, so it is omitted below)
if (session.Transaction.IsActive) session.Transaction.Commit();
}
}
else
{
// an exception was encountered, so immediately rollback the transaction, let the content return unmolested, and clean up your session (in this example, the container cleans up the session for me, so I omitted it)
if (session.Transaction.IsActive) session.Transaction.Rollback();
}
}
}
And the magic happens in this ObjectContent derivative. Its responsibility is to stream the object to the response, supporting async actions, and do something after the response is sent down. You can add your logging, db, whatever in here. In this example, it merely commits a transaction after successfully writing the response.
public class TransactionObjectContent : ObjectContent
{
private ISession _session;
public TransactionObjectContent(Type type, object value, MediaTypeFormatter formatter, ISession session, HttpContentHeaders headers)
: base(type, value, formatter)
{
_session = session;
foreach (var header in headers)
{
response.Content.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
}
protected async override Task SerializeToStreamAsync(Stream stream, TransportContext context)
{
await base.SerializeToStreamAsync(stream, context); // let it write the response to the client
// here's the meat and potatoes. you'd add anything here that you need done after the response is written.
if (_session.Transaction.IsActive) _session.Transaction.Commit();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
if (_session != null)
{
// if transaction is still active by now, we need to roll it back because it means an error occurred while serializing to stream above.
if (_session.Transaction.IsActive) _session.Transaction.Rollback();
_session = null;
}
}
}
}
Now you can either register the filter in your global filters, or add it directly to actions or controllers. You don't have to keep copying and pasting redundant code for executing your logic in each action in another thread; the logic just gets applied automagically to every action you target with the filter. Much cleaner, much easier, and DRY.
Example controller:
[Transaction] // will apply the TransactionFilter to each action in this controller
public DoAllTheThingsController : ApiController
{
private ISession _session;
public DoAllTheThingsController(ISession session)
{
_session = session; // we're assuming here you've set up an IoC to inject the Isession from the dependency scope, which will be the same as the one we saw in the filter
}
[HttpPost]
public TheThing Post(TheThingModel model)
{
var thing = new TheThing();
// omitted: map model to the thing.
// the filter will have created a session and ensured a transaction, so this all nice and safe, no need to add logic to fart around with the session or transaction. if an error occurs while saving, the filter will roll it back.
_session.Save(thing);
return thing;
}
}

EF - The context cannot be used while the model is being created exception during HTTP requests

I am receiving "The context cannot be used while the model is being created." issue in my web application in one of my webpages. This particular webpage POSTs to the server every 2-3 seconds to refresh the screen. From my testing I found that If I have 2 or more browser instances open to this page, after several minutes I receive a "The context cannot be used while the model is being created" exception from deep in the repository.
This code calls a "service" to retrieve the needed data. This code is executed in an custom authorization attribute of the MVC Controller class.
// Code in custom "Authorization" attribute on the controller
int? stationId = stationCookieValue; // Read value from cookie
RoomStationModel roomStationModel = RoomStationService.GetRoomStation(stationId); // Error occurs inside this call
Here is the "RoomStationModel"
public class RoomStationModel
{
[Key]
public int RoomStationId { get; set; }
public int? RoomId { get; set; }
[ForeignKey("RoomId")]
public virtual RoomModel Room { get; set; }
/* Some other data properties.... */
}
public class RoomModel
{
[Key]
public int RoomId { get; set; }
public virtual ICollection<RoomStationModel> Stations { get; set; }
}
Here is the code for the service call above:
public RoomStationModel GetRoomStation(int? roomStationId)
{
RoomStationModel roomStationModel = null;
if (roomStationId.HasValue)
{
using (IRepository<RoomStationModel> roomStationRepo = new Repository<RoomStationModel>(Context))
{
roomStationModel = roomStationRepo.FirstOrDefault(rs => rs.RoomStationId == roomStationId.Value, false, new string[] { "Room" });
}
}
return roomStationModel;
}
Here's the repository....where the error occurs
public class Repository<TObject> : IRepository<TObject> where TObject : class
{
protected MyContext Context = null;
public Repository(IDataContext context)
{
Context = context as MyContext;
}
protected DbSet<TObject> DbSet { get { return Context.Set<TObject>(); } }
public virtual TObject FirstOrDefault(Expression<Func<TObject, bool>> predicate, bool track = true, string[] children = null)
{
var objectSet = DbSet.AsQueryable();
if (children != null)
foreach (string child in children)
objectSet = objectSet.Include(child);
if (track)
return objectSet.Where(predicate).FirstOrDefault<TObject>(predicate);
return objectSet.Where(predicate).AsNoTracking().FirstOrDefault<TObject>(predicate);
}
}
Screenshot of error:
Stacktrace:
at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
at System.Data.Entity.Internal.InternalContext.Initialize()
at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
at System.Data.Entity.Internal.Linq.InternalSet`1.Initialize()
at System.Data.Entity.Internal.Linq.InternalSet`1.Include(String path)
at System.Data.Entity.Infrastructure.DbQuery`1.Include(String path)
at System.Data.Entity.DbExtensions.Include[T](IQueryable`1 source, String path)
at Vanguard.AssetManager.Data.Repository`1.FirstOrDefault(Expression`1 predicate, Boolean track, String[] children) in C:\Work\VanguardAssetManager\Main\Vanguard.AssetManager.Data\Repository.cs:line 100
at Vanguard.AssetManager.Services.Business.RoomStationService.GetRoomStation(Nullable`1 roomStationId) in C:\Work\VanguardAssetManager\Main\Vanguard.AssetManager.Services\Business\RoomStationService.cs:line 61
at Vanguard.AssetManager.Web.Attributes.RoomStationAuthorizeAttribute.OnAuthorization(AuthorizationContext filterContext) in C:\Work\VanguardAssetManager\Main\Vanguard.AssetManager.Web\Attributes\RoomStationAuthorizeAttribute.cs:line 52
at System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters(ControllerContext controllerContext, IList`1 filters, ActionDescriptor actionDescriptor)
at System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName)
EF Version: 4.1 (Code first)
Your repository is short-lived (you create it for each call to GetRoomStation() but your actual context appears to be long-lived (RoomServiceStation.Context property). This means that every call to your web application is going to use the same context.
This is the "EF in an N-tier" scenario where you're trying to keep something stateful around (the context) in the architecturally stateless model of a web application. All those requests are being channeled to the same context on different threads and you're getting a race condition.
One thread could be kicking off first-time initialization of your context in response to a request, and another comes in attempting to use the context. The second request thinks the context is ready for use and you get this exception. You may even get this if you have multiple contexts trying to "spin up" at the same time as suggested in another SO thread.
You can do a few things. You could try pessimistic locking around access to your context, but you're putting in an unnecessary bottleneck. You could try creating some sort of "before clients are calling me, initialize the context" code, but you have to find a good place to do this, perhaps using the "brute force" method suggested in an MSDN thread.
A better thing to do is to simply create a new context for every request to your back-end service. There is some overhead, yes, but minimal. The overhead is probably less likely to kill performance than pessimistic locking, and won't be subject to app pool recycle events scaling out your web app on a farm and so on.
If you're relying on change tracking or other stateful nature of a context, you will lose this benefit. In this case, you're going to have to come up with a different mechanism for tracking and minimizing database hits.
From an MSDN article this is summed up (emphasis mine):
If you serialize entities from one tier to another, the recommended
pattern is to keep the context around on the mid-tier only long enough
for a single service method call. Subsequent calls will spin up a new
instance of the context to complete each task.
A thread on EF/WCF/N-tier may also give you some insights, and Jorge's blog post #5 talks about EF in N-Tiers (the whole series might be a good read). And by the way, I've run into the exact same thing: many clients hitting the context at the same time, resulting in this issue.
I encountered this error and have appeared to have solved it by providing an override to the Dispose() method in the controller. It would appear that force closing the database connection before attempting to open an new one subverts this error.
protected override void Dispose(bool disposing)
{
if(disposing)
{
_fooRepository.Dispose();
}
base.Dispose(disposing);
}
This seems as one of two things, a race condition of some sorts or a "context scoping" issue. You should make sure that the context is being initialized in a thread safe way and that the context is not being accessed by different threads to prevent race conditions. A hard to catch cause of this error is also the accessing of the model itself in the OnModelCreation override.
I experienced this problem today. The problem was that I was accidentally using the same instance of my DbContext across requests. The first request would create the instance and start to build up the model, and the second request would come in and try to retrieve data whilst it was still building up.
My mistake was silly. I accidentally used HttpContext.Current.Cache instead of HttpContext.Current.Items :)

Categories