我有几个控制器,我希望每个ActionResult都返回相同的viewdata。在这种情况下,我知道我将始终需要基本的产品和员工信息。
现在我一直在做这样的事情:
public ActionResult ProductBacklog(int id) {
PopulateGlobalData(id);
// do some other things
return View(StrongViewModel);
}
其中PopulateGlobalData()定义为:
public void PopulateGlobalData(int id) {
ViewData["employeeName"] = employeeRepo.Find(Thread.CurrentPrincipal.Identity.Name).First().FullName;
ViewData["productName"] = productRepo.Find(id).First().Name;
}
这只是伪代码,所以原谅任何明显的错误,有没有更好的方法来做到这一点?我想让我的控制器继承一个几乎与你在这里看到的相同的类,但我没有看到任何巨大的优势。感觉就像我正在做的那样是错误的和不可维护的,最好的方法是什么?
答案 0 :(得分:10)
您可以编写一个自定义action filter 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);
}
}
当然,使用基本视图模型和强类型视图会更加清晰:
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);
}
}
现在剩下的就是用这个属性来装饰你的基本控制器:
[GlobalDataInjector]
public abstract class BaseController: Controller
{ }
我个人更喜欢另一种更有趣的解决方案,涉及child actions。在这里,您可以定义一个处理此信息检索的控制器:
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);
}
}
现在剩下的就是include这个需要的地方(如果是全球的话可能是主页):
<%= Html.Action("Index", "GlobalData", new { id = Request["id"] }) %>
或者如果id是路线的一部分:
<%= Html.Action("Index", "GlobalData",
new { id = ViewContext.RouteData.GetRequiredString("id") }) %>
答案 1 :(得分:0)
我想让我的控制器继承一个几乎与你在这里看到的相同的类,但我没有看到任何巨大的优势。
在我看来,这是要走的路。您将创建一个提供此功能的基本Controller类。如果您熟悉ASP.NET WebForms模型,那么这类似于creating a custom base Page
class。
关于将其置于基类中的优势,主要优点是可读性,可维护性和可重用性。如果您将上述方法复制并粘贴到需要它的每个控制器中,那么如果您需要在ViewData
集合中添加新信息,则会遇到更困难的时间。
简而言之,只要您发现自己在应用程序中的类或视图之间复制和粘贴代码,就应该停下来思考如何将这些逻辑放在一个地方。有关更多信息,请阅读DRY - Don't Repeat Yourself。