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

前端 未结 2 1684
南笙
南笙 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:49

    I thought of having my controller inherit a class that pretty much does the same thing you see here, but I didn't see any great advantages to that.

    This is the way to go, in my opinion. You'd create a base Controller class that would provide this functionality. If you are familiar with the ASP.NET WebForms model then this is similar to creating a custom base Page class.

    As to the advantages of putting it in a base class, the main advantages are readability, maintainability and reusability. If you copy and paste the above method into each controller that needs it, you are going to have a more difficult time if, down the road, you need to add new information to the ViewData collection.

    In short, anytime you catch yourself copying and pasting code among classes or views in your application you should stop and think about how to put such logic in a single place. For more, read up on DRY - Don't Repeat Yourself.

    0 讨论(0)
  • 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") }) %>
    
    0 讨论(0)
提交回复
热议问题