assigning a layout for an entire controller - asp.net-mvc

is there any way to assign an entire controller to use a certain layout? I know you can assign the layout in the ViewStart, is there anyway for the viewStart to know what controller is being used?
My objective is to have two admin layouts, one with an extra navbar when you are working with anything in the admin controller.

You could write a custom action filter:
public class LayoutAttribute : ActionFilterAttribute
{
private readonly string _layout;
public LayoutAttribute(string layout)
{
_layout = layout;
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var result = filterContext.Result as ViewResult;
if (result != null)
{
result.MasterName = _layout;
}
}
}
and then decorate your controller with it and all actions (returning view results obviously) withing this controller will use the layout you have specified:
[Layout("_SimpleLayout")]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult About()
{
return View();
}
}

Related

how to check condition if temp data has value in every controller

I am looking for the optimized way of checking if tempdata has value or not in every controller in MVC. I don't want to write in every controller, Where can I write this condition that if temp data has value then show the view else redirect to Login page.
Please create a base controller
public class BaseController : Controller
{
protected override void OnActionExecuting(System.Web.Mvc.ActionExecutingContext filterContext)
{
var id = filterContext.Controller.TempData["id"];
if (id == null)
{
filterContext.Result = RedirectToAction("Index", "Home");
}
base.OnActionExecuting(filterContext);
}
}
After that you have to inherit the base controller on very controller.
public class CheckController : BaseController
{
public ActionResult Index()
{
TempData["id"] = 2;
return View();
}
}
Use base controller in every controller. Then check in base controller that if temp data has value then show the view else redirect to Login page.
Filter code:
public class MyCustomAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.TempData.Add("some key", "some value");
}
}
Action code:
[MyCustom]
public ViewResult Index()
{
string value = TempData["key"] as string;
return View();
}

Setting a compiled view (RazorGenerator) as a layout using an ActionFilter

I created a layout view in a separate project (let's call it Shared) from the main MVC project (let's call it Main) and compiled that view using RazorGenerator. When I use the layout in any MVC page in Main it works fine:
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
However, when I set the layout using an ActionFilter this way:
public class LayoutAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
var viewResult = filterContext.Result as ViewResult;
viewResult.MasterName = "~/Views/Shared/_Layout.cshtml";
}
}
And simply use it in the controller in Main like this:
[Layout]
public class HomeController : Controller
{
// GET: Home
public ActionResult Index()
{
return View();
}
}
I get this error:
The view 'Index' or its master was not found or no view engine supports the searched locations. The following locations were searched:
~/Views/Home/Index.cshtml
~/Views/Shared/Index.cshtml
~/Views/Home/Index.aspx
~/Views/Home/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx
~/Views/Shared/_Layout.cshtml
I'd really like to use an ActionFilter built into the Shared project so that I can just use it in any controller in the Main project (or any others).
Any ideas why I'm getting this error?
Change your action from this:
public class LayoutAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
var viewResult = filterContext.Result as ViewResult;
viewResult.MasterName = "~/Views/Shared/_Layout.cshtml";
}
}
To this one below:
public class LayoutAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
var viewResult = filterContext.Result as ViewResult;
if (viewResult != null)
{
viewResult.MasterName = "~/Views/Shared/_Layout.cshtml";
}
}
}
Hope that helps.

Asp.Net MVC How to log all actions being called

