What's the best way to defer actions after a request in ASP.NET? - asp.net

I'm writing an ASP.NET application. When a specific kind of request is being handled, I want to schedule a method to be called in a certain number of minutes after the request is handled. The postponed method does not need to communicate with the client that made the original request, it is just intended to do some "housekeeping" work. What is the best way to do this in an ASP.NET context? (It's ok to not fire the event if the application domain dies for some reason).

In Global.asax use this to check your incoming request:
protected void Application_BeginRequest(object sender, EventArgs e)
{
CheckRequest(HttpContext.Current.Request);
}
if your request is valid, register a cache entry:
private void CheckRequest(HttpRequest request)
{
if (request)
RegisterCacheEntry();
}
private void RegisterCacheEntry()
{
if (HttpRuntime.Cache[CacheItemKey] == null)
{
HttpRuntime.Cache.Add(CacheItemKey, "your key", null,
DateTime.Now.AddSeconds(60), //change to fire in whatever time frame you require
Cache.NoSlidingExpiration,
CacheItemPriority.NotRemovable,
new CacheItemRemovedCallback(CacheItemRemovedCallback));
}
}
then process your function in the callback:
private void CacheItemRemovedCallback(string key, object value, CacheItemRemovedReason reason)
{
// execute your function
}

You could start a timer (System.Timers.Timer) from one of the application event in global.asax.cs (e.g. in Application_BeginRequest) after checking that it is required for that request.
Then, in the handler of the timer's Elapsed event, make sure that you stop the timer.
E.g. put something like this into global.asax.cs:
System.Timers.Timer _timer = null;
void Application_BeginRequest(object sender, EventArgs e)
{
// check if cleanup must be initiated
bool mustInitCleanup = RequestRequiresCleanup();
if ((_timer == null) && mustInitCleanup)
{
_timer = new System.Timers.Timer(5000);
_timer.Elapsed += new System.Timers.ElapsedEventHandler(_timer_Elapsed);
_timer.Start();
}
}
void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
_timer.Stop();
_timer = null;
// do cleanup task
}

Simply create a new thread to do the housekeeping work and at its beginning have it sleep for however long you want the server to wait before doing the action.
For example, somewhere in that specific request you want to call DoSomething:
aNewThread = new Thread(Foo);
aNewThread.Start();
public void Foo()
{
Thread.Sleep(5000);
DoSomething();
}

Related

Independent thread in Asp.net 4.5

protected void Button1_OnClick(object sender, EventArgs e)
{
FineTuneDB();// Long Task running in db
SendSMStoAllClients();// Using Twolio API to send sms to all client long task
lblText.Text = "Button click is completed our system threads working on your request";
}
Is this possible that on button click I can response to client and independent long task going on separately.
If you don't care about whether task is completed or not, you call FineTuneDB method like this.
Action fineTuneDB = FineTuneDB;
fineTuneDB.BeginInvoke(null, null);
Asynchronous Method Invocation
Updated:
Action<int, string> fineTuneDB = FineTuneDB;
fineTuneDB.BeginInvoke((int)Session["id"],
Session["name"].ToString(), null, null);
// Your method will be like this
public void FineTuneDB(int id, string)
{
}

Session was already closed in ASP .NET Session Per Request pattern

I'm using Session Per Request approach in ASP .NET Web Forms application.
This is the code that runs when request ends, and this is how I close session:
protected void Application_EndRequest(object sender, EventArgs e)
{
ISession session = ManagedWebSessionContext.Unbind(HttpContext.Current, SessionFactoryProvider.SessionFactory);
if (session != null)
{
if (session.Transaction != null && session.Transaction.IsActive)
{
session.Transaction.Rollback();
}
if (session.IsOpen)
{
session.Close();
}
}
}
Pretty obvious stuff. However I'm constantly getting exception when calling session.Close. The exception is of type SessionException and the message is:
{"Session was already closed"}
The stack trace is:
at NHibernate.Impl.SessionImpl.Close() in d:\CSharp\NH\nhibernate\src\NHibernate\Impl\SessionImpl.cs:line 343
The version of NHibernate I'm using is : 3.0.0.4000
I've already searched for code that could close the session, but haven't found any. Also I've tested it with simple requests that do nothing with session object, and still the same. Any thoughts what could be the issue?
EDIT: After divide and conquer session, I've targeted a piece of code that is responsible for that exception:
var session = SessionFactoryProvider.GetCurrentSession();
using (ITransaction tx = session.BeginTransaction())
{
session.Update(instrument);
tx.Commit();
}
If I comment it out session is closed smoothly in EndRequest handler and no Session was already closed exception is thrown.
We are using IHttpModule, this way:
public class ModuleSessionNHibernate : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(BeginTransaction);
context.EndRequest += new EventHandler(CommitAndCloseSession);
}
private void CommitAndCloseSession(object sender, EventArgs e)
{
// commit or rollback depending on errors
// then close session
}
...
}
And in web.config
<httpModules>
<add name="NHibernateSessionModule" type="XXX.ModuleSessionNHibernate, XXX"/>
</httpModules>
EDIT: About commit and close session (but we are using a lot of Helper and Services...)
private void CommitAndCloseSession(object sender, EventArgs e)
{
try
{
if (no exception in context)
{
// commit transaction
}
else
{
// rollback transaction
}
}
finally
{
try
{
// close session
}
catch (Exception ex)
{
// send email
}
// rollback
// close
throw;
}
}
}
Issue was resolved by moving to NHibernate 3.3.1.
However, other issue surfaced:
Could not find the property - exception after switching from NHibernate 3 to 3.3.1

