ASP.Net MVC 3 Strange Session Behaviour - asp.net

I have an mvc 3 app for which I'm implementing authorization using my own login view which checks if the users name and password are allowed and then sets a variable in the session to say that the user is loggged in. This kind of works but for one particular view it is behaving in a strange undesirable way. The said view contains a form which I use to input some data and upload a file. For some reason which I can't figure out, after this form is posted a new session is started and therefore the variable which remembered that the user was logged in is reset to false and subsequently the login page is displayed again.
I'm lost as to why the application is starting a new session at this point? I have not instructed it to do this. Can anyone recommend solutions to stop this behaviour and get it to keep the old session?
Thanks.
UPDATE - Some Code:
Note the session seems to be terminated immediately after the response to the posted Create form
CMS controller which uses a custom Autorize attribute called "RDAutorize" on all actions:
[RDAuthorize]
public class PhotoCMSController : Controller
{
public ActionResult Create()
{
/* Code omitted: set up a newPhoto object with default state */
/* Display view containing form to upload photo and set title etc. */
return View("../Views/PhotoCMS/Create", newPhoto);
}
[HttpPost]
public ContentResult Upload(int pPhotoId)
{
/* Code ommited: receive and store image file which was posted
via an iframe on the Create view */
string thumbnail = "<img src='/path/to/thumb.jpg' />";
return Content(thumbnail);
}
[HttpPost]
public ActionResult Create(string pPhotoTitle, string pCaption etc...)
{
/*Code omitted: receive the rest of the photo data and save
it along with a reference to the image file which was uploaded
previously via the Upload action above.*/
/* Display view showing list of all photo records created */
return View("../Views/PhotoCMS/Index", qAllPhotos.ToList<Photo>());
/* **Note: after this view is returned the Session_End() method fires in
the Global.asax.cs file i.e. this seems to be where the session is
being lost** */
}
}/*End of CMS Controller*/
Custom Authorize action filter:
public class RDAuthorize : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Boolean authorized = Convert.ToBoolean(
HttpContext.Current.Session["UserIsAuthorized"]
);
if (!authorized) {
/* Not logged in so send user to the login page */
filterContext.HttpContext.Response.Redirect("/Login/Login");
}
}
public override void OnActionExecuted(ActionExecutedContext filterContext) {}
public override void OnResultExecuting(ResultExecutingContext filterContext) {}
public override void OnResultExecuted(ResultExecutedContext filterContext) {}
}/*End of Authorize Action Filter*/
Login controller:
public class LoginController : Controller
{
private PhotoDBContext _db = new PhotoDBContext();
public ActionResult Login()
{
string viewName = "";
Boolean authorized = Convert.ToBoolean(Session["UserIsAuthorized"]);
if (authorized)
{
viewName = "../Views/Index";
}
else
{
viewName = "../Views/Login/Login";
}
return View(viewName);
}
[HttpPost]
public ActionResult Login(string pUsername, string pPassword)
{
string viewName = "";
List<Photo> model = new List<Photo>();
var qUsers = from u in _db.Users
select u;
foreach (User user in qUsers.ToList<User>())
{
/* If authorized goto CMS pages */
if (pUsername == user.Username && pPassword == user.Password)
{
Session["UserIsAuthorized"] = true;
var qPhotos = from p in _db.Photos
where p.IsNew == false
select p;
model = qPhotos.ToList<Photo>();
viewName = "../Views/PhotoCMS/Index";
break;
}
}
return View(viewName, model);
}
}/* End of Login controller */

