回复:ASP.NET MVC Tip #31: 给 Master Pages 和 User Control...
Using an Action Filter所以让我们以另一种途径来解决这个传递数据给MasterPage或者view的问题。在这一节,我们创建一个ActionFilter来修改传递给view的ViewData。这个方法你可以通过给controller添加一个或者多个action filter来修改由controller传递给view的ViewData。
这个ActionFilter,命名为[Partial] ,如Listing 3所示。
Listing 3 – ActionFilters\PartialAttribute.cs

Code
using System;
using System.Reflection;
using System.Web.Mvc;
namespace Solution2.ActionFilters
{
public class PartialAttribute : ActionFilterAttribute
{
private string _partialClassName;
public PartialAttribute(string partialClassName)
{
_partialClassName = partialClassName;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var viewData = (filterContext.Controller as Controller).ViewData;
ExecutePartial(_partialClassName, viewData);
}
private void ExecutePartial(string partialName, ViewDataDictionary viewData)
{
// Get partial type
var partialType = Type.GetType(partialName, true, true);
var partial = Activator.CreateInstance(partialType);
// Execute all public methods
var methods = partialType.GetMethods();
foreach (MethodInfo method in methods)
{
var pams = method.GetParameters();
if (pams.Length > 0 && pams[0].ParameterType == typeof(ViewDataDictionary))
method.Invoke(partial, new object[] { viewData });
}
}
}
}
当你添加[Partial] 到一个controller action的时候,这个ation filter会附加一些数据到view data中去。例如,有可以使用[Partial] 特性来添加电影分类列表的数据到view data中去以便在master page中显示。你也可以使用[Partial] 特性来添加热门电影列表到view data 中以使在FeaturedMovie 用户控件中得到该数据。
[Partial] 特性通过一个类名,实例化这个类,然后执行类里面所有的public方法(每一个方法都包含一个ViewDataDictionary参数),Listing 4 中的controller说明了你可以怎样使用[Partial] action filter来为不同的controller返回不同的ViewData。
Listing 4 – HomeController.cs

Code
using System.Linq;
using System.Web.Mvc;
using Solution2.ActionFilters;
using Solution2.Models;
namespace Solution2.Controllers
{
[Partial("Solution2.Partials.Master")]
public class HomeController : Controller
{
[Partial("Solution2.Partials.Featured")]
public ActionResult Index()
{
return View();
}
public ActionResult Category(int id)
{
var dataContext = new MovieDataContext();
var movies = from m in dataContext.Movies where m.CategoryId == id select m;
return View("Category", movies);
}
}
}
注意到HomeController它本身是添加了[Partial] action filter的。由于[Partial] action filter应用到类上,在HomeController里面的每一个action执行的时候这个action filter都会执行的。在类级别上应用[Partial] 特性来为master page提供view data。
类级别上的[Partial]特性添加电影分类列表到view data中。[Partial]执行Solution2.Partials.Master 类中的方法,如Listing 5 所示。
Listing 5 – Master.cs

Code
using System.Linq;
using System.Web.Mvc;
using Solution2.Models;
namespace Solution2.Partials
{
public class Master
{
public void AddViewData(ViewDataDictionary viewData)
{
var dataContext = new MovieDataContext();
var categories = from c in dataContext.MovieCategories select c;
viewData["master"] = categories;
}
}
}
AddViewData() 方法将categories 添加到key为"master"的view data dictionary中。master page从view data 中取出categories 并显示。
[Partial] 也可以添加到特定的action上,例如Listing 4 中的Index()方法。
然而这种从controller中传递数据给母版页和用户控件的解决方案错在哪里呢?这种方法的优于前面一种方法的地方在于他将获取数据的逻辑放回到controller中来处理了。ViewData在controller action 调用的时候会被修改。
无论怎样,这个解决方案还是挺不错的。通过使用[Partial] 特性,你可以为view data dictionary 添加更多的数据。例如,如果你决定你要添加一个新的用户控件到某一个页面,而这个新的用户控件需要一个不同的数据集,你可以很简单的添加一个新的[Partial] 特性到正确的controller action上来添加新的数据到view data dictionary中去。
遗憾的是,只是一点点的遗憾,这个解决方案不是很容易进行单元测试。当你在一个单元测试里面执行action方法的时候ActionFilter并不会执行。所以,我们需要寻找一个不同的策略来解决这个问题。