我们是否应该将应该位于Controller中的逻辑(例如用于渲染局部视图的数据)移动到ActionFilter?
例如,我正在建立一个CMS网站。应该有一个广告块要呈现在多个页面上,而不是所有页面上。我是否应该像 [ShowAd(categoryId)] 那样创建一个 ActionFilter 属性,并用该属性装饰动作方法?
此控制器的实现将包括服务调用,以从数据库,构建视图模型中检索信息并将其放入 ViewData 中。如果存在,将有一个 HtmlHelper 来使用ViewData中的数据呈现部分视图。
最佳答案
在我看来,这真令人讨厌。
当我试图确定是否需要ActionFilter时,我的第一个问题是,这是一个跨领域的关注点吗?首先,您的特定用例不适合这种情况。原因是,广告只是在页面上呈现的另一件事。没有什么特别的地方可以使它成为跨领域的。如果您在问题中将“广告”一词替换为“产品”,则所有相同的事实都是正确的。
就是这样,然后是关注点和可测试性的分离。一旦安装了ActionFilter
,您的控制器的可测试性如何?测试时,您还必须模拟掉其他东西,更糟糕的是,您必须对添加了ActionFilter
的每个控制器都模拟出那些依赖关系。
我问的第二个问题是:“如何以我使用的平台中最惯用的方式执行此操作?”
对于这个特殊的问题,听起来像RenderAction,而AdController是可行的方法。
原因如下:
RenderPartial
方法会带给您的地方)这样的野兽看起来像这样:
public AdController : Controller
{
//DI'd in
private AdRepository AdRepository;
[ChildActionOnly]
public ActionResult ShowAd(int categoryId)
{
Ad ad = Adrepository.GetAdByCategory(categoryId);
AdViewModel avm = new AdViewModel(ad);
return View(avm);
}
}
然后,您可以围绕此设置一个自定义的局部视图,并且无需在每个动作(或每个控制器)上放置过滤器,并且不必尝试在其中安装方形钉(动作过滤器)圆孔(动态视图)。
将广告添加到现有页面变得非常容易:
<% Html.RenderAction("ShowAd", "Ad" new { categoryId = Model.CategoryId }); %>