.NET MVC user details in Controller CTOR - asp.net-mvc

I would like to find a way to use custom User provider within a controllers ctor in order to not have to get the user on every method (dirty)..
This is what is in my ctor but keeps returning null?
Resource oResource;
public EntityController()
{
try
{
DataEntities oEntities = new DataEntities();
oResource = oEntities.Resources.Where(c => c.user == User.Identity.Name).First();
}
catch
{
oResource = new Resource();
}
}

Override the Controller.Initialize() method and put your code in there. The controller's context isn't available in the constructor.
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
// your code here
}
If you need the user's entity on every action, then push the behavior into a base class that your controllers inherit from.

Related

MVC global filter to load/maintain menus from database on page load

I am working on MVC5 applications where I have a base controller which is inherited by every controller in my application.
I am using OnActionExecuting function of base controller to load and maintain menus from database.
As this function will be called every-time any controller inherit base controller so some times it has been called more than once.
Can I use/create any other function from base controller which will be called once when view is about to render.
Is there any better way to maintain menus for the same user as in such case no need to hit database on every page, TIA.
Good idea will be to use caching. For instance Cache Class where you can store objects and retrieve them by key.
public abstract class ControllerBase
{
private readonly Cache _cache;
public ControllerBase(Cache cache)
{
// null check
_cache = cache;
}
protected virtual void OnActionExecuting(ActionExecutingContext filterContext)
{
string username = filterContext.HttpContext.User.Identity.Name;
// authenticate duser has always username
if(!String.IsNullOrEmpty(username))
{
var cacheItem = _cache.Get("menu_" + username);
if(cacheItem is {Type})
{
}
else
{
// Load from db
// Add to cache with sliding expiration
}
}
else
{
var cacheItem = _cache.Get("menu_anonymous_user");
if(cacheItem is {Type})
{
}
else
{
// Load from db
// Add to cache with sliding expiration
}
}
}
}
It is an example. It can be done much better, but for an idea it is enough, I guess.

MVC BaseController and BaseModel to ensure that values are available to all views

This is a question about a solution provided by #Andre Calil in the following SO
Razor MVC, where to put global variables that's accessible across master page, partiview and view?
I'm using Andre's approach and have a slight problem:
My _Layout is strongly typed as BaseModel and my view is strongly typed as AccountModel which inherits from BaseModel.
Problem is: if I return a view with no model i.e. return View() then I get an exception. It's caused because the BaseController OnActionExecuted event is checking if the model provided is null as in:
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Result is ViewResultBase)//Gets ViewResult and PartialViewResult
{
object viewModel = ((ViewResultBase)filterContext.Result).Model;
if (viewModel != null && viewModel is MyBaseModel)
{
MyBaseModel myBase = viewModel as MyBaseModel;
myBase.Firstname = CurrentUser.Firstname; //available from base controller
}
}
base.OnActionExecuted(filterContext);//this is important!
}
The model is null so this scenario won't always work. My next step was to make sure I always pass a model into a view even if it's an empty BaseModel object. Problem with that is that I get the following exception:
The model item passed into the dictionary is of type 'MyNamespace.BaseModel', but this dictionary requires a model item of type 'MyNamespace.AccountModel'.
Two points that I need to clarify:
I thought this would work because AccountModel is a sub class of BaseModel?
If the model is null in the code above, is there another way that I can inject a model into each view so that I can avoid having to refactor all my code to include return View(BaseModel.Empty)?
You need to look at custom razor views as described by Phil Haacked in this article:
http://haacked.com/archive/2011/02/21/changing-base-type-of-a-razor-view.aspx
So basically in your BaseController you would set up a public variable that will be fetched on every request in the base controller's Initialize event (in my case it is the instance of User):
public class BaseController : Controller
{
public User AppUser;
protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
AppUser = _userService.GetUser(id);
ViewBag.User = AppUser;
}
}
So now you have a variable which can be accessed by any controller which inherits from the base controller. The only thing left to do is to figure out how to use this variable inside your view. This is where the article I linked above will help you. By default all your views are generated from System.Web.Mvc.WebViewPage. However you can make a custom implementation of this class by doing the following:
namespace YourApplication.Namespace
{
public abstract class CustomWebViewPage : WebViewPage
{
private User _appUser;
public User AppUser
{
get
{
try
{
_appUser = (User)ViewBag.User;
}
catch (Exception)
{
_appUser = null;
}
return _appUser;
}
}
}
public abstract class CustomWebViewPage<TModel> : WebViewPage<TModel> where TModel : class
{
private User _appUser;
public User AppUser
{
get
{
try
{
_appUser = (User)ViewBag.User;
}
catch (Exception)
{
_appUser = null;
}
return _appUser;
}
}
}
}
You have just defined a custom razor view class which has a property of user and tries to fetch that from the ViewBag.User that we setup in our base controller. The only thing left to do is to tell your app to use this class when it's trying to generate the view. You can do this by setting the following line in your VIEWS web.config file:
<pages pageBaseType="YourApplication.Namespace.CustomWebViewPage">
Now on your view you get a helper for your User property that you can use like this:
#AppUser
Please not that the pages declaration needs to go into the VIEWS web.config files not the main app web.config!
I think this is a much better solution for you since you don't have to provide the base model to all your view via the view model. View model should be reserved for what it is intended.

MVC3 - How to correctly inject dependencies with MVC3 and Ninject?