Turns out the whole ASP.Net application was restarting because as part of the photo upload I was storing the image file in a temporary folder and then deleting the directory after moving the file to a permanent location. Apparently its default behaviour for ASP.Net to restart if a directory within the web site is deleted. I found this post
which describes the problem and offers a solution whereby the following code is added to the Global.asax.cs file. Implementing this solution has fixed the problem. The fix is applied by calling FixAppDomainRestartWhenTouchingFiles() from the Application_Start() event:
protected void Application_Start()
{
FixAppDomainRestartWhenTouchingFiles();
}
private void FixAppDomainRestartWhenTouchingFiles()
{
if (GetCurrentTrustLevel() == AspNetHostingPermissionLevel.Unrestricted)
{
/*
From: http://www.aaronblake.co.uk/blog/2009/09/28/bug-fix-application-restarts-on-directory-delete-in-asp-net/
FIX disable AppDomain restart when deleting subdirectory
This code will turn off monitoring from the root website directory.
Monitoring of Bin, App_Themes and other folders will still be
operational, so updated DLLs will still auto deploy.
*/
PropertyInfo p = typeof(HttpRuntime).GetProperty(
"FileChangesMonitor", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static);
object o = p.GetValue(null, null);
FieldInfo f = o.GetType().GetField(
"_dirMonSubdirs", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.IgnoreCase);
object monitor = f.GetValue(o);
MethodInfo m = monitor.GetType().GetMethod(
"StopMonitoring", BindingFlags.Instance | BindingFlags.NonPublic);
m.Invoke(monitor, new object[] { });
}
}
private AspNetHostingPermissionLevel GetCurrentTrustLevel()
{
foreach (AspNetHostingPermissionLevel trustLevel in
new AspNetHostingPermissionLevel[] {
AspNetHostingPermissionLevel.Unrestricted,
AspNetHostingPermissionLevel.High,
AspNetHostingPermissionLevel.Medium,
AspNetHostingPermissionLevel.Low,
AspNetHostingPermissionLevel.Minimal }
)
{
try
{
new AspNetHostingPermission(trustLevel).Demand();
}
catch (System.Security.SecurityException)
{
continue;
}
return trustLevel;
}
return AspNetHostingPermissionLevel.None;
}

Since sessions are associated with cookies, they are available for a specific domain.
It's a common mistake to ask for a session variable in the same application while the domain has changed (i.e. redirecting to a subdomain).

Does the controller action that you are posting the form contains any [Authorize] attribute. You need to post some code.

Verify a new session is really started every time. Check Trace output for the user's session id to ensure it realllly has changed.
Ensure the cookie getting sent over is actually getting set and sent over. (called ASPsessionIDSOMETHING ) and if that is being sent by the browser. Download the tool Fiddler to check the cookies easily (set cookie header coming from the server and the request cookies going back to the server from the browser. Make sure your browser is accepting the cookie and you dont say... have cookies turned off.
If your session id is changing at every request then your session isn't properly getting set the first time, set a break point on that code if you havent already.
You can log when the worker process resets - ensure that isn't the case. see http://www.microsoft.com/technet/prodtechnol/windowsserver2003/library/IIS/87892589-4eda-4003-b4ac-3879eac4bf48.mspx

I had the same problem. The problem only occured when a post request was send to the server and the session was not modified during that request. What I did as a workaround was, to write a custom filter which does nothing more than writing a key / value into the session on each request and added that filter to the GlobalFilter collection in the global.asax.
public class KeepSessionAlive : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext filterContext)
{
if(filterContext.HttpContext.Session != null)
{
filterContext.HttpContext.Session["HeartBeat"] = DateTime.Now.ToShortDateString();
}
}
public void OnActionExecuted(ActionExecutedContext filterContext) { }
}
And in the global.asax:
protected override void AddCustomGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new KeepSessionAlive());
}
This might be not the best solution but it helped me in my case.

Related

Using Unity Dependency Injection in Multi-User Web Application: Second User to Log In Causes First User To See Second User's Data