I need to be able to log all actions that are called from my asp.net mvc application. How and what would be the best way to achieve this? Where I log it to whether it be the console or log file doesn't matter.
You could create your own class which inherits from ActionFilterAttribute and then override the OnActionExecuting method.
Example
public class LogActionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.RequestContext.RouteData.Values["Controller"];
var action = filterContext.RequestContext.RouteData.Values["Action"];
//
// Perform logging here
//
base.OnActionExecuting(filterContext);
}
}
public class HomeController : Controller
{
[LogAction]
public ActionResult Index()
{
return View();
}
}
Hope this helps!
Credit HeyMega for their answer. Here's an example of an expanded implementation I arrived at in MVC5.
public class LogActionAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.RequestContext.RouteData.Values.ContainsKey("Controller") ? filterContext.RequestContext.RouteData.Values["Controller"].ToString() : null;
var action = filterContext.RequestContext.RouteData.Values.ContainsKey("Action") ? filterContext.RequestContext.RouteData.Values["Action"].ToString() : null;
var area = filterContext.RequestContext.RouteData.DataTokens.ContainsKey("Area") ? filterContext.RequestContext.RouteData.DataTokens["Area"].ToString() : null;
var user = filterContext.RequestContext.HttpContext.User.Identity.GetUserId();
Task.Run(() => Generic().AreaActionLog(user, area, controller, action));
base.OnActionExecuting(filterContext);
}
}
I chose to separate the method doing the actual logging into a separate process, if anything goes wrong with the Database interaction, or the DB interaction takes several seconds, the UI is uninterrupted.
You can then decorate the entire controller with [LogAction] attribute like so.
[LogAction]
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult Contact()
{
return View();
}
}
Or selectively apply the attribute by decorating individual methods:
public class HomeController : Controller
{
[LogAction]
public ActionResult Index_Logs_Things()
{
return View();
}
}
Hope this helps someone.
You could try Audit.NET library with its Audit.MVC and the different data providers to store the logs on files, eventlog, sql, redis, mongo, and much more.
With the MVC extension you just need to decorate your controllers or actions with an attribute:
[Audit]
public class HomeController : Controller
{ ... }
Execute a static configuration to set the output of your logs:
Audit.Core.Configuration.Setup()
.UseFileLogProvider(_ => _
.Directory(#"C:\Logs"));
And it will provide the infrastructure to log the interactions with your MVC application.

Asp.Net MVC call method when every view is called

I have some ViewBag need to pass to every views when them are called.
For example:
public ActionResult Index()
{
ViewBag.Importan1 = "A";
ViewBag.Importan2 = "B";
return View();
}
public ActionResult Detail()
{
ViewBag.Importan1 = "A";
ViewBag.Importan2 = "B";
return View();
}
and I want to:
public ActionResult Index()
{
return View();
}
public ActionResult Detail()
{
return View();
}
but the ViewBag Important1 and Important2 are called implicit.
I would suggest you a custom filter attribute. You create it (here some good examples http://msdn.microsoft.com/en-us/library/dd381609(v=vs.100).aspx) and do all the work inside. Then you just have to apply the filter on your controller and it will affect all your action methods.
You can write a super class for the controller defines Index and Detail as virtual (to be able to override them and add some extra functionality) and set Important1 and Important2 in your base class as
public class MyController : Controller
{
public virtual ActionResult Index()
{
ViewBag.Importan1 = "A";
ViewBag.Importan2 = "B";
return View();
}
public virtual ActionResult Detail()
{
ViewBag.Importan1 = "A";
ViewBag.Importan2 = "B";
return View();
}
}
that is it, you have Index and Detail with your desired behavior in all of your controllers which is inherited from MyController from example
public class Controller1 : MyController
{
public override ActionResult Index()
{
var result = base.Index();
//do some manipulation here
return result;
}
}
Another Solution
You can write this controller (I didn't check that on my pc)
public class MyController : Controller
{
protected override void OnActionExecuting(
ActionExecutingContext filterContext)
{
//write your custom code here
}
}
then inherit your controller from MyController
Another Solution
using filterActionAttribute and write your own filer action attribute

how can i set layout to null in action filter?

Is it posible to set the layout to null in action filter? For example
public ActionResult SomeAction()
{
return PartialView();
}
I want to render some action with
#Html.Action("someaction")
it works for now.
But i want to use this action in 2 modes : like child and like master for different situations. I can set Layout to null in view for this
view:
#{
if(condtition)
{
Layout = null;
}
}
But i want to find more elegant way :)
Like:
action without partial
public ActionResult SomeAction()
{
return View();
}
and in filter set the layout to null if action is child
public class LayoutFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if(filterContext.IsChildAction)
{
//set the layout to NULL here!
}
}
}
It is posible? Any ideas?
In your example you have overriden the OnActionExecuting event but this happens way too early. The actions hasn't even executed yet nor returned an ActionResult and you are already attempting to set its Layout.
Wait for it to complete, by overriding the OnActionExecuted event, retrieve the Result property from the filterContext and if it is a ViewResult set its MasterName property to null:
public class LayoutFilter : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var result = filterContext.Result as ViewResult;
if (result != null)
{
result.MasterName = null;
}
}
}

Resources