I am attempting to redesign an existing application using dependency injection with Ninject in MVC3. Here is a portion of the legacy behavior I'm having difficulty with (and yes I know its bad, that's why I'm trying to refactor it):
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
MyUserSession userSession = filterContext.HttpContext.Session[SESSIONKEY_USER] as MyUserSession;
// if session empty, rebuild user information
if (userSession == null)
{
string userName = HttpContext.User.Identity.Name;
userSession = new MyUserSession();
using (ADSearcher ad = new ADSearcher(ldapPath, excludeOUString.Split(',')))
{
// get basic user information from Active Directory
ADUserInfo aduser = MyActiveDirectorySearcher.GetUserRecord(userName);
// ... set several properties queries from AD...
userSession.propertyXYZ = aduser.propXYZ
}
// if user can proxy as another indivudual, set property
using (EDMContainer db = new EDMContainer())
{
if (db.Proxies.Any(p => p.ProxyLogin == userSession.userLogin))
userSession.CanProxy == true;
}
// save new user object to session
filterContext.HttpContext.Session[SESSIONKEY_USER] = userSession;
if(userSession.canProxy)
filterContext.Result = RedirectToAction("Proxy", "Home");
return;
}
}
So currently, the controller users several objects directly: Session, ActiveDirectorySearch, EF Database. I understand it would be better to create a class that exposes a single method "GetUser" masking all the complexity but I'm struggling with how to inject the dependencies.
If I create a class SomeUserProvider, it will also need access to the Session to check for existing user information, and then ActiveDirectorySearcher and Database to rebuild the user properties if session was empty.
My confusion is over the fact that the controller itself will need access to ActiveDirectorySearcher in other action methods and then other classes will also use the same database. Do I inject an IActiveDirSearchrer into the controller's constructor and then pass it down into the ISomeUserProvider? What about IMyDatabase? Is it also injected in controller constructor and passed down?
And last but not lease, ISessionWrapper? I know session is controversial, but I need to track who the current user is and who they are proxied as during each request (GETs and POSTs). So, does that get injected as well?
If the answer is yes to each of those, is it bad to have 3+ injected contstuctor parameters?
I realize my question may be vague, so please ask for clarification where needed. I am open to any and all suggestions and recommendations. My goal is to learn how to do it correctly.
Thanks.
I'm not certain if this is exactly what you're looking for, but this should get you started down the path of refactoring your app for DI
public class YourController : Controller
{
private readonly ISessionWrapper _sessionWrapper;
private readonly IActiveDirSearcher _adSearcher;
private readonly IMyDatabase _database;
public YourController(ISessionWrapper sessionWrapper,
IActiveDirSearcher adSearcher, IMyDatabase database)
{
this._sessionWrapper = sessionWrapper;
this._adSearcher = adSearcher;
this._database = database;
}
// now all actions in this controller have a _sessionWrapper,
// _adSearcher and _database
}
Then you have to bind your injections the Ninject way. Subclass your application from NinjectHttpApplication and override OnApplicationStarted and CreateKernel
public class MvcApplication : NinjectHttpApplication
{
// ...
protected override void OnApplicationStarted()
{
base.OnApplicationStarted();
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
protected override IKernel CreateKernel()
{
var kernel = new StandardKernel();
kernel.Bind<ISessionWrapper>().To<YourSessionWrapperImplementation>();
kernel.Bind<IActiveDirSearcher>().To<YourADImplementation>();
kernel.Bind<IMyDataBase>().To<YourEDMContainerIThink>();
return kernel;
}
}
The implementations of these appear to be described in your question. However, you mentioned other actions (and other classes) depend on these implementations. Good news--the bindings in CreateKernel will take care of any missing dependencies elsewhere in your app. e.g.
public class MyActiveDirImplementation : IActiveDirSearcher
{
private readonly IMyDatabase _database;
// injected automagically WOOHOO!
public MyActiveDirImplementation(IMyDatabase database)
{
this._database = database;
}
public ADUserInfo GetUserRecord(string username)
{
return _database.GetSomeUserRecord(username);
}
}
You could, of course, similarly implement your ISessionWrapper or IMyDatabase

Best approach to don't request same info over and over

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.

HttpContext.Current.User is null in ControllerBase(asp.net mvc)

I have a ControllerBase class in an ASP.NET MVC Application. The other controllers inherit from ControllerBase.
I want to access HttpContext.User.Identity.Name, but HttpContext is null. What's the matter?
public ControllerBase()
{
var dataManager=new DataManager();
if (HttpContext.User.Identity.IsAuthenticated) // throws error
{
ViewData["assets"] = ud.BalanceFreeze + ud.Balance + ud.BalanceRealty;
ViewData["onaccount"] = ud.Balance;
ViewData["pending"] = ud.BalanceFreeze;
ViewData["inrealty"] = ud.BalanceRealty;
}
Try adding your code to this event in your ControllerBase:
protected override void Initialize(RequestContext requestContext){
}
Your controller gets constructed before the HttpContext has been set by ASP.NET. Like Nik says, you need to put this code into an overridden method in your class.
I would also point out that depending on HttpContext directly will make it impossible to perform unit testing on any of your controllers that extend this class. This is why many of the methods (like the Execute method) in the ControllerBase class take a RequestContext as an argument. You can say:
protected override void Execute(System.Web.Routing.RequestContext requestContext)
{
var currentUser = requestContext.HttpContext.User;
...
}
... which makes it possible to create and execute your controllers with "fake" contexts for unit testing purposes.

Resources