I'm trying to implement a web application using ASP.NET MVC and the Microsoft Unity DI framework. The application needs to support multiple user sessions at the same time, each of them with their own connection to a separate database (but all users using the same DbContext; the database schemas are identical, it's just the data that is different).
Upon a user's log-in, I register the necessary type mappings to the application's Unity container, using a session-based lifetime manager that I found in another question here.
My container is initialized like this:
// Global.asax.cs
public static UnityContainer CurrentUnityContainer { get; set; }
protected void Application_Start()
{
// ...other code...
CurrentUnityContainer = UnityConfig.Initialize();
// misc services - nothing data access related, apart from the fact that they all depend on IRepository<ClientContext>
UnityConfig.RegisterComponents(CurrentUnityContainer);
}
// UnityConfig.cs
public static UnityContainer Initialize()
{
UnityContainer container = new UnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
return container;
}
This is the code that's called upon logging in:
// UserController.cs
UnityConfig.RegisterUserDataAccess(MvcApplication.CurrentUnityContainer, UserData.Get(model.AzureUID).CurrentDatabase);
// UnityConfig.cs
public static void RegisterUserDataAccess(IUnityContainer container, string databaseName)
{
container.AddExtension(new DataAccessDependencies(databaseName));
}
// DataAccessDependencies.cs
public class DataAccessDependencies : UnityContainerExtension
{
private readonly string _databaseName;
public DataAccessDependencies(string databaseName)
{
_databaseName = databaseName;
}
protected override void Initialize()
{
IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();
Container.RegisterType<ClientContext>(new SessionLifetimeManager(), new InjectionConstructor(configurationBuilder.GetConnectionString(_databaseName)));
Container.RegisterType<IRepository<ClientContext>, RepositoryService<ClientContext>>(new SessionLifetimeManager());
}
}
// SessionLifetimeManager.cs
public class SessionLifetimeManager : LifetimeManager
{
private readonly string _key = Guid.NewGuid().ToString();
public override void RemoveValue(ILifetimeContainer container = null)
{
HttpContext.Current.Session.Remove(_key);
}
public override void SetValue(object newValue, ILifetimeContainer container = null)
{
HttpContext.Current.Session[_key] = newValue;
}
public override object GetValue(ILifetimeContainer container = null)
{
return HttpContext.Current.Session[_key];
}
protected override LifetimeManager OnCreateLifetimeManager()
{
return new SessionLifetimeManager();
}
}
This works fine as long as only one user is logged in at a time. The data is fetched properly, the dashboards work as expected, and everything's just peachy keen.
Then, as soon as a second user logs in, disaster strikes.
The last user to have prompted a call to RegisterUserDataAccess seems to always have "priority"; their data is displayed on the dashboard, and nothing else. Whether this is initiated by a log-in, or through a database access selection in my web application that calls the same method to re-route the user's connection to another database they have permission to access, the last one to draw always imposes their data on all other users of the web application. If I understand correctly, this is a problem the SessionLifetimeManager was supposed to solve - unfortunately, I really can't seem to get it to work.
I sincerely doubt that a simple and common use-case like this - multiple users logged into an MVC application who each are supposed to access their own, separate data - is beyond the abilities of Unity, so obviously, I must be doing something very wrong here. Having spent most of my day searching through depths of the internet I wasn't even sure truly existed, I must, unfortunately, now realize that I am at a total and utter loss here.
Has anyone dealt with this issue before? Has anyone dealt with this use-case before, and if yes, can anyone tell me how to change my approach to make this a little less headache-inducing? I am utterly desperate at this point and am considering rewriting my entire data access methodology just to make it work - not the healthiest mindset for clean and maintainable code.
Many thanks.
the issue seems to originate from your registration call, when registering the same type multiple times with unity, the last registration call wins, in this case, that will be data access object for whoever user logs-in last. Unity will take that as the default registration, and will create instances that have the connection to that user's database.
The SessionLifetimeManager is there to make sure you get only one instance of the objects you resolve under one session.
One option to solve this is to use named registration syntax to register the data-access types under a key that maps to the logged-in user (could be the database name), and on the resolve side, retrieve this user key, and use it resolve the corresponding data access implementation for the user
Thank you, Mohammed. Your answer has put me on the right track - I ended up finally solving this using a RepositoryFactory which is instantiated in an InjectionFactory during registration and returns a repository that always wraps around a ClientContext pointing to the currently logged on user's currently selected database.
// DataAccessDependencies.cs
protected override void Initialize()
{
IConfigurationBuilder configurationBuilder = Container.Resolve<IConfigurationBuilder>();
Container.RegisterType<IRepository<ClientContext>>(new InjectionFactory(c => {
ClientRepositoryFactory repositoryFactory = new ClientRepositoryFactory(configurationBuilder);
return repositoryFactory.GetRepository();
}));
}
// ClientRepositoryFactory.cs
public class ClientRepositoryFactory : IRepositoryFactory<RepositoryService<ClientContext>>
{
private readonly IConfigurationBuilder _configurationBuilder;
public ClientRepositoryFactory(IConfigurationBuilder configurationBuilder)
{
_configurationBuilder = configurationBuilder;
}
public RepositoryService<ClientContext> GetRepository()
{
var connectionString = _configurationBuilder.GetConnectionString(UserData.Current.CurrentPermission);
ClientContext ctx = new ClientContext(connectionString);
RepositoryService<ClientContext> repository = new RepositoryService<ClientContext>(ctx);
return repository;
}
}
// UserData.cs (multiton-singleton-hybrid)
public static UserData Current
{
get
{
var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
return Get(currentAADUID);
}
}
public static UserData Get(string AADUID)
{
UserData instance;
lock(_instances)
{
if(!_instances.TryGetValue(AADUID, out instance))
{
throw new UserDataNotInitializedException();
}
}
return instance;
}
public static UserData Current
{
get
{
var currentAADUID = (string)(HttpContext.Current.Session["currentAADUID"]);
return Get(currentAADUID);
}
}
public static UserData Get(string AADUID)
{
UserData instance;
lock(_instances)
{
if(!_instances.TryGetValue(AADUID, out instance))
{
throw new UserDataNotInitializedException();
}
}
return instance;
}

