我们是否应该将应该位于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 }); %>
    

    08-26 19:13