asp.net set sessions before postback

is there an easy way, to store all needed global variables in sessions at once, before the PostBack starts? Or have I to store them in each step where I change them?
I will do something like:
// Global variable.
bool test = true;
// Store all needed information in a session.
protected void Before_globalvariable_is_set_to_default_value(...)
{
Session["Test"] = test;
...
}
protected void Page_Load(object sender, EventArgs e)
{
if(IsPostBack)
{
//if(Session["Test"] != null)
//{
test = (bool)Session["Test"];
Session.Contents.Remove("Test");
//}
}
}
Is something like that possible?
Additional Information
At the Page_Load (!IsPostBack) I check if the user gets more vision, if he gets, I set a global var to true. Later in my code I check if that var is true and add additional columns to a GridView.
Now if a PostBack occurs, I can’t check that var, because I lose the information. I knew that I need to store the information in a Session. If I set the Session at the time where I set the global var to true, I get problems with the session timeout (If the user is on the site, but doesn’t do something for a while). So I thought it will be good, if I set the Session shortly before I lose the information of the global var and delete the Session after reinitialization.
That’s my idea, but I don’t know if something like that is possible.
Edit2:
If I do following it works:
//Global variable
bool test = false;
protected void Page_PreRender(object sender, EventArgs e)
{
Session["Test"] = test;
}
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
test = (bool)Session["Test"]; // Session is true!!!
Session.Contents.Remove("Test");
}
else
{
test = true; // Set at the PageLoad the var to true.
}
}
I’m a little bit confused, I thought PreRender is after the PageLoad, why suddenly the test var is true and if I remove the PreRender it isn’t?
Greetz
If you're worried about losing a specific value between requests, because you've maintained the state of that variable in the Session object and it might have been cleared by a timeout, you could consider using another, more durable, mechanism to save the state: for example, cookies or database.
If the value only needs to live during that one Request, you can use class-level fields of the code-behind class. Set them in the Init or Load phase, then you can use those values in all other phases.
For a lifetime of just a single request:
public partial class MyPage: Page
{
private bool test = true;
public void Page_Load(...)
{
// maybe set 'test' to another value
}
public void Button_Click(...)
{
// you can still access 'test'
}
public void Page_PreRender(...)
{
// you can still access 'test'
}
}
If however you need that value to live from request to the next postback, you can use ViewState instead of Session. Advantage: no timeout as it is stored in the html and posted back from the browser along with other data. Disadvantage: it only works in postback-scanario's, not when you link to a different page.

How do I avoid calling my initialization method repeatedly in ASP.NET?

protected void Page_Load(object sender, EventArgs e) {
if (!IsPostBack) { // sadly, **never** in here }
MyInit() // Slow initialization method, that I only wan't to call one time.
}
So, if I can't tuck my MyInit() in the if, can I solve my performance/strucktur problem with use of OnNeedDataSource()?
Not really sure if this is what you mean, but to initialise something once from Page_Load, you could use a static class with a static bool to determine if it's been initialized. Given it's on Page_Load, you'll also need to guard against multiple threads - so use a double checked lock to make it threadsafe and guard against a race condition.
public static class InitMe
{
private static bool isInitialized = false;
private static object theLock = new Object();
public static void MyInit()
{
if(!isInitialized)
{
lock(theLock);
{
if(!isInitialized) // double checked lock for thread safety
{
// Perform initialization
isInitialized = true;
}
}
}
}
}
and in your Page_Load, call it via InitMe.MyInit()
Hope that helps.
Try this:
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (!Page.IsPostBack) { MyInit(); }
}
I assume you are in a page or user control...
HTH.

Example of Asynchronous page processing in ASP.net webforms (.NET 2.0)