Remember Values Asp.Net

This is my controller code:
private string testVal;
public ActionResult Index()
{
testVal = "test";
return View();
}
public ActionResult NextView()
{
if (testVal == null)
Debug.WriteLine("testVal is null");
return View();
}
Is it possible to remeber values like testVal after changing page? It seems that when redirecting it resets values (testVal in NextVal is null).
Edit:
I try to save values to session but Session is null. I am using SignalR and when user is connected to page i use static event from hub to inform controller that user has connected - but inside method that runs on that event Session is unfortunetly null.
My controller code:
public ActionResult Index()
{
LoadingHub.userConnected += new EventHandler<IdEventArgs>(UserConnected);
return View();
}
private void UserConnected(object sender, IdEventArgs e)
{
Debug.WriteLine("User Connected with Id: " + e.Id);
if (Session == null)
Debug.WriteLine("Session is null");
}
My signalr hub:
public class LoadingHub : Hub
{
public static event EventHandler<IdEventArgs> userConnected;
//Function informs server that user has connected
public void Connected()
{
Debug.WriteLine("Hub Connected Method");
var id = Context.ConnectionId;
userConnected(this, new IdEventArgs(id));
}
}
Every time that you make a request a new instance of the controller is created so using a private field you will not be able to retain the value of this variable.
The easiest way for you to retain it it is to use a session. (if you want to retain this value per user base)
for example in your code
public ActionResult Index()
{
System.Web.HttpContext.Current.Session["testVal"] = "test";
return View();
}
public ActionResult NextView()
{
if (System.Web.HttpContext.Current.Session["testVal"] == null)
Debug.WriteLine("testVal is null");
return View();
}
you can use cookie or cache to replace the variable.
when you redirect to a webpage ,the controller will be newed ,so you cannot get the right testVal .but the cookie is stored in broswer .so you can set it and get .
You may use session or Pass the data to the controller
Have you looked into ASP.NET server side state management click here.
These are basically different ways to remember a value on the server once a new page has been loaded.
So a few server side techniques you could use to remember testVal are Session State or Application State. However Session State is more suitable for your scenario as it is only specific to the user's current session whereas Application State stores data that can be shared between sessions and would therefore be more ideal for global variables.
You can read the link I provided to read more on the differences though.
I would also like to warn you (as some say to use cookies), the user can delete or disable or manipulate them on the browser so this isn't an ideal solution.

Asp.Net MVC5 How to ensure that a cookie exists?

