I have a scenario where i have a page which opens a dialog on click of a button, in the opened dialog form on button click i can read a list of data from a selected .txt file and build a query and add the data to some database tables. Since there could be large amount of data this process can take large time because of this the user would not be able to work on the application until the upload completes. Hence to make the upload process Asynk i am using the PageAsyncTask. Below is the code sample, but in the method called in the PageAsyncTask the HttpContext.Current is null hence i am not able to use session handling. Please any guidance on this why would this be null and how can i use the session in this case
protected void BtnUpload_click(object sender, EventArgs e)
{
PageAsyncTask asyncTask1 = new PageAsyncTask(OnBegin, OnEnd, OnTimeout, SessionManager.UserData, true);
Page.RegisterAsyncTask(asyncTask1);
Page.ExecuteRegisteredAsyncTasks();
}
public IAsyncResult OnBegin(object sender, EventArgs e,
AsyncCallback cb, object extraData)
{
_taskprogress = "AsyncTask started at: " + DateTime.Now + ". ";
uData = extraData as UserData;
_dlgt = new AsyncTaskDelegate(BeginInvokeUpload);
IAsyncResult result = _dlgt.BeginInvoke(cb, extraData);
return result;
}
private void BeginInvokeUpload()
{
string selectedFileName = string.Empty;
string returnValuePage = string.Empty;
User teller = new User();
SessionManager.UserData = uData;
}
private void BeginInvokeUpload()
{
string selectedFileName = string.Empty;
string returnValuePage = string.Empty;
User teller = new User();
SessionManager.UserData = uData;
}
public class SessionManager
{
public static UserData UserData
{
get
{
UserData userData = null;
if (HttpContext.Current.Session["UserData"] != null)
{
userData = HttpContext.Current.Session["UserData"] as UserData;
}
return userData;
}
set
{
HttpContext.Current.Session["UserData"]=value;
}
}
}
The answer is simple : you can not use the session if the HttpContext.Current is null
So if you need to modify the session you simple can not and the only alternative is to make your totally custom session module/solution.
If you only need to read some values, then you can pass them when you create your thread.
And finally the only solution is to not use the thread if you won to manipulate the session variables.
why this design?
why MS session did not allow you to handle it out side of a page and inside a thread ? the answer is because is need to lock the session data on page processing - with this lock even if you start a thread and been able to get the session data, will not been able to use it parallel.
Also if you been able to use the session your self in a thread, then this thread may lock the entire page view process, because I say it again, session is lock the entire page view, and each page that use the same session are not work in parallel
This lock of session on the entire page is necessary the way the MS session works, and the only way to avoid that is to make a totally custom session solution, and handle special cases with different code.
The good about that design is that you avoid to make a lot of locking and synchronization by your self on every page call - for example if you disable the session on a page, and use that page for data inserting, if a use make multiple double clicks on the insert, and you do not handle it with synchronization on the insert, you end up with multiple same insertions.
More about session lock:
Replacing ASP.Net's session entirely
Web app blocked while processing another web app on sharing same session
jQuery Ajax calls to web service seem to be synchronous
ASP.NET Server does not process pages asynchronously
Similar question:
How to get Session Data with out having HttpContext.Current, by SessionID
Related
I want to use a Session object through all my pages in my asp.net site, first I Save an object in my session like this, this line of code is in an HttpHandler
HttpContext.Current.Session["DocumnetInfo"] = doc;
after that I created a a thread to manipulate this doc and send the session as parameter to the thread as follows
HttpContext ctx = HttpContext.Current;
Thread t = new Thread(new ThreadStart(() =>
{
// HttpContext.Current = ctx;
SomeMethod(ctx);
}));
t.Start();
and In SomeMethod I read the Session as follows:
private void SomeMethod ( HttpContext ctx)
{
DocResultsBLL doc = (DocResultsBLL)ctx.Session["DocumnetInfo"];
// Here is the logic of the manipulation
// then save the doc in the session back
ctx.Session["DocumnetInfo"]=doc;
Response.Redirect("ResultsPage.aspx");
}
The problem is that I couldn't read the session in the results page.. HttpContext.Current is null.
1-How can I work with session , to send it to a thread, then to get it back outside the thread.
2- Is there any other scenario other than session that is better?
3- How can I stop the Thread if the Client Closed his browser?
The only you can do is like passing http context see the following link and you will understand why its not possible to have session available in multi threading application.
http://blogs.msdn.com/b/webtopics/archive/2009/01/30/why-can-t-i-execute-two-requests-from-the-same-session-simultaneously-for-an-asp-net-application.aspx
I have an IIS hosted web application with a C# backend.
When a user logs in, I want to instantiate an instance of HttpClient() for the logged in user to communicate with the back-end over a REST API. Once that client is created, the backend will initialize some user-specific memory which should be cleared once the user has logged out (that is, the HttpClient() object is disposed).
It seems like the right thing to do here is to instantiate that HttpClient() object at log-in, and then have some code that is called when either the user manually logs out or the user session times out or the user closes the browser, and that code will dispose of the HttpClient() manually.
This is surely a well-travelled problem, so there must be an elegant solution to it. How can I dispose of this user-specific HttpClient() when any possible log-out scenario occurs (manual/timeout/browser close)?
Handling the departure of a web user is not trivial, as the HTTP protocol is stateless. The server can never be certain if the user is still there; a HTTP connection that gets closed doesn't mean that user have to have gone away, and the server can think that a connection is still open eventhough the user is no longer there.
Unless you will be using the HttpClient object intensly, so that you expect that keeping it alive would save a lot of resources, you should just dispose it at the end of each REST request, and open a new one for the next request.
A web request normally takes a short time to handle, and most resources used for it is freed when the request is gone. That make most of the objects short lived, and those are the ones that the garbage collector handles most efficiently. Holding on to objects across several requests makes them very long lived, which uses up memory on the server, and make the garbage collector work harder. Unless there is a specific reason to hold on to an object, you shouldn't let it live longer than it takes to handle the request.
What you could do is create a class which performs the user-specific memory functions you want to perform. This class would contain a method which instantiates the HttpClient() object and then performs the user-specific operations(functions). This class would also contain another method which clears the user-specific memory functions i.e. it disposes the HttpClient() object and performs cleanup of any user-specific data.
So, essentially, you code would look like this:
public class HttpHelper
{
public void LoadUserInformation()
{
HttpClient httpClientObj = new HttpClient();
//perform user-specific tasks
//your logic here
//Store the httpClientObj object in session
}
public void DisposeUserInformation()
{
//Fetch the httpClientObj from session
//perform user-specific tasks
//your logic here
httpClient.Dispose();
}
}
Now, in either of the scenarios, whether the session times out or the user logs out, you could call the DisposeUserInformation() method and that would handle both of your scenario's be it session timing out or user logging out.
There is a Session_End() method in global.asax. The global.asax file will be wired to call this method when the session ends. You can call the DisposeUserInformation() method there.
You could also call this method on the logout button click in the controller.
Hope this helps!!!
I really don't recommend storing anything IDisposable in the session. What if in the process of downloading from the Web APi, in another window the user clicks Logout, you disposed of the HttpClient while it's in use. That is a small edge-case, but there can be plenty of edge cases with storing IDisposable in session. Also if you need to scale out to multiple servers, that requires storing Session in something other than in-proc which requires the object to be serializable (which HttpClient is not).
Instead:
[serializable]
public sealed class ApiClient
{
public ApiClient(uri baseAddress)
{
this._BaseAddress = baseAddress;
}
public Uri BaseAddress { get; set; }
public IEnumerable<Person> GetPersons()
{
var address = new Uri(this.BaseAddress, "Employees/Persons");
using (var client = new HttpClient())
{
// something like this
var task = GetStringAsync(address);
await task;
var json = task.Result;
}
}
}
Nice session wrapper:
public static class SessionExtensions
{
public static bool TryGetValue<T>(this HttpSessionStateBase session, out T value)
where T : class
{
var name = typeof(T).FullName;
value = session[name] as T;
var result = value != null;
return result;
}
public static void SetValue<T>(this HttpSessionStateBase session, T value)
{
var name = typeof(T).FullName;
session[name] = value;
}
public static void RemoveValue<T>(this HttpSessionStateBase session)
{
var name = typeof(T).FullName;
session[name] = null;
}
public static bool ValueExists(this HttpSessionStateBase session, Type objectType)
{
var name = objectType.FullName;
var result = session[name] != null;
return result;
}
}
Now you can create the api per client:
Session.SetValue(new ApiClient(new Uri("http://localhost:443")));
Somewhere else you can get persons:
ApiClient client;
if (Session.TryGetValue(out client))
{
client.GetPersons();
}
I know this probably isn't possible, but I would like to be able to get the Request user ID from within an ASP.NET web service method. So far, I've tried User.Identity.Name, Context.Request.LogonUserIdentity.Name, Request.ServerVariables["AUTH_USER"] and Request.ServerVariables["LOGON_USER"]. Am I tilting at windmills here, or is there something super simple that I'm missing?
Well, what do you mean by User ID?
If they've authenticated via Windows Authentication, User.Identity gives you the WindowsIdentity object that corresponds to that user.
If you want the User ID which corresponds to an authenticated user to "magically" show up in your pages, you can do that too! In your Global.asax, there is a function called Application_AuthenticateRequest which you implement to take whatever identity is passed to your application and turn it into a IPrincipal-based object which can be accessed from your pages.
So when you implement AuthenticateRequest(), you can take the HttpContext.Current.User.Identity.Name, and use that to look up your User ID from your database. From there, you construct your own IPrincipal-derived object and set the HttpContext.Currrent.User reference to that object you create. You can then cast "User" in your pages over to the object you create and read the User ID. We do this all the time. Here's some sample code (which actually caches the Principal object so that you don't have to go to the DB on every request):
protected void Application_AuthenticateRequest(object sender, EventArgs e) {
try {
IIdentity myIdentity = HttpContext.Current.User.Identity;
MyPrincipal myPrincipal = (MyPrincipal)HttpContext.Current.Cache[myIdentity.Name];
if (myPrincipal == null) {
myPrincipal = (MyPrincipal)GetPrincipalFromDatabase(HttpContext.Current.User.Identity);
HttpContext.Current.Cache.Insert(myIdentity.Name, myPrincipal, null, DateTime.Now.AddMinutes(1), TimeSpan.Zero);
}
HttpContext.Current.User = myPrincipal;
}
catch (SecurityException) {
HttpContext.Current.User = null;
}
catch (Exception ex) {
Trace.WriteLine("Could not validate your user.");
}
}
Can anybody help me to find out solution of following problem.
In ASP.NET website: at Application_OnPostAuthenticate() event, whatever code i write is executed for every request. therefore due to this customidentity object, countryid and weatherid is called everytime for each request (call for database for value). It effect response time of page and unneccessary code execute.
void Application_OnPostAuthenticateRequest(object sender, EventArgs e)
{
// Get a reference to the current User
IPrincipal objIPrincipal = HttpContext.Current.User;
// If we are dealing with an authenticated forms authentication request
if ((objIPrincipal.Identity.IsAuthenticated) && (objIPrincipal.Identity.AuthenticationType == "Forms"))
{
CustomPrincipal objCustomPrincipal = new CustomPrincipal();
objCustomPrincipal = objCustomPrincipal.GetCustomPrincipalObject(objIPrincipal.Identity.Name);
HttpContext.Current.User = objCustomPrincipal;
CustomIdentity ci = (CustomIdentity)objCustomPrincipal.Identity;
HttpContext.Current.Cache["CountryID"] = FatchMasterInfo.GetCountryID(ci.CultureId);
HttpContext.Current.Cache["WeatherLocationID"] = FatchMasterInfo.GetWeatherLocationId(ci.UserId);
Thread.CurrentPrincipal = objCustomPrincipal;
}
}
To solve this problem when i try tochange code as follows
HttpContext.Current.Session.Add("test", FatchMasterInfo.GetWeatherLocationId(ci.UserId);); in place of cache i found foolowing error
"Object refrence not set to the instance of object"
I don't know whether we can store session variable inside Application_OnPostAuthenticate() event or not?
You could try doing this a bit later in the request, such as in the PreRequestHandlerExecute event:
protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
IPrincipal objIPrincipal = HttpContext.Current.User;
if ((objIPrincipal.Identity.IsAuthenticated) && (objIPrincipal.Identity.AuthenticationType == "Forms"))
{
HttpSessionState session = HttpContext.Current.Session;
CustomPrincipal objCustomPrincipal = new CustomPrincipal();
if (session[objIPrincipal.Identity.Name] == null)
{
// get data from database or wherever
objCustomPrincipal = objCustomPrincipal.GetCustomPrincipalObject(objIPrincipal.Identity.Name);
CustomIdentity ci = (CustomIdentity)objCustomPrincipal.Identity;
Object countryID = FatchMasterInfo.GetCountryID(ci.CultureId);
Object weatherLocationID = FatchMasterInfo.GetWeatherLocationId(ci.UserId);
// save in session (not cache as cache is application-wide, not per-user):
session.Add(objIPrincipal.Identity.Name, objCustomPrincipal);
session.Add(objIPrincipal.Identity.Name + "_CountryID", countryID);
session.Add(objIPrincipal.Identity.Name + "_WeatherLocationID", weatherLocationID);
}
else
{
// already have custom principal object in session
objCustomPrincipal = (CustomPrincipal)session[objIPrincipal.Identity.Name];
}
// set the custom principal object to context/thread
HttpContext.Current.User = objCustomPrincipal;
Thread.CurrentPrincipal = objCustomPrincipal;
}
}
You probably don't want to access the session in any event that happens in every request. Some requests don't even have session (for instance, a lot of web service calls, or calls to WebResource.axd that load static resources).
Before adding value to cache object, check if it already exists in the cache.
You might not have session state enabled. Does it work anywhere else (like in a web form's display)?
Look for a <sessionState> element under your system.web element in web.config make sure it's turned on (set it to InProc unless you have a web farm).
how can I get IDs of all current sessions?
To get the session id, do this:
// In a user control or page
string sessionId = this.Session.SessionID;
// In a normal class, running in a asp.net app.
string sessionId = System.Web.HttpContext.Current.Session.SessionID;
You should not need to:
Make any data table or loop anything
Use SQL server for session state
Handle Session_Start or Session_End
In a cookieless scenario, the session id is created when you access the Session object for the first time. This shouldn't matter much, because the moment you access the SessionID property, the session object is accessed.
For more info, look into this:
http://msdn.microsoft.com/en-us/library/system.web.sessionstate.httpsessionstate.sessionid.aspx
Note: The msdn examples have been written by monkeys.
You can use Global.asax file and set the Session at Session_Start event. See below
in Global.asax file you can do something like this:
protected void Session_Start(object sender, EventArgs e)
{
Session["sid"] = Session.SessionID;
Session["sid"] = "Test";
}
Then in your WebForm you can get the Session ID and Value like below
protected void Page_Load(object sender, EventArgs e)
{
Response.Write("Session ID is:" + Session.SessionID.ToString()+ "<br/>");
Response.Write("Session value is:" + Session["sid"].ToString());
}
For details, see http://www.dotnetcurry.com/ShowArticle.aspx?ID=126
According to Dino Esposito each session is stored in the application's Cache and with some work you can retreive this information:
DataTable dt = new DataTable();
dt.Columns.Add("SessionID", typeof(string));
foreach(DictionaryEntry elem in Cache) {
string s = elem.Key.ToString();
if (s.StartsWith("System.Web.SessionState.SessionStateItem")) {
DataRow row = dt.NewRow();
char[] parms = {':'};
string[] a = s.Split(parms);
row["SessionID"] = a[1];
dt.Rows.Add(row);
}
}
If you want a way to store a list of the current sessions where you control the backing store, so that you may store extra data about the client, you can use a list. (I'm writing the following example from the top of my head)
Hook into Application_SessionStart in the global.asax.cs file:
static List<string> sessions = new List<string>();
static object sessionLock = new object();
void Application_SessionStart()
{
lock (sessionLock) {
sessions.Add(Session.SessionID);
}
}
void Application_SessionEnd()
{
lock (sessionLock) {
sessions.Remove(Session.SessionID);
}
}
Alternatively, you can use a dictionary, storing the session ID as a key, and extra data about that user as the value. Then you can easily create a page that shows all current user sessions, for example, for an admin site to show current user sessions.
SessionEnd will only be called if your sessions are InProc.
The answer depends partially on where you store session state. Assuming you use the default (inproc) then you can maintain a list of current session ids using the Session_Start and Session_End events in global.asax.
If you are storing your session state in SQL Server, you can also easily get it from there.