Session Data in site.master - asp.net-mvc

I want to get a value from the user session and display it in the site.master file. How can I do this so that every view page has this value? Do I have to place ViewData["MyValue"] in every controller action? Is there a global way of doing this in one place so I don't have to have the same code in every controller action?

You could write an action filter attribute and decorate your controller with it:
public class CustomFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
filterContext.Controller.ViewData["MyValue"] = "some value";
}
}
And then decorate the controller with this attribute:
[CustomFilter]
public class MyController: Controller
{
// actions
}
This will ensure that ViewData["MyValue"] will be set on all action belonging to this controller.

<%= Session["MyValue"] %> in the master page

Related

How can I use Base Controller in ASP.NET to record user actions?

I am required to record user action and I don't want to have code in every method is every controller so would it make sense to somehow do this in the basecontroller ? OR is there a better way?
public class BaseController : Controller
{
protected ILogger logger;
public BaseController(ILogger<BaseController> logger)
{
this.logger = logger;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
//How do I get the current controller?
//How do I get the current method being called?
//How can I pass in additional parameters?
//How can I get the user?
logger.LogWarning("Loaded BaseController");
base.OnActionExecuting(context);
}
}
There are many ways to do that.
First: You could create your own base controller and implement OnActionExecution as you did. See the sample bellow to get information from ActionExecutingContext.
If you go this way, every controller that inhirits from this base controller will get the implementation of the logger because you are overriding OnActionExecuting (that applies to all actions of your controller).
public override void OnActionExecuting(ActionExecutingContext context)
{
//How do I get the current controller?
string controllerName = context.ActionDescriptor.ControllerDescriptor.ControllerName
//How do I get the current method being called?
string actionName = context.ActionDescriptor.ActionName;
//How can I pass in additional parameters?
foreach (var parameter in context.ActionParameters)
{
var parameterKey = parameter.Key;
var parameterValue = parameter.Value;
}
//How can I get the user?
var user = this.User; // IPrinciple instance, explore this object
logger.LogWarning("Loaded BaseController");
base.OnActionExecuting(context);
}
Second: On the other hand, you can use ActionFilters which is a class that inhirits from ActionFilter class and do the same implementation on this classe overriding the OnActionExecuting. Then you can decorate your controllers with this attribute to make the logger. Given it is an attribute, you have to define the name fo the class with a sufix Attribute and use without it. For sample:
public class LoggerAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
// same code above
}
}
[Logger]
public class CustomerController : Controller
{
// actions code...
}
Third: Use the same action filter class and instead of applying on all classes you want, you define it as a global action filter and it will be applied to all controllers. You have to define it on GlobalFilter and if you are using the default template of asp.net mvc, you can define it on the FilterConfig.cs, for sample:
filters.Add(new LoggerAttribute());
For getting controller & action names you can use ActionDescriptor of ActionExecutingContext
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
var descriptor = filterContext.ActionDescriptor;
var actionName = descriptor.ActionName;
var controllerName = descriptor.ControllerDescriptor.ControllerName;
......
base.OnActionExecuting(filterContext);
}
Regarding User information: controller initialisation will occur before authorisation takes place. So all of yours controllers will be created before any OnAuthorization takes place.
Approach to deal with these situations is to use Action Filters. The Authorize Attribute is fired early than controller initialisation occur.
Have a look this articles:
How to get controller and action name in OnActionExecuting?
How do I get the action name from a base controller?
Getting User Identity on my base Controller constructor

how to avoid specific actions to call another action method MVC

I have several action methods that get initiatated on a each Action on the page.
One of the Property asscoiated with this class has an custom-attribute defined, this inturn calls an method(action=method) defined within the same controller class.
But this action-method shouldn't be called on all actions , How to check before redirecting to this specific action ?
You can read from filter context or controller context object the value of current action from RouteData like this:
public class MyAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
var action = filterContext.RouteData.Values["action"];
if(action == "xxx")
...// then
base.OnResultExecuting(filterContext);
}
}

MVC - Redirect inside the Constructor

I would like to know how am I able to redirect the request inside the controller constructor if I need to do it?.
For example:
Inside the constructor I need to initialize an object with an dynamic value, in some cases I don't want to do it and in that case I want to redirect to some other place.
At the same way the rest of the constructor will not be executed neither the "original following action".
How can I do it?
Thank you
EDIT #1
Initially I used:
public override void OnActionExecuting(ActionExecutingContext filterContext)
There I could redirect to some other controller/action/url, but later in time, I needed to change my controller, where I initialize an variable in his constructor and have some code that really needs to redirect the request :P
I need this also because the OnActionExecuting executes AFTER the controller constructor.
And in my logic, the redirect needs to be done there.
Performing redirects inside the controller constructor is not a good practice because the context might not be initialized. The standard practice is to write a custom action attribute and override the OnActionExecuting method and perform the redirect inside. Example:
public class RedirectingActionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
if (someConditionIsMet)
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "someOther",
action = "someAction"
}));
}
}
}
and then decorate the controller which you would like to redirect with this attribute. Be extremely careful not to decorate the controller you are redirecting to with this attribute or you are going to run into an endless loop.
So you could:
[RedirectingAction]
public class HomeController : Controller
{
public ActionResult Index()
{
// This action is never going to execute if the
// redirecting condition is met
return View();
}
}
public class SomeOtherController : Controller
{
public ActionResult SomeAction()
{
return View();
}
}

