Using Session with Threads in asp.net - asp.net

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

Related

Getting Cannot access a disposed object. Object name: 'SocketsHttpHandler' exception in .Net 6 Application

In one of my Azure Function app(.Net 6 isolated process) and I am making some http requests with a client certificate. I'm registering my services in the Program.cs like this,
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(clientCertificate);
services.AddHttpClient().Configure<HttpClientFactoryOptions>(
"myClient", options =>
options.HttpMessageHandlerBuilderActions.Add(builder =>
builder.PrimaryHandler = handler));
services.AddTransient<IMyCustomClient, MyCustomClient>(provider =>
new MyCustomClient(provider.GetService<IHttpClientFactory>(),
cutomParameter1, cutomParameter2));
services.AddSingleton<IMyCustomService, MyCustomService>();
And injecting MyCustomClient in MyCustomService constructor
private readonly IMyCustomClient _myCustomClient;
public PlatformEventManagementService(IMyCustomClient myCustomClient)
{
_myCustomClient = myCustomClient;
}
var result = await _myCustomClient.GetResponse();
It works fine for some time and getting the below exception after sending many requests.
Cannot access a disposed object. Object name: 'SocketsHttpHandler'.
You are supplying the factory with a single instance of HttpClientHandler to use in all clients. Once the default HandlerLifetime has elapsed (2 minutes) it will be marked for disposal, with the actual disposal occurring after all existing HttpClients referencing it are disposed.
All clients created after the handler is marked continue to be supplied the soon-to-be disposed handler, leaving them in an invalid state once the disposal is actioned.
To fix this, the factory should be configured to create a new handler for each client. You may wish to use the simpler syntax shown in the MS documentation.
// Existing syntax
services.AddHttpClient().Configure<HttpClientFactoryOptions>(
"myClient", options =>
options.HttpMessageHandlerBuilderActions.Add(builder =>
{
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(clientCertificate);
builder.PrimaryHandler = handler;
}));
// MS extension method syntax
services
.AddHttpClient("myClient")
// Lambda could be static if clientCertificate can be retrieved from static scope
.ConfigurePrimaryHttpMessageHandler(_ =>
{
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(clientCertificate);
return handler;
});

Changing my code to use async methods instead of sync methods will force my WebClient to never timeout (20 minute++)

I have 2 asp.net MVC web applications , as follow:-
ApplicationA . which is an Asp.net mvc-4 deployed under iis-8.
ApplicationB. which is an Asp.net mvc-5 deployed under iis-8.
now inside my ApplicationA i have the following method,which will call an action method (home/sync) on applicationB , as follow:-
public List<Technology> GetTechnology(int? currentfiltertype)
{
try
{
using (WebClient wc = new WebClient())
{
string url = currentURL + "home/sync?filtertype=" + currentfiltertype;
wc.Headers.Add("Authorization", token);
string json = wc.DownloadString(url);
List<Technology> result = JsonConvert.DeserializeObject<List<Technology>>(json);
return result;
}
}
catch (Exception e){}
}
now i have noted that when the WebClient calls the action method, and the method did not receive a response within around 2 minutes it will raise a timeout exception. But since the home/sync action method on web application B needs around 30 minutes to complete.. so i was searching for a solution to extend the web-client timeout period. so i tried changing my code to use async methods as follow,mainly by replacing wc.DownloadString with wc.DownloadStringTaskAsync as follow:-
public async Task<List<Technology>> GetTechnology(int? currentfiltertype)
{
try
{
using (WebClient wc = new WebClient())
{
string url = currentURL + "home/sync?filtertype=" + currentfiltertype;
wc.Headers.Add("Authorization", token);
string json = await wc.DownloadStringTaskAsync(url);
List<Technology> result = JsonConvert.DeserializeObject<List<Technology>>(json);
return result;
}
}
catch (Exception e) {}
}
and now seems the WebClient will never expired ... i tried calling the action method and the web client keep waiting for a response for more than 20 minutes without raising any timeout exception, then it received the response from web applicationB and everything worked well..
so can anyone advice why changing my code to use async methods as shown in the above code, caused the WebClient to not timeout ?? i can not understand the relation between using async logic and extending the timeout period for the web-client (not sure if the WebClient will ever timeout inside async methods!!)?
can anyone advice why changing my code to use async methods as shown in the above code, caused the WebClient to not timeout ??
The answer is a bit convoluted: WebClient is based on WebRequest, and HttpWebRequest's Timeout property is only honored for synchronous requests.
(noy sure if the WebClient will ever timeout inside async methods!!)?
It does not directly support asynchronous timeouts, but it does support (its own kind of) cancellation, which you can trigger after a timer.

What is the way to perform some session cleanup logic regardless of user logout/timeout/browser close?

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();
}

HttpContext.Current is null when in method called from PageAsyncTask

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

Problem with null object reference in Url.Action in MVC3 project

I am trying to set up a mocking scenario for my payment processor on a web site. Normally, my site redirects to the processor site, where the user pays. The processor then redirects back to my site, and I wait for an immediate payment notification (IPN) from the processor. The processor then posts to my NotifyUrl, which routes to the Notify action on my payments controller (PayFastController). To mock, I redirect to a local action, which after a conformation click, spawns a thread to post the IPN, as if posted by the processor, and redirects back to my registration process.
My mock processor controller uses the following two methods to simulate the processor's response:
[HttpGet]
public RedirectResult Pay(string returnUrl, string notifyUrl, int paymentId)
{
var waitThread = new Thread(Notify);
waitThread.Start(new { paymentId, ipnDelay = 1000 });
return new RedirectResult(returnUrl);
}
public void Notify(dynamic data)
{
// Simulate a delay before PayFast
Thread.Sleep(1000);
// Delegate URL determination to the model, vs. directly to the config.
var notifyUrl = new PayFastPaymentModel().NotifyUrl;
if (_payFastConfig.UseMock)
{
// Need an absoluate URL here just for the WebClient.
notifyUrl = Url.Action("Notify", "PayFast", new {data.paymentId}, "http");
}
// Use a canned IPN message.
Dictionary<string, string> dict = _payFastIntegration.GetMockIpn(data.paymentId);
var values = dict.ToNameValueCollection();
using (var wc = new WebClient())
{
// Just a reminder we are posting to Trocrates here, from PayFast.
wc.UploadValues(notifyUrl, "POST", values);
}
}
However, I get an 'Object reference not set to an instance of an object.' exception on the following line:
notifyUrl = Url.Action("Notify", "PayFast", new {data.paymentId}, "http");
data.paymentId has a valid value, e.g. 112, so I'm not passing any null references to the Url.Action method. I suspect I have lost some sort of context somewhere by calling Notify on a new thread. However, if I use just notifyUrl = Url.Action("Notify", "PayFast");, I avoid the exception, but I get a relative action URL, where I need the overload that takes a protocol parameter, as only that overload gives me the absolute URL that WebClient.UploadValues says it needs.
When you are inside the thread you no longer have access to the HttpContext and the Request property which the Url helper relies upon. So you should never use anything that relies on HttpContext inside threads.
You should pass all the information that's needed to the thread when calling it, like this:
waitThread.Start(new {
paymentId,
ipnDelay = 1000,
notifyUrl = Url.Action("Notify", "PayFast", new { paymentId }, "http")
});
and then inside the thread callback:
var notifyUrl = new PayFastPaymentModel().NotifyUrl;
if (_payFastConfig.UseMock)
{
// Need an absoluate URL here just for the WebClient.
notifyUrl = data.notifyUrl;
}

Resources