I've implemented a base controller for my MVC 3 project to allow a common way of accessing a user entity from my db context :
public abstract class MyBaseController : Controller
{
protected DBEntitiesContainer db;
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
this.db = new DBEntitiesContainer();
}
public UserDetails GetActiveUserDetails()
{
UserDetails userDetails = GetObjects.GetActiveUserDetails(this.db);
return userDetails;
}
}
This works great and all my controllers have access to this.db.MyEntity and can retrieve a UserDetails object by calling this.GetActiveUserDetails()
However, the problem arises when I try to perform an update on the entity thus :
public class UpdateController : MyBaseController
{
public ActionResult Index()
{
UserDetails userDetails = this.GetActiveUserDetails();
userDetails.LastOnline = DateTime.Now;
UpdateModel(userDetails);
this.db.SaveChanges();
}
}
Any ideas why the UserDetails object is easily retrieved, but when I check my database after calling SaveChanges(), nothing has been updated ? I'm presuming I'm accessing my context in two different ways, but I can't see how I am...!
Edit: Here's the GetObjects.GetActiveUserDetails() method
public static UserDetails GetActiveUserDetails(DBEntitiesContainer db)
{
MembershipUser membershipUser = Membership.GetUser();
UserDetails userDetails;
try
{
if (membershipUser != null)
{
userDetails = (from u in db.UserDetails
where (u.UserId == (System.Guid)membershipUser.ProviderUserKey)
select u).First();
}
else
{
return GetGuestAccount();
}
}
catch
{
return GetGuestAccount();
}
return userDetails;
}
Not the cleanest method I know...
UpdateModel is helper method for Controller base class. it supports updating the properties of an object we pass it using the incoming form parameters during HttpPost action method.
It uses relfection to find out the property names of the object (Model object what we passed) and then automatically converts the assigns values to them based on the input values submitted by the form ( client form).
In your case you when u use update model it has no input value find associated model and it make the model default values as it is in database.
try to comment the updatemodel line and runt he code... it must work.
It's not a good idea to setup data access this way. One reason is that you should dispose of the db context after you use it. This means that using your method, this leaves the database connection open until garbage collection occurs, which could be minutes or hours later. As other web requests come in, new database connections are created, and again those are not disposed of either.. etc.. etc.. it's a pseudo-memory leak (not a true memory leak because it will eventually get collected, but it means resources are being used well after they are needed)
Related
When i call my admin controller- Index Action method will get all the user details
when i want select particular user again i dont want to hit the DB.
both action method same controller and i'm using model popup for display details.
My Question
I dont want to use entity framework.
- when admin form load i will get all the user details this is Index Action Method
-based on user id i need to display particular user so again i dont want hit to the DB already i'm having all the user details. that details how to get another action method?
i can remember asp.net i used session to share the data globally. like that asp.net mvc is possible? please help me.
Thanks
It looks you're looking for a cache mechanism. For simple scenarios, I use a simple static variable, but I keep it in a separated class. Let's suppose you have a User class like this:
public class User
{
public string Id { get; set; }
public string Name { get; set; }
}
You could create a class like this:
public static class UserCacheService
{
private static IEnumerable<User> _users;
private static readonly object lockObj = new object();
public static IEnumerable<User> GetUsers()
{
lock (lockObj)
{
if (_users == null)
{
using (var db = new MyNiceDbContext())
{
_users = db.Users.ToList();
}
}
return _users;
}
}
public static void InvalidateCache()
{
lock (lockObj)
{
_users = null;
}
}
}
Then you can get your shared users in any action, of any controller like this:
public class AdminController : Controller
{
public ActionResult Index()
{
// the first time, it'll need to get users from DB (e.g with Entity Framework)
var users = UserCacheService.GetUsers();
return View();
}
}
The first time, the _users in your UserCacheService will be null, and as expected, it'll need to load users from database. However, the next time it won't, no matter if you are using another controller:
public class AnotherController : Controller
{
public ActionResult Index(string userId)
{
// now, it won't load from DB anymore, because _users is already populated...
var users = UserCacheService.GetUsers();
var currentUser = users.Where(u => u.Id == userId).FirstOrDefault();
if (currentUser != null)
{
// do something with the user...
}
return View();
}
}
There are times when unfortunately your _users will become null again, for example when you restart your ApplicationPool in IIS, but UserCacheService is already prepared for fetching database once if that's the case.
Be careful about three things:
Whenever you keep data in memory (like _users), you are consuming
your server's memory, which might be limited. Don't start trying to
keep everything in memory, only data you know you'll need everytime.
Whenever you update something in your users, like a name, an address or something else, since the _users will not get from database everytime, you need to call the UserCacheService.InvalidateCache() method, in order to force the next call to load again from database, thus making sure you have _users up to date.
This only works for simplistic scenarios. If you have your application distributed in two or more servers, this won't work, as each server has it's own memory and they can't share it out of the box. That's when you would look forward for something like Redis. Though, I don't think it's your case here.
I am using MVC3, ASP.NET4.5, SQL Server 2008.
I have a "User" class which contains details about the current user ie Organisation details.
At present I instantiate a new user object on each view or action where needed which makes calls to the db, and of course this is wasteful, as I could instantiate this object when the request is first made. I could then reuse the object throughout the request processing, to include action and view processing.
What would be the correct approach to do this. I had thought of loading the object into a session, but I am trying to avoid the use of these. Also I had an idea of using a static class that checks for the present of the instantiated object, and creates it is absent, then gets the relevant properties. Here is some possible code to illustrate my thinking:
public static class stCurrentUser
{
public static CurrentUser GetUserDetails()
{
CurrentUser myUser = (CurrentUser)System.Web.HttpContext.Current.Session["User"];
if (myUser == null)
{
myUser = new CurrentUser();
System.Web.HttpContext.Current.Session["User"] = myUser;
return myUser;
}
else
{
return myUser;
}
}
}
Thanks
Looking for ideas to guard against malicious data changes: userA manipulating (editing or deleting) data that belongs to userB. Since we are creating entities on the client, we need to assign them (or at least some of them) to the authenticated user.
For example:
var newItem = ds.createNewItem();
newItem.OwnerId(22); //this is the problem that I see.
newItem.Name("New Item");
newItem.Description("I just changed your item!");
... //and so on
ds.saveChanges();
Assuming we know the identity of the user calling SaveChanges on our API, how do we validate our entities (new or modified) against this user?
The first thought that comes to mind is to subclass EFContextProvider, override BeforeSaveEntity and examine the entities OwnerId property against the identity of our user. For example:
if (entityInfo.Entity.GetType() == typeof(Item)
&& (entityInfo.EntityState == EntityState.Added
|| entityInfo.EntityState == EntityState.Modified)
&& ((Item)entityInfo.Entity).OwnerId != _currentUserId) {
return false
... //and so on
If using this approach, does it make sense to establish _currentUserId in the constructor of our new EFContextProvider class?
An ideas or perhaps a better way to approach this problem?
I think you are on the right track. I've been noodling this myself and have gone down much the same path.
Let's assume you've handled authentication and there's an IPrincipal available. You've got yourself a custom IIdentity too (call it AppIdentity) where you can stash the UserId for the authenticated user.
The Web Api's base ApiController class makes the ambient IPrincipal available via its User property. We will leverage that in your custom Breeze Web Api controller which might begin like this:
[Authorize]
[JsonFormatter, ODataActionFilter]
public class BreezeApiController : ApiController
{
private readonly AppContextProvider _context;
public BreezeApiController() {
// pass 'User' IPrincipal to the context ctor
_context = new AppContextProvider(User);
}
...
// one of the Query action methods
[HttpGet]
public IQueryable<Foo> Foos() {
return _context.Foos
}
...
Your custom EFContextProvider might begin like this:
public class AppContextProvider : EFContextProvider<AppDbContext>
{
public AppContextProvider(IPrincipal user)
{
UserId = ((AppIdentity) user.Identity).UserId;
}
public int UserId { get; private set; }
...
Now you probably want to prevent UserB's entities from being seen by UserA. So instead of allowing every Foo to go out the door, your custom EFContextProvider could filter accordingly.
public DbQuery Foos
{
get
{
// Here the 'Context' is your EF DbContext
return (DbQuery) Context.Foos
.Where(f => f.UserId == UserId);
}
}
Looking back at the controller, we see that its Foos GET action method is oblivious to the filter ... as it should be. We want our controllers to be light and move the business logic to the custom EFContextProvider and its helpers.
Finally, a highly simplified, general purpose BeforeSaveEntity could look like this:
private bool BeforeSaveEntity(EntityInfo info)
{
var entity = info.Entity;
if (info.EntityState == EntityState.Added)
{
entity.UserId = UserId;
return true;
}
return UserId == entity.UserId || throwCannotSaveEntityForThisUser();
}
...
private bool throwCannotSaveEntityForThisUser()
{
throw new SecurityException("Unauthorized user");
}
Notice that the custom context provider on the server is responsible for setting the UserId of added entities. We wouldn't trust the client to do that anyway. And of course it is responsible for verifying the UserId of modified and deleted entities.
Hope this helps. Remember, this is only a sketch. The real deal would have greater sophistication and be refactored into helpers.
On my controller I have it inherit a MainController and there I override the Initialize and the OnActionExecuting.
Here I see what is the URL and by that I can check what Client is it, but I learned that for every Method called, this is fired up again and again, even a simple redirectToAction will fire the Initialization of the same controller.
Is there a better technique to avoid this repetition of database call? I'm using Entity Framework, so it will take no time to call the DB as it has the result in cache already, but ... just to know if there is a better technique now in MVC3 rather that host the variables in a Session Variable
sample code
public class MyController : MainController
{
public ActionResult Index()
{
return View();
}
}
public class MainController : Controller
{
public OS_Clients currentClient { get; set; }
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
// get URL Info
string url = requestContext.HttpContext.Request.Url.AbsoluteUri;
string action = requestContext.RouteData.GetRequiredString("action");
string controller = requestContext.RouteData.GetRequiredString("controller");
object _clientUrl = requestContext.RouteData.Values["cliurl"];
if (_clientUrl != null && _clientUrl.ToString() != "none")
{
// Fill up variables
this.currrentClient = db.FindClientById(_clientUrl.ToString());
}
base.Initialize(requestContext);
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// based on client and other variables, redirect to Disable or Login Actions
// ... more code here like:
// filterContext.Result = RedirectToAction("Login", "My");
base.OnActionExecuting(filterContext);
}
}
is it still best to do as:
public OS_Clients currentClient {
get {
OS_Clients _currentClient = null;
if (Session["CurrentClient"] != null)
_currentClient = (OS_Clients)Session["CurrentClient"];
return _currentClient;
}
set {
Session["CurrentClient"] = value;
}
}
It seems that you dealing with application security in that case I would suggest to create Authorization filter, which comes much early into the action. You can put your permission checking code over there and the framework will automatically redirect the user to login page if the permission does not meet AuthorizeCore.
Next, if the user has permission you can use the HttpContext.Items as a request level cache. And then you can create another ActionFilter and in action executing or you can use the base controller to get the user from the Httpcontext.items and assign it to controller property.
If you are using asp.net mvc 3 then you can use the GlobalFilters to register the above mentioned filters instead of decorating each controller.
Hope that helps.
In your base controller, you need to cache the result of the first call in a Session variable.
This makes sure the back-end (DB) is not called unnecessarily, and that the data is bound to the user's Session instead of shared across users, as would be the case with the Application Cache.
I'm having trouble trying to think what the best way is to recreate a database object in a controller Action.
I want to make use of ModelBinders so in my action I have access to the object via a parameter, rather than having to repeat code to get an object from the database based on an identifier parameter. So I was thinking of having a ModelBinder that performs a call to the dataaccess layer to obtain the original object (or creates a new one if it doesn't exist in the database), then binds any properties to the database object to update it. However I've read that the ModelBinders shouldn't make database queries (first comment of this article).
If the ModelBinder shouldn't perform a database query (so just using the DefaultModelBinder) then what about database objects that have properties that are other db objects? These would never get assigned.
Saving an object after the user has edited it (1 or 2 properties are editable in the view) the ModelBinded object would be missing data, so saving it as it is would result in data in the database being overwritten with invalid values, or NOT-NULL constraints failing.
So, whats the best way to get an object in a controller action from the database bound with the form data posted back from the view?
Note im using NHibernate.
I get the model object from the database, then use UpdateModel (or TryUpdateModel) on the object to update values from the form parameters.
public ActionResult Update( int id )
{
DataContext dc = new DataContext();
MyModel model = dc.MyModels.Where( m => m.ID == id ).SingleOrDefault();
string[] whitelist = new string[] { "Name", "Property1", "Property2" };
if (!TryUpdateModel( model, whitelist )) {
... model error handling...
return View("Edit");
}
ViewData.Model = model;
return View("Show");
}
Unfortunately you don't have control over the construction of the model binder, so you can't inject any repository implementation.
You can reach out directly into a service locator to pull in your repository & fetch the item:
public class ProductBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext,
ModelBindingContext bindingContext, Type modelType)
{
if(modelType != typeof(Product))
return null;
var form = controllerContext.HttpContext.Request.Form;
int id = Int32.Parse(form["Id"]);
if(id == 0)
return base.CreateModel(controllerContext, bindingContext, modelType);
IProductRepository repository = ServiceLocator.Resolve<IProductRepository>();
return repository.Fetch(id);
}
}
You might even make this work for all of your entities if you can use a base class or interface that provides the Id of the class.
You'll have to set this up in Global.asax:
ModelBinders.Binders.Add(typeof(Product), new ProductBinder());
and then you can do this:
public ActionResult Save([Bind] Product product)
{
....
_repository.Save(product);
}
Let me first state that I don't recommend to access database from ModelBinders, as from perspective of Separation Of Concern ModelBinders should only be responsible of interpretting client request, obviously database is not.
If you dont want to repeat your self (DRY), use repositories/services
However if u really want to do it like that, then
In global.asax.cs Register a custom MyModelBinderProvider to MVC
ModelBinderProviders.BinderProviders.Add(new EntityModelBinderProvider
{
ConnectionString = "my connection string"
));
Cunstruct the custom ModelBinderProvider to contain database settings
public class EntityBinderProvider: IModelBinderProvider
{
public string ConnectionString { get; set; }
public IModelBinder GetBinder(Type modelType)
{
if (Is known entity)
return new EntityBinder(ConnectionString);
else
return null;
}
}
Follow further instructions from Ben Scheirman
You don't actually have to hit the database. Simply setting the Id of the objects will be enough to set the relationship up, but watch your cascades. Make sure your cascde settings won't update the related object as it will clear the values.