C# Centralizing repeating VIewData in MVC - c#

When a user log in into my application i want to show his name throughout the whole application. I am using the asp.net MVC framework. But what i don't want is that is have to put in every controller something like:
ViewData["User"] = Session["User"];
This because you may not repeat yourself. (I believe this is the DRY [Don't Repeat Yourself] principle of OO programming.)
The ViewData["User"] is on my masterpage. So my question is, what is a neat way to handle my ViewData["User"] on one place?

You can do this fairly easily in either a controller base-class, or an action-filter that is applied to the controllers/actions. In either case, you get the chance to touch the request before (or after) the action does - so you can add this functionality there.
For example:
public class UserInfoAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(
ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
filterContext.Controller.ViewData["user"] = "Foo";
}
}
...
[HandleError, UserInfo]
public class HomeController : Controller
{...}
(can also be used at the action (method) level)
or with a common base-class:
public abstract class ControllerBase : Controller
{
protected override void OnActionExecuting(
ActionExecutingContext filterContext)
{
ViewData["user"] = "Bar";
base.OnActionExecuting(filterContext);
}
}
[HandleError]
public class HomeController : ControllerBase
{...}

It's been a year, but I've just stumbled across this question and I believe there's a better answer.
Jimmy Bogard describes the solution described in the accepted answer as an anti-pattern and offers a better solution involving RenderAction: http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/06/18/the-filter-viewdata-anti-pattern.aspx

Another method for providing persistent model data through out your entire application is by overriding the DefaultFactoryController with your custom one. In your CustomerFactoryController, you would hydrate the ViewBag with the model you are wanting to persist.

Create a base class for your models with UserName property:
public abstract class ModelBase
{
public string UserName { get; set; }
}
Create a base class for you controllers and override it's OnActionExecuted method. Within it check if model is derrived from BaseModel and if so, set it's UserName property.
public class ControllerBase : Controller
{
protected override void OnActionExecuted(
ActionExecutedContext filterContext)
{
var modelBase = ViewData.Model as ModelBase;
if (modelBase != null)
{
modelBase.UserName = "foo";
}
base.OnActionExecuted(filterContext);
}
}
Then you will be able to display user's UserName in the view like this:
<%= Html.Encode(Model.UserName) %>
See also:
ASP.NET MVC Best Practices, Tips and Tricks

Related

Create instance of interface with dependency injection

I have a base controller and before every page load I want to get the current user. I originally had a constructor in my BaseController that looked like this
public BaseController(ISystemUserCommand command)
{
_systemUserCommand = command
}
The problem with this then is that every controller that inherits from the BaseController would have to contain the ISystemUserCommand in its constructor, which I don't think would be good.
Instead I tried to create just an instance of the service class (shown below - it's the commented line under var sid...) but I need to pass in user service. How would I pass in the user service here or is this a bad way of doing it?
public abstract class BaseController : Controller
{
public SystemUserViewModel CurrentUser { get; set; }
private readonly ISystemUserCommand _systemUserCommand;
public SystemUserViewModel GetCurrentUser()
{
if (HttpContext == null || HttpContext.User == null) return null;
if (CurrentUser != null) return CurrentUser;
var sid = System.Web.HttpContext.Current.Request.LogonUserIdentity.User.ToString();
//var command = new SystemUserCommand();
CurrentUser = _systemUserCommand.GetUser(sid);
return CurrentUser;
}
public void SetUserInformation(SystemUserViewModel currentUser)
{
ViewBag.UserId = currentUser.SystemUserId;
ViewBag.FullName = string.Format("{0} {1}", currentUser.FirstName, currentUser.LastName);
ViewBag.FirstName = currentUser.FirstName;
ViewBag.LastName = currentUser.LastName;
ViewBag.CurrentUser = currentUser;
}
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var currentUser = GetCurrentUser();
if (currentUser != null)
{
if (currentUser.IsActive)
{
SetUserInformation(currentUser);
}
else
filterContext.Result = RedirectToAction("denied", "unauthorized");
}
else
filterContext.Result = RedirectToAction("denied", "unauthorized");
base.OnActionExecuting(filterContext);
}
}
public class SystemUserCommand : ISystemUserCommand
{
private readonly ISystemUserBusiness _systemUserBusiness;
public SystemUserCommand(ISystemUserBusiness systemUserBusiness)
{
_systemUserBusiness = systemUserBusiness;
}
...
}
You could use property injection instead of constructor injection, via the base class, eg using unity:
public abstract class BaseController : Controller
{
[Dependency]
public ISystemUserCommand SystemUserCommand { get; set; }
}
This would mean the interface reference is only on the base class.
See here for the full examples.
EDIT, Autofac example:
You don't need property attributes on the dependency,
public abstract class BaseController : Controller
{
public ISystemUserCommand SystemUserCommand { get; set; }
}
Just to register the properites to auto resolve on the autofac builder:
builder.RegisterControllers(typeof(MvcApplication).Assembly).Where(t => t.IsAssignableFrom(typeof(BaseController))).PropertiesAutowired();
See autofac property injection here.
First of all, it does not seem a good idea to have OnActionExecuting override in the controller. You can use filters, that are specially designed for this purpose. And it seems that is the main reason you created the BaseController at all.
Regarding the problem with injecting the system command in all the required service, I would do so, but without inheriting from a base class, since I generally prefer aggregation to inheritance. That would mean that each controller that needs to work with the service will get it.
Another option that I have used few times to abstract some operations is to create a UserSerivce that will provide the required operations to the controllers. It will have ISystemUserCommand and HttpContext injected inside so that all of your controllers won't have to do the job. You can either use HttpContext.Current as static or abstract it away if you need testability.
Moreover I would not recommend property injection since it is more obscure than constructor injection that should be preferred if possible.
You can read more about filters here. Unfortunately if you use filters it's not that easy to inject in filters themselves and mostly done with property injection or ServiceLocator pattern (which is not good usually). It's possible to do better with some amount of voodoo though. I think that SimpleInjector has a lot of examples and tutorials on how to apply DI to filters in MVC, maybe they even have a nuget package now to ahieve that.

How do I inherit from an ActionFilter attribute in a assembly in the same project but not directly referenced?

What I am asking my not even be possible (I am skeptical) but I have been tasked to do this exact thing so please if its not possible or dumb please explain why.
I have a base project that contains an ActionFilter attribute that overrides OnActionExecuting. It does nothing as it is supposed to be a base class to inherit from.
namespace mybaseproject
public class BaseActionAttribute : ActionFilterAttribute
{
public string ActionName {get;set;}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
}
}
I have a second project that references 'mybaseproject' and uses BaseActionAttribute.
namesapce mysecondproject
public class MyController : Controller
{
[BaseAction(ActionName="Index")]
public ActionResult Index()
{
//do stuff
}
}
I have a third project that references both 'mybaseproject' and 'mysecondproject'. It has an ActionFilterAttribute that inherits from the base one and has all of the real code to process.
using mybaseproject;
namespace mythirdproject
public class DerivedActionAttribute : BaseActionAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// do all the real stuff
base.OnActionExecuting(filterContext);
}
}
What the desired effect is for the BaseActionAttribute in mysecondproject to use the code in DerivedActionAttribute of mythirdproject. I don't see how this can work because mysecondproject has no reference to mythirdproject. They just reside in the solution. I have tried using Authorize attribute as well but my breakpoint never gets hit in the derived attribute when I call my action in the second project.
This was how I was told to tackle the problem. Either I misunderstood or am taking the wrong approach, or this is just not possible.
Which one? Thanks ahead for listening.

