I am trying to store data in an old program / application on the API side, and while I am able to access the session, I can store data, but every sessions is new, so I do not have my data from "last" session.
The session is InProc.
The question is what could be wrong, since I get a new session every time. I can see that IsNewSession is always true and I get a new SessionId.
I searched a lot a put this together. I am new to sessions, so I cannot ask specifically.
The application is using nuget packages for AspNet.Mvc version 5.2.7
My code:
public class WebApiApplication : System.Web.HttpApplication
{
...
protected void Session_Start()
{
// I always get a new session here
Log.Information(Session.Keys.Count.ToString());
Log.Information(Session.SessionID);
Log.Information(Session.IsNewSession ? "Yes":no");
Session.Add("x", DateTime.Now.ToString());
}
protected void Session_End(object sender, EventArgs e)
{
// here I have session data
Log.Information("session end");
}
protected void Application_PostAuthorizeRequest()
{
if (IsWebApiRequest())
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
private bool IsWebApiRequest()
{
return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(WebApiConfig.UrlPrefixRelative);
}
And the web.config
public static class WebApiConfig
{
public static string UrlPrefix { get { return "api"; } }
public static string UrlPrefixRelative { get { return "~/api"; }
}
Related
I created the nhibernate session in Application_start event of global.asax file,the session is being passed to constructors of service methods.
In the service method I am using the session to do CRUD operations, this works fine.However, When multiple requests or parallel transactions occuring nhibernate is throwing some exceptions.After reading forums i came to know that Nhibernate session is not thread safe.How to make it thread safe and let my application (ASP.NET mvc) work with parallel trandsactions?
Only way to make it thread safe is to create a new session per each request, you could use current_session_context_class property to managed_web in NHibernate config.
In global.asax
protected void Application_BeginRequest(object sender, EventArgs e)
{
var session = SessionFactory.OpenSession();
CurrentSessionContext.Bind(session);
}
protected void Application_EndRequest(object sender, EventArgs e)
{
var session = CurrentSessionContext.Unbind(SessionFactory);
//commit transaction and close the session
}
now when you want to access the session, you could use,
Global.SessionFactory.GetCurrentSession()
If you are using a DI container, it's usually built into the container,
For example for Autofac (see this question for more information),
containerBuilder.Register(x => {
return x.Resolve<ISessionFactory>().OpenSession();
}).As<ISession>().InstancePerHttpRequest();
Store it in the HttpContext.
Add this to your global.asax
public static String sessionkey = "current.session";
public static ISession CurrentSession
{
get { return (ISession)HttpContext.Current.Items[sessionkey]; }
set { HttpContext.Current.Items[sessionkey] = value; }
}
protected void Application_BeginRequest()
{
CurrentSession = SessionFactory.OpenSession();
}
protected void Application_EndRequest()
{
if (CurrentSession != null)
CurrentSession.Dispose();
}
And here is the component registration
public class SessionInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container
.Register(Component.For<ISession>().UsingFactoryMethod(() => MvcApplication.CurrentSession)
.LifeStyle
.PerWebRequest);
}
}
I have no background in WCF yet so what I have done so far is based only in research. What I am trying to do is this.
I created a solution with three projects. Two Windows Form Applications and one WCF Service Application.
In form 1, there is a textbox and a button. If update button is clicked, whatever the value in the textbox should be reflected in the other form if refresh button is clicked. Here is what I have done so far:
ISimulatorService.cs
[ServiceContract]
public interface ISimulatorService
{
[OperationContract]
void SetSerialNumber(int value);
[OperationContract]
int GetSerialNumber();
}
ISimulatorService.svc
public class Service1 : ISimulatorService
{
public int serial;
public string modelName;
public void SetSerialNumber(int value)
{
this.serial = value;
}
public int GetSerialNumber()
{
return serial;
}
}
Update button in Form1
private void btn_update_Click(object sender, EventArgs e)
{
ServiceReference.SimulatorServiceClient client = new ServiceReference.SimulatorServiceClient();
try
{
client.Open();
client.SetSerialNumber(Convert.ToInt32(txt_serialnumber.Text));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
if (client.State == System.ServiceModel.CommunicationState.Opened)
{
//MessageBox.Show("Communication Closed");
client.Close();
}
}
}
Refresh button in Form 2
private void btn_update_Click(object sender, EventArgs e)
{
ServiceReference.SimulatorServiceClient client = new ServiceReference.SimulatorServiceClient();
txt_serialnumber.Text = client.GetSerialNumber().ToString();
}
The return value is always zero. What should I implement to store a value so I can retrieve that value in the other form?
Your serial variable is not persited between calls. The service will be activated the first time when you call SetSerialNumber then closed, and it will be activated a second time when you call GetSerialNumber then closed again. In short therms, it will be two different instances, therefore you need something to persist the data beetween calls. You have two cases :
You don't need the data to persisted on disk, then use something like MemoryCache. Here is a short snippet :
public class Service1 : ISimulatorService
{
private ObjectCache _cache = MemoryCache.Default;
private CacheItemPolicy policy = new CacheItemPolicy();
public void SetSerialNumber(int value)
{
_cache.Set("serial", value, policy);
}
public int GetSerialNumber()
{
(int)_chache["serial"];
}
}
You need the data to be peristed on disk, then use a database.
I want to log the first request hitting my server, so I have wrote that portion of code in my Global.asax.cs
private bool _startupHasBeenLogged = false;
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (!_startupHasBeenLogged)
{
DoLog();
_startupHasBeenLogged = true;
}
}
The problem is that I get the log for the first 5 or 6 requests. I guess the server receive multiple request at once, and so DoLog is hitting multiple times before the boolean becomes true.
I have tried using Interlocked:
int running = 0;
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (Interlocked.CompareExchange(ref running, 1, 0) != 0) return;
if (!_startupHasBeenLogged)
{
_startupHasBeenLogged = true;
DoLog();
}
Interlocked.Exchange(ref running, 0);
}
And Monitor.TryEnter:
private static readonly object _lock = new object();
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (Monitor.TryEnter(_lock))
{
try
{
if (!_startupHasBeenLogged)
{
_startupHasBeenLogged = true;
DoLog();
}
}
finally
{
Monitor.Exit(_lock);
}
}
}
But everytime, the log is firing 5 or 6 times.
So how do I run a code only one time for the very first Request in a webserver?
Edit Solution :
The details I missed was that the Application is instanciated multiple times, so _startupHasBeenLogged had multiple instances. The lock works fine. Just declare a static bool
private static bool _startupHasBeenLogged = false;
ASP.NET instantiates your HttpApplication derived class multiple times. I think it's an object pool. I find this design appalling. It has no practical use.
Don't put state in the application object. Create a separate class that has static variables. They are global per AppDomain. ASP.NET can't mess with them.
Of course, there can be situations where multiple app domains are running at the same time but that's rare. it happens when recycling a pool for example.
There is the following overrides on controller.
protected override void OnActionExecuting(ActionExecutingContext filterContext)
protected override void OnActionExecuted(ActionExecutedContext filterContext)
I have used this in a baseController class before.
In my program I'm trying to use session variable in IHttpModule. Here is my code. This is working fine in VS 2010 development server. But when I try to debug in IIS7 it shows exception System.Web.HttpException: Session state is not available in this context
So why session not available in IIS 7 but in development server.
using System;
using System.Web;
public class Globalizer : IHttpModule
{
public void Init(HttpApplication context)
{
context.AcquireRequestState += new EventHandler(setLanguage);
}
public void Dispose(){}
public void setLanguage(Object sender, EventArgs i_eventArgs)
{
HttpApplication http_application = sender as HttpApplication;
http_application.Session["language"] = "test";
}
}
All you need is to implement IRequiresSessionState.
So your code should look like:
public class Globalizer : IHttpModule, IRequiresSessionState
if all you use is reading Sessions (and never update them) then you should use IReadOnlySessionState as this last do not lock the session state, and therefor, you will not have concurrent requests).
If possible, move your handler to a PostAcquireRequestState handler:
Occurs when the request state (for example, session state) that is associated with the current request has been obtained.
(Or an even later event)
The SessionStateModule itself loads session state in response to the AcquireRequestState event - So at the moment, you're depending on whether your module or the session state module's handlers fire first.
Found this over on the ASP.NET forums:
using System;
using System.Web;
using System.Web.Security;
using System.Web.SessionState;
using System.Diagnostics;
// This code demonstrates how to make session state available in HttpModule,
// regradless of requested resource.
// author: Tomasz Jastrzebski
public class MyHttpModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.PostAcquireRequestState += new EventHandler(Application_PostAcquireRequestState);
application.PostMapRequestHandler += new EventHandler(Application_PostMapRequestHandler);
}
void Application_PostMapRequestHandler(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
if (app.Context.Handler is IReadOnlySessionState || app.Context.Handler is IRequiresSessionState) {
// no need to replace the current handler
return;
}
// swap the current handler
app.Context.Handler = new MyHttpHandler(app.Context.Handler);
}
void Application_PostAcquireRequestState(object source, EventArgs e)
{
HttpApplication app = (HttpApplication)source;
MyHttpHandler resourceHttpHandler = HttpContext.Current.Handler as MyHttpHandler;
if (resourceHttpHandler != null) {
// set the original handler back
HttpContext.Current.Handler = resourceHttpHandler.OriginalHandler;
}
// -> at this point session state should be available
Debug.Assert(app.Session != null, "it did not work :(");
}
public void Dispose()
{
}
// a temp handler used to force the SessionStateModule to load session state
public class MyHttpHandler : IHttpHandler, IRequiresSessionState
{
internal readonly IHttpHandler OriginalHandler;
public MyHttpHandler(IHttpHandler originalHandler)
{
OriginalHandler = originalHandler;
}
public void ProcessRequest(HttpContext context)
{
// do not worry, ProcessRequest() will not be called, but let's be safe
throw new InvalidOperationException("MyHttpHandler cannot process requests.");
}
public bool IsReusable
{
// IsReusable must be set to false since class has a member!
get { return false; }
}
}
}
Found the reason.
If is because AcquireRequestState trigger for the all files such as CSS, JS, images. those files are not having session.
solution: Seems there is a way to avoid calling IHttpModule for all the request. ckeck this answer JS,Images and CSS getting intercepted by HTTPModule.
But it didn't work for me. So I uses HttpContext.Current.Session instead of HttpApplication.Session and every time it checks if Session is null before save to session.
If some one knows, How to avoid calling IHttpModule for files other than .aspx Please help here.
Here is the final code
using System;
using System.Web;
public class Globalizer : IHttpModule
{
public void Init(HttpApplication context)
{
context.AcquireRequestState += new EventHandler(setLanguage);
}
public void Dispose(){}
public void setLanguage(Object sender, EventArgs i_eventArgs)
{
if(HttpContext.Current.Session != null){
HttpContext.Current.Session["language"] = "test";
}
}
}
Edit: One other way is only use session if request comes to a .aspx file
HttpApplication http_application = sender as HttpApplication;
HttpContext context = http_application.Context;
if(Path.GetExtension(context.Request.PhysicalPath) == ".aspx")
{
HttpContext.Current.Session["language"] = "test";
http_application.Session["language2"] = "test2";
}
I have a WebSite with a custom Cache object inside a class library. All of the projects are running .NET 3.5.
I would like to convert this class to use Session state instead of cache, in order to preserve state in a stateserver when my application recycles.
However this code throws an exception with "HttpContext.Current.Session is null" when I visit the methods from my Global.asax file. I call the class like this:
Customer customer = CustomerCache.Instance.GetCustomer(authTicket.UserData);
Why is the object allways null?
public class CustomerCache: System.Web.SessionState.IRequiresSessionState
{
private static CustomerCache m_instance;
private static Cache m_cache = HttpContext.Current.Cache;
private CustomerCache()
{
}
public static CustomerCache Instance
{
get
{
if ( m_instance == null )
m_instance = new CustomerCache();
return m_instance;
}
}
public void AddCustomer( string key, Customer customer )
{
HttpContext.Current.Session[key] = customer;
m_cache.Insert( key, customer, null, Cache.NoAbsoluteExpiration, new TimeSpan( 0, 20, 0 ), CacheItemPriority.NotRemovable, null );
}
public Customer GetCustomer( string key )
{
object test = HttpContext.Current.Session[ key ];
return m_cache[ key ] as Customer;
}
}
As you can see I've tried to add IRequiresSessionState to the class but that doesn't make a difference.
Cheers
Jens
It isn't really about including the State inside your class, but rather where you call it in your Global.asax. Session isn't available in all the methods.
A working example would be:
using System.Web.SessionState;
// ...
protected void Application_PreRequestHandlerExecute(object sender, EventArgs e)
{
if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState)
{
HttpContext context = HttpContext.Current;
// Your Methods
}
}
It will not work e.g. in Application_Start
Depending on what you're trying to do, you may also benefit from using Session_Start and Session_End in Global.asax:
http://msdn.microsoft.com/en-us/library/ms178473(v=vs.100).aspx
http://msdn.microsoft.com/en-us/library/ms178581(v=vs.100).aspx
void Session_Start(object sender, EventArgs e)
{
// Code that runs when a new session is started
}
void Session_End(object sender, EventArgs e)
{
// Code that runs when a session ends.
// Note: The Session_End event is raised only when the sessionstate mode
// is set to InProc in the Web.config file. If session mode is set to StateServer
// or SQLServer, the event is not raised.
}
Note the restrictions on SessionState Mode before relying on Session_End.