How to ensure specific shared logic is fired before anything else in an MVC web app?
I currently have a partial view that is called within my _Layout.cshtml, and is r开发者_JAVA百科endered throughout my whole MVC web application. It's the first item at the top of the page to be rendered, and within the ActionResult method that is responsible for it, I have a call to my service to perform some business logic.
I originally put this service call in this particular ActionResult method as, still being new to MVC, I was falsely under the impression that it would get fired BEFORE any of the main page content, for each page across the site. However, I've just realised that the main view method is being called first, and this ChildActionOnly Method (for the Partial View) is being called afterwards.
Basically, it's essential that the business logic call that Im currently making within the ChildActionOnly Method (associated with my partial view) is made BEFORE any other page processing is carried out (as I need to perform some DB updates upon each page load, which are then reflected in each requested page).
I'm happy to split this business logic out from the current partial views actionmethod, but am unsure where it should be going, or how I should be ensuring that it's called before anything else, for each page request? What would be the norm approach?
You need a global action filter (luckily available starting MVC3).
See an example here:
http://weblogs.asp.net/gunnarpeipman/archive/2010/08/15/asp-net-mvc-3-global-action-filters.aspx
Copying from the sample (with slight modifications):
public class MyActionFilterAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext context)
{
// Your logic can come here
base.OnResultExecuting(filterContext);
// Your logic can come here
}
}
Applying in global.asax.cs (from the article):
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
// Register global filter
GlobalFilters.Filters.Add(new MyActionFilterAttribute());
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
How about overriding the OnResultExecuting
method of your controllers? If you've got a controller base class do it in there - otherwise inject derive a new Controller
class and make all your existing controllers inherit from that (this is good practise anyway).
public class MyControllerBase : Controller
{
protected override void OnResultExecuting(ResultExecutingContext filterContext)
{
//filterContext contains the Result (e.g. ViewResult) plus route data etc.
//you can also analyse Model and ViewData in here too.
}
}
What you are describing sounds like an aspect of your application. For this MVC has a feature called Action Filters.
You could implement an ActionFilterAttribute like this:
public class LogActionAttribute : ActionFilterAttribute
{
private ILog _log = new SomeLogger();
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var type = filterContext.Controller.GetType();
_log.Debug("Executing action '{0}' on controller '{1}'", filterContext.ActionDescriptor.ActionName, type.Name);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var type = filterContext.Controller.GetType();
_log.Debug("Executed action '{0}' on controller '{1}'", filterContext.ActionDescriptor.ActionName, type.Name);
}
}
You can apply this to exactly the actions you want or as what is called "global action filters" if you want it to apply to all actions.
More on filters.
精彩评论