I'm new to MVC (5). In order to add localization support to my website I added a "Language" field to my ApplicationUser : IdentityUser
What's the best approach to now store this information in the browser and ensure that it gets re-created even if the user manually deletes it?
TL; but I've got time
What I've tried until now:
I started creating a cookie in my method private async Task SignInAsync(ApplicationUser user, bool isPersistent) but I notice that:
This method is not used if the user is already authenticated and automatically logs in using the .Aspnet.Applicationcookie and my language cookie could be meanwhile expired (or been deleted).
A user could manually delete the cookie, just for fun.
I thought about checking its existence in the controller (querying the logged user and getting it from the db) and it works but I'd need to do it in EVERY controller. I'm not sure is the correct way to do this.
Any suggestion about how to approach this problem and guarantee that the application has a valid "language cookie" on every request?
It sounds to me like what you want here is a Custom Action Filter. You can override the OnActionExecuting method which means the logic is run before any action is called
public class EnsureLanguagePreferenceAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var langCookie = filterContext.HttpContext.Request.Cookies["LanguagePref"];
if (langCookie == null)
{
// cookie doesn't exist, either pull preferred lang from user profile
// or just setup a cookie with the default language
langCookie = new HttpCookie("LanguagePref", "en-gb");
filterContext.HttpContext.Request.Cookies.Add(langCookie);
}
// do something with langCookie
base.OnActionExecuting(filterContext);
}
}
Then register your attribute globally so it just becomes the default behaviour on every controller action
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new EnsureLanguagePreferenceAttribute());
}
To me, the easiest way would be to create your own Authorize attribute (since your language options are tied to an authenticated user account). Inside of your new authorize attribute, simply perform the check if the cookie exists. If it does, then life is good. Else, query the user's database profile and reissue the cookie with the stored value
public class MyAuthorization : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//no point in cookie checking if they are not authorized
if(!base.AuthorizeCore(httpContext)) return false;
var cookie = httpContext.Request.Cookies["LanguageCookie"];
if (cookie == null) {
CreateNewCookieMethod();
}
return true;
}
}
To use, replace [Authorize] with [MyAuthorization] in your project.
If you don't want to mess with the [Authorize] attribute, you could create your own attribute that does the cookie checking and decorate your controller with that one as well.
One last alternative is to create your own Controller class that does the checking on the OnActionExecuting.
public class MyBaseController : Controller
{
public string Language {get;set;}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var cookie = filterContext.HttpContext.Request.Cookies["LanguageCookie"];
if(cookie == null){
cookie = CreateNewCookieMethod();
filterContext.HttpContext.Request.Cookies.Add(cookie);
}
Language = cookie.Value;
base.OnActionExecuting(filterContext);
}
How to use (note that we inherit from MybaseController now)
public class HomeController : MyBaseController{
public ActionResult Index(){
//Language comes from the base controller class
ViewBag.Language = Language;
Return View();
}
}
This method is neat because now that Language variable will be available in any controller that inherits from this new class.
Either of these will give you a single, cookie checking point. Additionally, you are only going back to the database only in the instance that the cookie does not exist.

Disable Session state per-request in ASP.Net MVC