Accessing current IPrinciple in a Master View in Asp.Net MVC

I currently have a abstract controller class that I all my controllers inherit from.
I want to be able to use the current user (IPrinciple) object in my master page.
I read that I could use the contructor of my abstract base controller class, that is I could do something like
public BaseController()
{
ViewData["UserName"] = this.User.Identity.Name;
}
I could then access ViewData["UserName"] etc from my master page.
My problem is that this.User is null at this point.
Does anybody know of a different approach?
Thanks in advance.
You could write an ActionFilter and in the OnActionExecuted event put the user inside ViewData:
public class UserActionFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
filterContext.Controller.ViewData["UserName"] = filterContext.HttpContext.User.Identity.Name;
}
}
And then decorate your base controller with this attribute:
[UserActionFilter]
public abstract class BaseController: Controller
{ }

.NET MVC instantiate controller inside another controller

Is it possible for an ASP.NET MVC controller to create a new instance of a different controller and effectively delegate resonsibility to that?
Let's say for example that I have two controllers in the /Controllers/ directory:
public class HomeController : Controller
{
public ActionResult Index()
{
var otherController = new OtherController();
return otherController.ShowNumberOfThings(100);
}
}
public class OtherController : Controller
{
public ActionResult ShowNumberOfThings(int index)
{
return View(index);
}
}
...and a View called Views/Other/ShowNumberOfThings.aspx:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="ViewPage<int>" %>
Number of things: <%= Model.ToString() %>
When I hit the url:
http://localhost/Home/Index
I want to be presented with a page that reads:
"Number of things: 100"
I would like to be able to persist temporary data between controller redirections without being forced to use the session object (TempData[""] uses the session object for cross-controller redirections). My real world case has a complex object which needs passing (not just an int) so using a URL/Cookie is out of the question, and session state is a no-no.
In WebForms at least we could use Server.Transfer and maintain any state in the HttpContext.Items collection. In MVC the only option I can see is to call the controller method directly passing in required arguments.
At the moment it's having trouble trying to resolve the view folder as the "context" is still running under the HomeController.
I guess where I am going with this is trying to cludge ASP.NET MVC into acting like a FrontContoller.
Any ideas?
EDIT
In the end we had to serialise everything into a session and use that. A shame, but I have heard that MVC2 will support serialising objects into a ViewState.
If you want to be presented with "Number of things: 100" when you hit the Index action why not directly render the corresponding view:
public class HomeController : Controller
{
public ActionResult Index()
{
return View("~Views/Other/ShowNumberOfThings.aspx", 100);
}
}
I think it would be preferred to use.
return RedirectToAction("Controller", "Action")
However I'm guessing you want to maintain the Url Home/Index.
If you're looking at the FrontController pattern then you should investigate writing a Custom ControllerFactory which inherits from DefaultControllerFactory then Override the CreateController method.
You can register your factory using the code below.
protected void Application_Start()
{
ControllerBuilder.Current.SetControllerFactory(new MyCustomControllerFactory();
RegisterRoutes(RouteTable.Routes);
}
In the Controller factory you have access to the RequestContext so you can change the RouteData as needed and delegate to the correct controller.
You could of course just set a a Custom route for Home/Index which goes to OtherController.ShowNumberOfThings()
routes.MapRoute("Home", "Home/Index/{id}",
new {controller = "Other", action = "ShowNumberOfThings", id = 100});
a different approach would be the use of partial views
instead of ~Views/Other/ShowNumberOfThings.aspx
you could put your view in ~Views/shared/ShowNumberOfThings.ascx
have both views ~Views/Other/ShowNumberOfThings.aspx and ~Views/Home/Index.aspx implement the partial view
public class HomeController : Controller
{
public ActionResult Index()
{
return View(100);
}
}
public class OtherController : Controller
{
public ActionResult ShowNumberOfThings(int index)
{
return View(index);
}
}
and in both views implement the partial view
<% Html.RenderPartial("~Views/shared/ShowNumberOfThings.ascx", ViewData.Model); %>
you can change the int for any object that will be passed to the model
Another possibility (similar to partial views) is to use Html.RenderAction. This allows for different view model classes and separate controller methods.
<% Html.RenderAction("yourActionName", "yourControllerName", routeValues); %>

Resources