Best way to do global viewdata in an area of my ASP.NET MVC site?

前端 未结 2 1686
南笙
南笙 2021-02-09 01:00

I have an several controllers where I want every ActionResult to return the same viewdata. In this case, I know I will always need basic product and employee information.

<
2条回答
  •  南笙
    南笙 (楼主)
    2021-02-09 01:56

    You could write a custom action filter attribute which will fetch this data and store it in the view model on each action/controller decorated with this attribute.

    public class GlobalDataInjectorAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            string id = filterContext.HttpContext.Request["id"];
            // TODO: use the id and fetch data
            filterContext.Controller.ViewData["employeeName"] = employeeName;
            filterContext.Controller.ViewData["productName"] = productName;
            base.OnActionExecuted(filterContext);
        }
    }
    

    Of course it would much cleaner to use a base view model and strongly typed views:

    public class GlobalDataInjectorAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            string id = filterContext.HttpContext.Request["id"];
            // TODO: use the id and fetch data
            var model = filterContext.Controller.ViewData.Model as BaseViewModel;
            if (model != null)
            {
                model.EmployeeName = employeeName;
                model.ProductName = productName;
            }
            base.OnActionExecuted(filterContext);
        }
    }
    

    Now all that's left is to is to decorate your base controller with this attribute:

    [GlobalDataInjector]
    public abstract class BaseController: Controller
    { }
    

    There's another more interesting solution which I personally prefer and which involves child actions. Here you define a controller which handles the retrieval of this information:

    public class GlobalDataController: Index
    {
        private readonly IEmployeesRepository _employeesRepository;
        private readonly IProductsRepository _productsRepository;
        public GlobalDataController(
            IEmployeesRepository employeesRepository,
            IProductsRepository productsRepository
        )
        {
            // usual constructor DI stuff
            _employeesRepository = employeesRepository;
            _productsRepository = productsRepository;
        }
    
        [ChildActionOnly]
        public ActionResult Index(int id)
        {
            var model = new MyViewModel
            {
                EmployeeName = _employeesRepository.Find(Thread.CurrentPrincipal.Identity.Name).First().FullName,
                ProductName = _productsRepository.Find(id).First().Name;
            };
            return View(model);
        }
    }
    

    And now all that's left is to include this wherever needed (probably the master page if global):

    <%= Html.Action("Index", "GlobalData", new { id = Request["id"] }) %>
    

    or if the id is part of the routes:

    <%= Html.Action("Index", "GlobalData", 
        new { id = ViewContext.RouteData.GetRequiredString("id") }) %>
    

提交回复
热议问题