I am creating an ActionResult in ASP.Net MVC to serve images. With Session state enabled, IIS will only handle one request at a time from the same user. (This is true not just in MVC.)
Therefore, on a page with multiple images calling back to this Action, only one image request can be handled at a time. It's synchronous.
I'd like this image Action to be asynchronous -- I'd like multiple image requests to each execute without needing the previous one to complete. (If the images were just static files, IIS would serve them up this way.)
So, I'd like to disable Session just for calls to that Action, or to specify that certain requests do not have Session state. Anyone know how this is done in MVC? Thanks!
If anyone is in the situation I was in, where your image controller actually needs read only access to the session, you can put the SessionState attribute on your controller
[SessionState(SessionStateBehavior.ReadOnly)]
See http://msdn.microsoft.com/en-us/library/system.web.mvc.sessionstateattribute.aspx for more info.
Thanks to https://stackoverflow.com/a/4235006/372926
Rather than implementing an action filter for this, why don't you implement a RouteHandler?
Here's the deal - IRouteHandler has one method - GetHttpHandler. When you make an ASP.Net MVC request to a controller, by default the routing engine handles the request by creating a new instance of MvcRouteHandler, which returns an MvcHandler. MvcHandler is an implementation of IHttpHandler which is marked with the (surprise!) IRequiresSessionState interface. This is why a normal request uses Session.
If you follow my blog post on how to implement a custom RouteHandler (instead of using MvcRouteHandler) for serving up images - you can skip returning a session-tagged IHttpHandler.
This should free IIS from imposing synchronicity on you. It would also likely be more performant because it's skipping all the layers of the MVC code dealing with filters.
I also came across the same problem and after doing R&D this link worked for me
Reference:
https://techatfingers.wordpress.com/2016/06/14/session-state-on-action/
Create custom Attribute
Override the “GetControllerSessionBehavior” method present in class DefaultControllerFactory.
Register it in global.aspx
1> Create custom Attribute
public sealed class ActionSessionStateAttribute : Attribute
{
public SessionStateBehavior SessionBehavior { get; private set; }
public ActionSessionStateAttribute(SessionStateBehavior sessionBehavior)
{
SessionBehavior = sessioBehavior;
}
}
2. Override
public class SessionControllerFactory : DefaultControllerFactory
{
protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return SessionStateBehavior.Default;
var actionName = requestContext.RouteData.Values["action"].ToString();
Type typeOfRequest=requestContext.HttpContext.Request.RequestType.ToLower() =="get"?typeof(HttpGetAttribute):typeof(HttpPostAttribute);
// [Line1]
var cntMethods = controllerType.GetMethods()
.Where(m =>
m.Name == actionName &&
( ( typeOfRequest == typeof(HttpPostAttribute) &&
m.CustomAttributes.Where(a => a.AttributeType == typeOfRequest).Count()>0
)
||
( typeOfRequest == typeof(HttpGetAttribute) &&
m.CustomAttributes.Where(a => a.AttributeType == typeof(HttpPostAttribute)).Count() == 0
)
)
);
MethodInfo actionMethodInfo = actionMethodInfo = cntMethods != null && cntMethods.Count() == 1 ? cntMethods.ElementAt(0):null;
if (actionMethodInfo != null)
{
var sessionStateAttr = actionMethodInfo.GetCustomAttributes(typeof(ActionSessionStateAttribute), false)
.OfType<ActionSessionStateAttribute>()
.FirstOrDefault();
if (sessionStateAttr != null)
{
return sessionStateAttr.Behavior;
}
}
return base.GetControllerSessionBehavior(requestContext, controllerType);
}
3. Register class in Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// --- other code ---
ControllerBuilder.Current.SetControllerFactory(typeof(SessionControllerFactory));
}
}
Try serving the images from another domain. So something like images.mysite.com.
This will provide you two benefits: One, sessions are tracked by a cookie, so images.mysite.com won't have the cookie. Two, it will give you an additional two concurrent requests to retrieve images.
Have you considered setting up a HttpHandler to serve up your images?
SessionState attribute is quite helpful if u use mvc3. How to achieve this with mvc2 needs a little more coding.
Idea is to tell the asp.net that specific request wont use session object.
So, Create a custom route handler for specific requests
public class CustomRouteHandler : IRouteHandler
{
public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.HttpContext.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.ReadOnly);
return new MvcHandler(requestContext);
}
}
SessionStateBehavior enum has 4 members, you should use "disabled" or "readonly" modes to get async behavior.
After creating this custom route handler, be sure that your specific requests goes through this handler. This can be done via defining new routes at Global.asax
routes.Add("Default", new Route(
"{controller}/{action}",
new RouteValueDictionary(new { controller = "Home", action = "Index"}),
new CustomRouteHandler()
));
Adding this route makes all your requests to be handled by your custom route handler class. You can make it specific by defining different routes.
Change DefaultCOntrollerFactory to custom ControllerFactory class. Default Controller.TempDataProvider use SessionStateTempDataProvider. you can change it.
1.Set web.config/system.web/sessionState:mode="Off".
2.create DictionaryTempDataProvider class.
public class DictionaryTempDataProvider : ITempDataProvider
{
public IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
{
return new Dictionary<string, object>();
}
public void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
}
}
3.Create DictionaryTempDataControllerFactory
public class DictionaryTempDataControllerFactory : DefaultControllerFactory
{
public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
var controller = base.CreateController(requestContext, controllerName) as Controller;
if (controller!=null)
controller.TempDataProvider = new DictionaryTempDataProvider();
return controller;
}
}
4.In global.asax.cs Apprication_Start event set DictionaryTempDataControllerFactory.
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(
new DictionaryTempDataControllerFactory()
);
}
On our server, IIS doesn't even know about sessions - it's the ASP.NET stack that handles one request per session at a time. Static files, like images, are never affected.
Is it possible that your ASP.NET app is serving the files instead of IIS?
Create new Controller
Decorate controler with [SessionState(SessionStateBehavior.Disabled)]
Refactor code you want seesion stated disabled for to that controller
I would to share my solution for disable ASP.NET Session for an specific request (in my case, a WCF Service) using an HttpModule:
public class AspNetSessionFilterModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PostMapRequestHandler += OnPostMapRequestHandler;
}
private void OnPostMapRequestHandler(object sender, EventArgs e)
{
var context = (sender as HttpApplication).Context;
DisableSessionForSomeRequests(context);
}
private void DisableSessionForSomeRequests(HttpContext context)
{
if ("~/Services/MyService.svc".Equals(context.Request.AppRelativeCurrentExecutionFilePath, StringComparison.InvariantCultureIgnoreCase))
{
context.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Disabled);
}
}
public void Dispose()
{ }
}

