I am developing an ASP.NET MVC project, and I would like to lazy-load a database DataContext object that is used across Classes within a server Request (that is, it only live through a Request, and for each Request, I have an unique object).
Currently I can obtain the Request using HttpContext.Current or HttpContext.Current.Request, but the Request only stores string values.
Is there something like the Session object, but for the Request only? Something like the ViewBag or ViewData, but accessible from the HttpContext object?
EDIT: Here is what I am trying to achieve: I have a AccountBusiness class, that might be instantiated multiple times in a request. It has LoggedInUser property (note: this is not the AspNetUser object of ASP.NET Identity) that is lazy-loaded. Normally I would do something like this:
private UserProfile loggedInProfile;
public UserProfile LoggedInProfile
{
get
{
if (this.loggedInProfile == null)
{
var userId = HttpContext.Current.User.Identity.GetUserId();
this.loggedInProfile = this.Context.UserProfiles.FirstOrDefault(q => q.ID == userId);
}
return this.loggedInProfile;
}
}
However, as I stated before, this property might be instantiated multiple times in a request, it will access the database multiple time for just a same UserProfile object. This is just an example, I have much more objects similar to this, and would like to change so that it only access the database once, then save it for using for the current Request, like this:
public UserProfile LoggedInProfile
{
get
{
if (this.loggedInProfile == null)
{
var dataTokens = HttpContext.Current.Request.RequestContext.RouteData.DataTokens;
object o;
if (!dataTokens.TryGetValue("CurrentUserProfile", out o))
{
var userId = HttpContext.Current.User.Identity.GetUserId();
dataTokens["CurrentUserProfile"] = o = this.Context.UserProfiles
.FirstOrDefault(q => q.ID == userId);
}
this.loggedInProfile = o as UserProfile;
}
return this.loggedInProfile;
}
}
Is there any bad thing in my solution?
P.s: I just discover RouteValues and DataTokens may contain a key-object pair, but I wonder if it is okay to use it?
Use the HttpContext.Items collection to share objects during the lifetime of the request.
I would like to lazy-load a database DataContext object that is used across the Request only.
That bit troubles me. Perhaps it's just worded awkwardly, but it sounds as if you're somehow trying to persist your context across requests, which would be a very bad idea.
If, however, you're just talking about persisting the results of a query or similar, I'd recommend using the in-memory caching provided by ASP.NET. Using session or cookies involves relying on the client to store information, and that's both unnecessary and inappropriate for this kind of thing.
You can use Cache or Cookie both are accessible with HttpContext and after using you can make empty them.
Use dependency injection and Inversion of Control. Most IOC tools, like Castle Windsor, will allow you to have your context persisted for the life of a request.
If you are not familiar with DI, here is a simple example:
Mycontroller
{
IMyContext _contex;
public Mycontroller(IMyContext context)
{
_context = context;
}
}
Related
To keep this question simple, I'll describe the higher level problem and then go into any implementation details if needed.
I use the ASP.NET Identity in my application under development. In a specific scenario on a series of requests, the UserManager first get the current user(at least one FindById request), where the user is fetched. On a subsequent request, I update information on this user that is saved by UserManager.Update and I can see the change persisted in the database.
The problem is here that on further subsequent requests, the user object gotten from FindById is not updated. That is strange, but could be something about caching in UserManager I do not understand. However, when I trace the database calls, I see that UserManager indeed is sending the sql-requests to the database for getting the user.
And this is where it gets really strange - even though the database is confirmed to be up to date, UserManager still somehow returns an old object from this process. When I myself run exactly the same query traced directly to the database, I get updated data as expected.
What is this black magic?
Obviously, something is cached somewhere, but why does it make a query to the database, just to disregard the updated data it gets?
Example
This below example updates everything as expected in the db for each request to the controller action, and when GetUserDummyTestClass is calling findById on the other instance of UserManager I can trace the sql requests, and can test these directly to the db and verify that they return updated data. However, the user object returned from that very same line of code still has the old values (in this scenario, the first edit after the application was started, regardless of how many time the Test action is invoked).
Controller
public ActionResult Test()
{
var userId = User.Identity.GetUserId();
var user = UserManager.FindById(userId);
user.RealName = "name - " + DateTime.Now.ToString("mm:ss:fff");
UserManager.Update(user);
return View((object)userId);
}
Test.cshtml
#model string
#{
var user = GetUserDummyTestClass.GetUser(Model);
}
#user.RealName;
GetUserDummyTestClass
public class GetUserDummyTestClass
{
private static UserManager<ApplicationUser> _userManager;
private static UserManager<ApplicationUser> UserManager
{
get { return _userManager ?? (_userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()))); }
}
public static ApplicationUser GetUser(string id)
{
var user = UserManager.FindById(id);
return user;
}
}
Update
As Erik pointed out, I should not use static UserManagers. However, if I keep the UserManager in GetUserDummyTest bound to the HttpContext (persisting it per HttpRequest) in case I want to use it several times during a request, it is still caching the first User object it gets by a Id, and disregarding any updates from another UserManager. Thus suggesting that the real issue is indeed that I'm using two different UserManagers as trailmax suggested, and that it's not designed for this kind of usage.
In my example above, if I keep the UserManager in GetUserDummyTestClass persistent over the HttpRequest, add a Update-method and only use this in the controller, everything works fine as expected.
So if going to a conclusion, would it be correct to state that if I want to use logic from a UserManager outside of the scope of the controller, I have to globalize the UserManager instance in an appropriate class where I can bind the instance to the HttpContext, if I want to avoid creating and disposing instances for one-time usage?
Update 2
Investigating a little further, I realized that I am indeed intended to use one instance per request, and that this already actually is set up for the OwinContext in Startup.Auth and later accessed like this:
using Microsoft.AspNet.Identity.Owin;
// Controller
HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>()
// Other scopes
HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>()
This is actually embarrassingly obvious looking at the setup of the default AccountController provided, but I guess the rather strange and unexpected behavior described above proved quite distracting. Still, it would be interesting to understand the reason for this behavior, even though it will not be a problem anymore using OwinContext.GetUserManager.
Your problem is that you're using two different UserManager instances, and it looks like they're both statically defined (which is a huge no-no in Web applications, since these are shared between all threads and users of the system and are not thread safe, you can't even make them thread safe by locking around them because they contain state specific to a user)
Change your GetUserDummyTestClass to this:
private static UserManager<ApplicationUser> UserManager
{
get { return new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(new ApplicationDbContext())); }
}
public static ApplicationUser GetUser(string id)
{
using (var userManager = UserManager)
{
return UserManager.FindById(id);
}
}
I have a method in my generic repository:
public IQueryable<T> Query<T>() where T : class, IEntity
{
return _context.Set<T>();
}
This is method for getting user:
public User GetUser(string email)
{
return _repository.Query<User>().FirstOrDefault(u => u.Email == email);
}
Finally, I put the user to session:
AppSession.CurrentUser = UserService.GetUser(email);
In my action I need to get the current user and get collection of objects Notifications (one-to-many):
AppSession.CurrentUser.Notifications.OfType<EmailNotification>().FirstOrDefault();
But, here I get the error:
The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
I know that Notifications not loaded when I getting User from DB.
How to say EF to load Notifications objects? I know about Include, but I cannot use it in GetUser method.
When the first HttpRequest ends after looking up your CurrentUser object, your _repository reference that the CurrentUser is expecting for additional lookups like EmailNotifications isn't available.
The exception is thrown because CurrentUser doesn't have the original object context, so you either have to attach the CurrentUser object to the new objectContext that your _repository is using, or use the easier solution of simply reloading the user through the new context that was created for your current request in the repository.
Before attempting to find the notifications in your action, add the following line:
AppSession.CurrentUser = UserService.GetUser(AppSession.CurrentUser.Email);
AppSession.CurrentUser.Notifications.OfType<EmailNotification>().FirstOrDefault();
As #Ryan said it is due to the fact that the object context is not available to lazy load in the associated notifications.
What I'd suggest is turn off lazy loading (if possible) as can cause lots of issues later and then do something like ...
var user = UserService.GetUser(AppSession.CurrentUser.Email);
user.Notifications = NotificationService.GetUserNotifications(user.Id /* or another identifier */);
AppSession.CurrentUser = user;
To do this you will require a new NotificationService, this can load (as suggested above) but also handle the execution of notifications (sending emails etc).
You should now have the notifications for that user in your Application session cache.
HTH
I have seen many posts of how to setup a session per request in Asp.Net MVC by using ActionFilter or by a DI package to inject the session into the controller. What I wanted to know was, will it be a bad idea/pattern to just make an extension method like :
public static ISession GetNHibernateSession(this Controller controller)
{
return SessionFactory.OpenSession();
}
so that the session can be instantiated when required like :
public ActionResult DoSomething()
{
using( var session = this.GetNHibernateSession())
{
// Do something with the session
}
}
reasons why this may be a good/bad idea will be greatly appreciated
Good:
It's simple
It just works
Bad:
You are doing session management, even if it's just a three lines, all over your code
With an extension method, you can't replace the behavior for testing
In short, for small, RAD and proof-of-concept projects, your idea will work just fine. For more complex development, it's probably better to extract session management from the controllers, at least moving it to a base class.
Is this a nice way to use the LINQ context during one http request? In almost every request i have some selects from the database and some inserts/updates. It seams to work but I dont know how this will work with heavy traffic to the servers and on load balanced servers, anyone have any opinions/ideas about this way to keep the Context during the entire lifespan of the Request?
public static AccountingDataContext Accounting
{
get
{
if (!HttpContext.Current.Items.Contains("AccountingDataContext"))
{
HttpContext.Current.Items.Add("AccountingDataContext", new AccountingDataContext(ConfigurationManager.ConnectionStrings["SQLServer.Accounting"].ConnectionString));
}
return HttpContext.Current.Items["AccountingDataContext"] as AccountingDataContext;
}
}
This is a generally good idea on some levels. But you probably want to push instantiation back from the Begin_Request event. With the integrated pipeline, you will be initializing a rather expensive DB Context for every single request to your site. Including favicon.ico, all your stylesheets and all your images.
Best, simple implementation of something that only instantiates it when something asks for the context is Ayende's example for NHibernate's ISession; you can just replace it with the appropriate bits to instantiate your L2S context.
I'm using Unity for dependency injection, but the idea is the same:
protected void Application_BeginRequest() {
var childContainer = this.Container.CreateChildContainer();
HttpContext.Current.Items["container"] = childContainer;
this.ControllerFactory.RegisterTypes(childContainer);
}
protected void Application_EndRequest() {
var container = HttpContext.Current.Items["container"] as IUnityContainer;
if (container != null) {
container.Dispose();
}
}
The container is responsible for setting up a number of things, one of which is the data context. Works like a charm. I haven't done load balancing, but can't imagine you'd run into issues there either. The request gets its own context, which is wrapping a single user connecting to a database. No different that using old school ADO .NET for data access.
Here is my scenario:
I'm using nhibernate, openning and closing the session in an IHttpModule (PreRequestHandlerExecute and PostRequestHandlerExecute).
Ninject takes care of injecting my session in all my repositories and I'm very happy with it.
Now suppose an Update to one of my entities (code simplified):
Controller:
User user = _userService.GetUser(id);
user.Name = "foo";
user.Email = "foo#bar.com";
user.Group = _groupService.GetGroup(idGroup);
if(_userService.Edit(user)) {
RedirectToAction("Index");
}
else {
return View(user);
}
Service:
if(ValidateUser(user) {
return _rep.Update(user);
}
return false;
ValidateUser is doing the validation logic and inserting any error in a IValidationDictionary that is a wrapper for the ModelState in the controller.
So far so good, I get all errors (if any) and I can show them in the view.
Here comes the problem:
When I try to save an user with an error (no name for instance), the method _rep.Update(user) is never called, but the user gets saved anyway.
Googling around it come to my knowledge the nhibernate AutoDirtyCheck, witch means that if I change an entity in memory, it will be persisted automatically in the database.
Very powerful feature I agree, but since my session is commited in PostRequestHandlerExecute, my invalid entity is saved anyway, something that I don't want.
I tried to remove this behavior using unhaddins, it worked, but then my child objects are not automatically saved when I save only the parent :(
So how to solve that?
Make ValidadeUser public and validade a copy before calling the _userService.GetUser(id)?
Put the validation logic elsewhere? Maybe in the entity class itself? (I like it so much separated!).
Thanks a lot in advance.
FYI - you can set the Nhibernate session FlushMode property to FlushMode.Never to be totally in control of when NHibernate will flush updates to the database. It might be that you could cheat and if an action is not authorized - never do a flush and the nhibernate session will die when the response is over (if the session didnt go away you really should evict the modified object, tho)
Personally, I call my validation code in mvc in the defaultmodelbinder. My viewmodel (posted data) is validated before I have done anything with it. I use one validator class for each validation concern.
public class MyController : Controller
{
public ActionResult MyActionMethod(UserChangeModel model)
{
if (!ModelState.IsValid)
{
return RedirectToAction("Index");
}
User user = _userService.GetUser(model.Id);
user.Name = model.Name;
user.Email = model.Email;
user.Group = _groupService.GetGroup(model.IdGroup);
return View(user);
}
}
public class MyDefaultModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var boundInstance = base.BindModel(controllerContext, bindingContext);
if (boundInstance != null)
{
var validator = findValidator(bindingContext.ModelType);
var errors = validator.Validate(boundinstance);
addErrorsToTheModelState(bindingContext, errors);
}
}
}
I haven't tried this myself but the first thing that comes to mind is detaching the invalid user object. If you don't doing another query that would retrieve the same user would also return the same, now invalid, object.
Fabio Maulo has a very good writeup/solution
http://fabiomaulo.blogspot.com/2009/03/ensuring-updates-on-flush.html
You can use ISession.Evict(obj) to remove the object from the session and that will prevent it from being automatically persisted. One thing to note is that this makes the object transient and attempting to load any lazy initialized child objects (typically collections) will cause NH to throw a LazyInitializationException.
ETA: I just read your comment to Maurice that you can't directly access the ISession. I manually inject the ISession into repository/service classes because it's needed for WinForms. There are several methods in ISession that I've had to access from time-to-time, particularly Evict, Merge, and Lock. I would expose the ISession or a wrapper so that you can use Evict.