ASP.NET MVC - 在基础控制器中设置主页面的ViewData

28

我在我的ASP.NET MVC项目中使用了一个母版页。这个母版页希望在每个页面上都能显示一些ViewData。

如果在我的控制器中没有设置这个ViewData键,那么就会出现找不到的错误。但是,我不想在每个控制器中都设置ViewData(我不想在每个控制器中写ViewData["foo"] = GetFoo();)。

因此,我想在一个基础控制器中设置这个值,并让每个控制器都继承自这个基础控制器。在基础控制器的默认构造函数中,我设置了ViewData。我在这里找到了一个类似的方法:http://www.asp.net/learn/MVC/tutorial-13-cs.aspx。 到目前为止,这个方法可以正常工作...但问题是这个数据来自数据库。

现在当我想对我的控制器进行单元测试时,继承自基础控制器的控制器会调用它的默认构造函数。在默认构造函数中,我初始化了我的仓库类以从数据库中获取这个数据。结果是:我的单元测试失败了,因为它无法访问这些数据(而且我肯定不想让它们访问这些数据)。

我也不想把正确的Repository(或DataContext,无论你叫它什么)类传递给每个控制器,然后再传递给默认控制器,然后我可以使用我的单元测试模拟。控制器反过来依赖于其他仓库类,我将不得不传递多个参数给构造函数。对我来说太麻烦了,或者我错了吗?还有其他解决方法吗?

我尝试过使用StructureMap,但最终感觉那并不能解决我的问题,因为每个控制器仍然必须调用基础构造函数,这将初始化仓库类,所以我无法模拟它。

这个 是一个类似的问题,但我发现没有令人满意的答案。我能否以一种简单的方式解决它,也许使用StructureMap作为解决方案?或者我应该顶住压力,在每个控制器中传递一个Repository并再次将其传递给基础控制器?再说一遍,对于这么简单的东西来说,感觉需要做很多工作。谢谢!

1个回答

45

我看到有两个选项:

第一:

在 YourBaseController.OnActionExecuting() 或 YourBaseController.OnActionExecuted() 中设置 MasterPage 的 ViewData:

public class YourBaseController : Controller
{
    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // Optional: Work only for GET request
        if (filterContext.RequestContext.HttpContext.Request.RequestType != "GET")
            return;

        // Optional: Do not work with AjaxRequests
        if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
            return;

        ...

        filterContext.Controller.ViewData["foo"] = ...
    }
}

第二步:

或者创建自定义过滤器:

public class DataForMasterPageAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        // Optional: Work only for GET request
        if (filterContext.RequestContext.HttpContext.Request.RequestType != "GET")
            return;

        // Optional: Do not work with AjaxRequests
        if (filterContext.RequestContext.HttpContext.Request.IsAjaxRequest())
            return;

        ...

        filterContext.Controller.ViewData["foo"] = ...
    }
}

然后将其应用到您的控制器中:

[DataForMasterPage]
public class YourController : YourBaseController
{
    ...
}

我认为第二个解决方案正是适用于您的情况。


1
嗯,看起来很有前途。我现在在家,所以明天会测试一下,虽然我觉得第二个应该会起作用(祈祷中,已经尝试了太多小时各种方法)。 我喜欢它的AOP风格。 - Razzie
我在我的项目中使用了第二个解决方案,它运行顺畅。 - eu-ge-ne
1
@eu-ge-ne:在看到你的代码片段之前,我从未真正理解如何(或何时)使用ActionFilters。现在我可以想象出许多用途了。 :)我已经将我的实现从类似于你的“First”方法的架构切换到了使用ActionFilter的方法。它运行得非常好,并且一路上清理了我的基础控制器中的许多杂乱代码。感谢你提供的绝佳答案! - Luc
在操作级别上设置属性是否可行,还是我必须另外实现过滤器? - Nick Masao
2
是的,您可以在控制器级别或操作级别设置属性。 - eu-ge-ne
有没有一种方法可以将信息作为ViewModel传递回视图,即使控制器可能已经返回了自己的ViewModel?我尽量避免使用ViewData[],想知道是否有更简洁的方法来处理如果你要传回几个值的情况? - Andrew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接