NHibernate crashes when being called from a web user control in ASP.Net

I have a very strange problem: NHibernate crashes when being called from a web user control.
I am working on a ASP.Net (2.0) web page which uses NHibernate to access a database.
And I have a simple factory class to access a column CurrentStepNumber in the table ProjectInfo:
public class ProjectEntity
{
private int? _currentStepNumber;
public virtual int? CurrentStepNumber
{
get { return _currentStepNumber; }
set { _currentStepNumber = value; }
}
public static ProjectWizardEntity GetById(int id, bool shouldLock)
{
return RepositoryFactory.ProjectWizardRepository.GetById(id, shouldLock);
}
public static ProjectWizardEntity GetById(int id)
{
return GetById(id, false);
}
public virtual void Save()
{
RepositoryFactory.ProjectWizardRepository.Save(this);
}
public virtual void SaveOrUpdate()
{
RepositoryFactory.ProjectWizardRepository.SaveOrUpdate(this);
}
}
This class is accessed via a proxy class, so that everytime a new value is assigned it is flushed to the database:
public class DBHelper
{
ProjectEntity _projectEntity;
ProjectEntity GetProjectEntity()
{
if (_projectEntity == null)
_projectEntity = //get or create a new one;
return _projectEntity ;
}
public int? CurrentStepNumber
{
get
{
return (CurrentProjectId > 0) ? CurrentProjectWizardEntity.CurrentStepNumber : 0;
}
set
{
if (CurrentProjectId > 0)
{
CurrentProjectWizardEntity.CurrentStepNumber = value;
CurrentProjectWizardEntity.SaveOrUpdate();
}
}
}
}
Now the problem:
When I access CurrentStepNumber from the test.aspx page, everything works perfectly
When I read this field from the web user control (test.ascx) which is used on test.aspx page it is still OK
However when I try to assign a value to CurrentStepNumber in the code behind the control (test.ascx) I always get an exception:
NHibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session
Actually SaveOrUpdate method of the NHibernate Repository throws the exception.
I could not figure out what could be the problem here, so any help will be appreciated.
I think that You have a session management problem here. How is RepositoryFactory.ProjectWizardRepository.GetById creating and possibly disposing the NHibernate session? Does it create and then close a session?
It seems that DBHelper.GetProjectEntity() is creating or loading a ProjectEntity. Later, when CurrentStepNumber's setter i called, You insert or update the object to the database.
The problem is that when DBHelper.GetProjectEntity() is loading an existing object and later closing the session after it has been loaded but keep the object around, we are entering deep water. When the object is later updated, You set a new value for CurrentStepNumber and send the object to NHibernate to save. The problem here is that the object is not associated with the new session that is created and closed during saving. Nhibernate then gets confused since a new object is discovered that is not loaded from current session but it has an id of an existing object.
For a solution, Google for "NHibernate asp.net session management" and You will get a number of good hits about how to use the ASP.NET request cycle as a unit of work.

Resources