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.
Related
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;
}
}
I'm trying to figure out the best way to manage the DbContext. I've seen code samples that don't dispose and I've seen people say that that is a bad idea. Is it appropriate for me to do something like below? Also, should I put every transaction, including reads, in a new DbContext? This might be another question, but is the part about the EntityState necessary?
public abstract class GenericRepository<T> where T : EntityData
{
protected MyDbContext Context
{
get { return new MyDbContext(); }
}
public T Save(T obj)
{
T item;
using (var context = Context)
{
var set = context.Set<T>();
if (String.IsNullOrEmpty(obj.Id))
item = set.Add(obj);
else
{
item = set.Find(obj.Id);
item = obj;
}
// taken from another code sample
var entry = context.Entry(item);
if (entry.State == EntityState.Detached)
{
//Need to set modified so any detached entities are updated
// otherwise they won't be sent across to the db.
// Since it would've been outside the context, change tracking
//wouldn't have occurred anyways so we have no idea about its state - save it!
set.Attach(item);
context.Entry(item).State = EntityState.Modified;
}
context.SaveChanges();
}
return item;
}
}
EDIT
I also have an extended class that implements this function below. The context is not being wrapped in a using statement in this query, so I'm a little suspicious of my code.
public IQueryable<T> FindByAccountId(string accountId)
{
return from item in Context.Set<T>()
let user = UserRepository.FindByAccountId(accountId).FirstOrDefault()
where item.UserId == user.Id
select item;
}
Contexts should really be on a per request basis. The request comes in and a new context is created. This context is used for the remainder of the request then disposed of at the end of the request accordingly. This gives you the benefit of request long transactions, and as highlighted by HamidP, you also have the added benefit of cached entities; meaning that any entities loaded into the context can be loaded by retrieved without Entity Framework needing to query the database.
If you're using any kind of inversion of control container such as StructureMap then you can easily create HTTP request bound contexts by a configuration such as:
this.For<DbContext>().HybridHttpOrThreadLocalScoped().Use<DbContext>();
You're then able to inject your DbContext (or a derivative of it) into your repository and leave your IOC container of choice to dispose of the context at the end of the request. If you were to inject the same context into another repository then you'd receive the same instance of the context.
I hope this helps!
No, it should not
Best approach here is to assign a context just for a request. you should attach a context to an incoming request and dispose your context when request is finished. In this approach you save the overhead of creating a context for every transaction and also benefit from caching mechanism of context because each context has it's inside cache and a request may access the data it had access recently.
Creating a context for each transaction is not as bad as having a long life context!! Don't ever do that, long life contexts result in many concurrency issue and the cache becomes stale and memory consumption grows high and higher and you should maintain your application in future by miracles.
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 WCF which calls EF functions.
When I invoke the method from the client to insert a user nothing happens.
This is the method I am invoking :
public void insertData(Users pUser)
{
using (var context = new AMTEntitiesContainer())
{
var User = context.Users.Add(pUser);
context.SaveChanges();
}
}
Is there anything wrong in this code?
The only thing that I see wrong is var User is not needed. Simply use context.Users.Add(pUser); and save the changes.
The other possibility is that AMTEntitiesContainer is not a proper DbContext. Without seeing your AMTEntitiesContainer class it would be hard to figure that out though.
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