Can someone provide me with a simple example of Asynchronous page processing in ASP.NET Webforms 2.0 (I'm using VS 2010, so new syntax like lambdas are ok)?
I have some long running requests that I don't want tying up IIS threads.
For simplicity's sake, let's say my current code looks like this:
protected void Page_Load(object sender, EventArgs e)
{
string param1 = _txtParam1.Text;
string param2 = _txtParam2.Text;
//This takes a long time (relative to a web request)
List<MyEntity> entities = _myRepository.GetEntities(param1, param2);
//Conceptually, I would like IIS to bring up a new thread here so that I can
//display the data after it has come back.
DoStuffWithEntities(entities);
}
How can I modify this code so that it is asynchronous? Let's assume that I already set async="true" in the aspx page.
EDIT
I think I figured out how to get what I'm looking for. I've put the example code in an answer here. Feel free to point out any flaws or changes that can be made.
I asked some folks on the ASP.NET team. Here's their emailed response to me, and now, to you.
All that code ends up doing is spinning up a new thread and performing delegate invocation on that thread. So now there are two threads running: the request thread and the new thread. Hence this sample actually has worse performance than the original synchronous code would have had.
See http://www.asp.net/web-forms/tutorials/aspnet-45/using-asynchronous-methods-in-aspnet-45 for a sample on how to write and consume async methods in ASP.NET.
Here is a simple example of asynchronous processing.
protected void Page_Load(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));
ThreadPool.QueueUserWorkItem(state => Dokimes_Programming_multithread_QueryWorkThead.ThreadProc2());
Debug.Write("Main thread does some work, then sleeps.");
// If you comment out the Sleep, the main thread exits before
// the thread pool task runs. The thread pool uses background
// threads, which do not keep the application running. (This
// is a simple example of a race condition.)
// Thread.Sleep(4000);
txtDebug.Text += "ended";
Debug.Write("end.");
}
// This thread procedure performs the task.
static void ThreadProc(Object stateInfo)
{
// No state object was passed to QueueUserWorkItem, so stateInfo is null.
Debug.Write(" Hello from the thread pool 1.");
}
static void ThreadProc2()
{
// No state object was passed to QueueUserWorkItem, so stateInfo is null.
Debug.Write("Hello from the thread pool 2.");
}
Other way
You can use the PageAsyncTask, see here a full example:
http://msdn.microsoft.com/en-us/library/system.web.ui.pageasynctask.aspx
Something like
clAsynCustomObject oAsynRun = new clAsynCustomObject();
PageAsyncTask asyncTask = new PageAsyncTask(oAsynRun.OnBegin, oAsynRun.OnEnd, oAsynRun.OnTimeout, null, true);
Page.RegisterAsyncTask(asyncTask);
Page.ExecuteRegisteredAsyncTasks();
I think I discovered how to do what I wanted to accomplish... though it may not be the best way, feel free to chime in.
At the time of writing there was only one answer in this thread, by Aristos. While he gave an example of executing an asynchronous request, what I wanted was a way to tell ASP.NET to execute some long running method, release the IIS thread so it can be available to service other requests, and then come back when the method finished.
Here's what I came up with, using the same (or similar) example in the question:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Web.UI;
namespace WebApplication2
{
public class MyEntity
{
public string Name { get; set; }
}
public class MyRepository
{
public List<MyEntity> GetEntities(string param1, string param2)
{
Thread.Sleep(10000);
return new List<MyEntity> {new MyEntity {Name = "John Smith"}};
}
}
public partial class Default : Page
{
private readonly MyRepository _myRepository = new MyRepository();
private List<MyEntity> _myEntities;
protected void Page_Load(object sender, EventArgs e)
{
}
private void DoStuffWithEntities()
{
Response.Write("<br/><br/><b>" + _myEntities[0].Name + "</b><br/><br/>");
}
protected void _btnProcess_Click(object sender, EventArgs e)
{
AddOnPreRenderCompleteAsync(BeginExecution, EndExecution, null);
}
private void GetEntities()
{
string param1 = _txtParam1.Text;
string param2 = _txtParam2.Text;
//This takes a long time (relative to a web request)
_myEntities = _myRepository.GetEntities(param1, param2);
}
private IAsyncResult BeginExecution(object sender, EventArgs e, AsyncCallback callback, object state)
{
var t = new ThreadStart(GetEntities);
return t.BeginInvoke(callback, null);
}
private void EndExecution(IAsyncResult result)
{
//Conceptually, I would like IIS to bring up a new thread here so that I can
//display the data after it has come back.
DoStuffWithEntities();
}
}
}

Resources