Create controller base class (partial)

Since my #html.render action crashes my dev and prod servers i have to use partials(crap).
I tried creating public partial controller{} class so i can set needed data for all my views but i am having no luck (everything breaks).
I am coming from LAMP cakePHP background and really need simplicity.
I need to know how to create a partial base controller(that doesnt override the regular base controller) and how to access multiple models from the class.
Thank you!
public class BaseController: Controller
{
public override OnActionExecuting(...) { ... }
public override OnActionExecuted(... context)
{
if (context.Result is ViewResult)
((ViewResult)context.Result).ViewData["mycommondata"] = data;
}
...
}
public class MyController1: BaseController
{
}
I.e. just derive from your new base controller class.
However I'd suggest you to ask here why your RenderPartial "crashes" - since it can be a better way for you, and it obviously shouldn't crash.
better way to create base controller
public class Controller : System.Web.Mvc.Controller
{
public shipsEntities db = new shipsEntities();
public Controller()
{
ViewData["ships"] = db.ships.ToList();
}
}
that way the rest of controllers follow regular convention
public class MyController : Controller

MVC Attributes on Controllers and Actions

Is there a way to add an Attribute on the Controller level but not on a specific action. For example say if i had 10 Actions in my Controller and just 1 of those Actions does not require a specific attribute I created.
[MyAttribute]
public class MyController : Controller
{
public ActionResult Action1() {}
public ActionResult Action2() {}
[Remove_MyAttribute]
public ActionResult Action3() {}
}
I could potentially move this Action into another controller (but dont like that) or I could apply the MyAttribute to all actions except from Action3 but just thought if there is an easier way?
I know my answer is a little late (almost four years) to the game, but I came across this question and wanted to share a solution I devised that allows me to do pretty much what the original question wanted to do, in case it helps anyone else in the future.
The solution involves a little gem called AttributeUsage, which allows us to specify an attribute on the controller (and even any base controllers!) and then override (ignore/remove) on individual actions or sub-controllers as needed. They will "cascade" down to where only the most granular attribute actually fires: i.e., they go from least-specific (base controllers), to more-specific (derived controllers), to most-specific (action methods).
Here's how:
[AttributeUsage(AttributeTargets.Class|AttributeTargets.Method, Inherited=true, AllowMultiple=false)]
public class MyCustomFilterAttribute : ActionFilterAttribute
{
private MyCustomFilterMode _Mode = MyCustomFilterMode.Respect; // this is the default, so don't always have to specify
public MyCustomFilterAttribute()
{
}
public MyCustomFilterAttribute(MyCustomFilterMode mode)
{
_Mode = mode;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (_Mode == MyCustomFilterMode.Ignore)
{
return;
}
// Otherwise, respect the attribute and work your magic here!
//
//
//
}
}
public enum MyCustomFilterMode
{
Ignore = 0,
Respect = 1
}
(I heard you like attributes, so I put some attributes on the attribute! That's really what makes the magic work here at the very top: Allowing them to inherit/cascade, but only allowing one of them to execute.)
Here's how it is used now:
[MyCustomFilter]
public class MyBaseController : Controller
{
// I am the application's base controller with the filter,
// so any derived controllers will ALSO get the filter (unless they override/Ignore)
}
public class HomeController : MyBaseController
{
// Since I derive from MyBaseController,
// all of my action methods will also get the filter,
// unless they specify otherwise!
public ActionResult FilteredAction1...
public ActionResult FilteredAction2...
[MyCustomFilter(Ignore)]
public ActionResult MyIgnoredAction... // I am ignoring the filter!
}
[MyCustomFilter(Ignore)]
public class SomeSpecialCaseController : MyBaseController
{
// Even though I also derive from MyBaseController, I can choose
// to "opt out" and indicate for everything to be ignored
public ActionResult IgnoredAction1...
public ActionResult IgnoredAction2...
// Whoops! I guess I do need the filter on just one little method here:
[MyCustomFilter]
public ActionResult FilteredAction1...
}
I hope this compiles, I yanked it from some similar code and did a little search-and-replace on it so it may not be perfect.
You have to override/extend the default attribute and add a custom constructor to allow exclusion. Or you can create your custom attribute for exclusion (in your example is the [Remove_MyAttribute]).
Johannes gave the correct solution and here is how I coded it... hope it helps other people.
[MyFilter("MyAction")]
public class HomeController : Controller
{
public ActionResult Action1...
public ActionResult Action2...
public ActionResult MyAction...
}
public class CompressFilter : ActionFilterAttribute
{
private IList _ExcludeActions = null;
public CompressFilter()
{
_ExcludeActions = new List();
}
public CompressFilter(string excludeActions)
{
_ExcludeActions = new List(excludeActions.Split(','));
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
HttpRequestBase request = filterContext.HttpContext.Request;
string currentActionName = (string)filterContext.RouteData.Values["action"];
if (_ExcludeActions.Contains(currentActionName))
return;
...
}
You could exclude a specific action by passing it to the main attribute:
[MyAttribute(Exclude="Action3")]
EDIT
My example was from the head (as you can see the following is VB.NET, maybe that's where it went wrong), this is how I implemented:
<Models.MyAttribute(Exclude:="Action3")> _
Public Class MyController
Inherits System.Web.Mvc.Controller
End Class
The usual pattern for what you are trying to do is to have and attribute with a boolean parameter that indicates if the attribute is applied or not.
Ex:
[ComVisible] which is equivalent with [ComVisible(true)]
or
[ComVisible(false)]
inf your case you would have:
[MyAttribute] // defaults to true
and
[MyAttribute(false)] for applying the attribute on excluded members

Why is User (as in User.Identity.Name) null in my abstract base controller?

I was asking a related question but messed the title up and no-one would understand it. Since I am able now to ask the question more precisely, I decided to reformulate it in a new question and close the old one. Sorry for that.
So what I want to do is passing data (my custom user's nickname as stored in the db) to the LoginUserControl. This login gets rendered from the master page via Html.RenderPartial(), so what I really need to do is making sure that, say ViewData["UserNickname"] is present on every call. But I don't want to populate ViewData["UserNickname"] in each and every action of every controller, so I decided to use this approach and create an abstract base controller which will do the work for me, like so:
public abstract class ApplicationController : Controller
{
private IUserRepository _repUser;
public ApplicationController()
{
_repUser = RepositoryFactory.getUserRepository();
var loggedInUser = _repUser.FindById(User.Identity.Name); //Problem!
ViewData["LoggedInUser"] = loggedInUser;
}
}
This way, whatever my deriving Controller does, the user information will already be present.
So far, so good. Now for the problem:
I can't call User.Identity.Name because User is already null. This is not the case in all of my deriving controllers, so this is specific for the abstract base controller.
I am setting the User.Identity.Name via FormsAuthentication at another place in the code, but I think this can't be the problem - afaik User.Identity.Name can be null, but not User itself.
It looks to me like the HttpContext is not available (since also null ;-) and that I am missing a simple yet important point here. Can anyone give me some hints? I would really appreciate it.
The answer to this problem is actually quite simple. I can't execute the code from within the constructor for reasons pointed out by Raimond, but I can do it outside the constructor.
So what I did was overriding onActionExecuting() in the base controller class (I created a custom Attribute for it, but just overriding the method should also work) and then do my user lookup from there.
Now it works as expected and I have no repeated code.
The User property is not assigned until after the Controller has been instantiated, but you can gain early access from your constructor with:
System.Web.HttpContext.Current.User
My guess would be that the Controller's base constructor is not filling in the User, but that it is only known later when the ControllerContext is set for the Controller. You should check this in the documentation about the lifecycle of an MVC application, (the one here will probably do, although it might be a bit out of date since it's for the preview version), or just check the source code of MVC.
from the code that I have of MVC (also a preview version, but that should be fine):
(In Controller)
public IPrincipal User {
get {
return HttpContext == null ? null : HttpContext.User;
}
}
...
public HttpContextBase HttpContext {
get {
return ControllerContext == null ? null : ControllerContext.HttpContext;
}
}
I don't see en an implementation of a default constructor in the code.
That would prove that the ControllerContext is null at the time of construction.
So you should execute your code somewhere else.
Can you grab this using something like:
HttpContext currentContext = HttpContext.Current;
string userName = currentContext.User.Identity.Name;
Or is the HttpContext always empty??
Could you set the httpContext through the constructor of the abstract class? and use it this way?
Thanks Raimond. I was too tired to see the obvious.
#Keeney: Yes the context is always null. Raimond pointed out why. Thanks anyway, I didn't see why too :-)
My current working solution (albeit not what I wanted) is a Attribute that I use to decorate all my controller actions. Here is the implementation:
public class MasterPageDataAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
IUserRepository _repUser = RepositoryFactory.getUserRepository();
IPrincipal siteUser = filterContext.Controller.ControllerContext.HttpContext.User;
User loggedInUser = null;
if (siteUser == null || siteUser.Identity.Name == null)
{
//do nothing
}
else
{
loggedInUser = _repUser.findUserById(siteUser.Identity.Name);
}
filterContext.Controller.ViewData["LoggedInUser"] = loggedInUser ?? new User { Nickname = "Guest" };
}
}
I will be looking into how to get that code executed in a way that follows the DRY principle, since using attributes for that definitely means repeating oneself. Maybe some sort of interceptor (interesting idea) or hook might help.
Cheers for that.
I am doing this in a basecontroller implementation and it works as expected.
public abstract class BaseController : Controller
{
public bool LoggedOn
{
get { return User.Identity.IsAuthenticated; }
}
}
This always returns true or false for me so User != null
to Masterfu:
I did something similiar with your help, wish that can help latter visitors.
In my case, i need to create reposiotry of controllers for different users, yet in the constructor of controllers, (principal)User is not ready. So i created a attribute for controllers:
[CreateRepositoryByUser]
public class MFCController : Controller
{
protected MFCRepository _repository
{
get { return ViewData["repository"] as MFCRepository; }
}
...
the _repository, indeed, is not a private variable of controller, but somethign create by the attribute:
public class CreateRepositoryByUser : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
CreateRepository(filterContext);
}
public static void CreateRepository(ActionExecutingContext filterContext)
{
if (filterContext.Controller.ViewData["repository"] == null)
{
filterContext.Controller.ViewData["repository"] =
MFCRepository.CreateMFCRepository(filterContext.Controller.ControllerContext.HttpContext.User);
}
}
}
I put codes of creating the repository in a seperate method, in case of that other attributes may want to use (principal)User before this attribute being triggered.
Calling from a constructor is too soon in the MVC pipeline.
Moving code to OnAuthorization, you get authorized user in a parameter. Worked for me!
From your example I would do something like this:
public abstract class ApplicationController : Controller {
private IUserRepository _repUser;
protected override void OnAuthorization(AuthorizationContext filterContext)
{
_repUser = RepositoryFactory.getUserRepository();
var loggedInUser = _repUser.FindById(filterContext.HttpContext.User.Identity.Name); //Problem!
ViewData["LoggedInUser"] = loggedInUser;
}
}
Inject IPrincipal if you need User in the constructor.
// startup.cs
// Inject IPrincipal
services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);
Then add as IPrincipal in your constructor. Note that it is guaranteed to be ClaimsPrincipal with ASPNET - because that's what HttpContext.User is.
Similar question
Select Project -> press F4 -> anonymous login -> false | windows authentication - > True

Categories

Resources