扩展NerdDinner:为ASP.NET MVC添加MEF和插件
[原文发表地址] Extending NerdDinner: Adding MEF and plugins to ASP.NET MVC
[原文发表时间] 2010-05-20 8:28 AM
原始的NerdDinner 范例程序是很简单的。事实上两个示例都很简单。或许它是新的Northwind,数据库,因为它是处理ASP.NET MVC的一个好途径。然而,对于用户们想要做的事情,NerdDinner 范例程序可能并不完美或理想化。
幸运的是,社会上有很多能人已经对NerdDinner产生过分歧,而且对其做过一些有趣的研究。这些示例通常针对一个特定的方案。所以大可不必与主干部分合并,但是尽管如此他们也是教育性的。
在更趋于社会化的方向下, Jon Galloway 和我也在NerdDinner中增添了一点东西。如今Jon的MVC Music Store对ASP.NET MVC 2. 算是一更好的“入门”示例我们针对有趣的事情上发布了一系列的帖子,这些事情被网络社区上的人添加到了NerdDinner上,同样地其中的一些事情几个月前我和Jon也添加了并且发布在了Mix 上,相信再过不久我和Jon就会在CodePlex上发布一款升级版的NerdDinner v2 (尽管它在源代码标签上已经有几个星期了),并带有许多补丁程序和新的性能。我们也会添加一些一次性的范例程序并把它们发布在CodePlex上。
我曾找微软工程师Hamilton Verissimo de Oliveira , 也有人叫他"Hammett"(你可能从Castle Project 和Monorailto听过这个人)谈过关于创建一个包含MEF(Managed Extensibility Framework)的范例程序,因为现在MEF 的大部分程序都已内置于.NET 4。他很乐意帮忙。所以我得以发布这篇博客,非常感谢Hammett的耐心的帮助。
NerdDinner on MEF
MEF 存在于System.ComponentModel.Composition. Hammett在他的范例程序中添加了很多有趣的东西,比如Microsoft.ComponentModel.Composition.Extensions 和 Microsoft.ComponentModel.Composition.Extensions.Web 对于一般的技术,在一些很好的扩展方法上创建命名空间,实现了IcontrollerFactory,同时还导出了HttpApplication.。
MefControllerFactory
记住,MEF使应用程序的处理变得简单。在这个范例程序中,Hammett创建了自己的MefControllerFactory,取代了默认controller factory配备的ASP.NET MVC. ASP.NET MVC能使它实现更好的转换:
1: protected override void Application_Start()
2: {
3: base.Application_Start();
4: ControllerBuilder.Current.SetControllerFactory(new MefControllerFactory(base.ScopeManager));
5:
6: RegisterRoutes(RouteTable.Routes);
7:
8: ViewEngines.Engines.Clear();
9: ViewEngines.Engines.Add(new MobileCapableWebFormViewEngine());
10: }
值得注意的是,Hammett的controller factory含有一个ScopeManager。这是一个web应用程序,有些组件可能属于Application Scope(先创建一次,再保留),有些则可能属于Request scope(每次请求时创建一个新的程序)
关于controllers,Hammett有效地重设了ASP.NET MVC's controller factory的默认行为。但在这个过程中,他提供了很多方法,通过在MefhttpApplication中重写CreateRootCatalog使我们能够以外来的方式跳转来改变行为。它的默认实现如下:
1: protected virtual ComposablePartCatalog CreateRootCatalog()
2: {
3: return new DirectoryCatalog(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
4: }
可能你也知道,ASP.NET MVC查找类通过常规的方法。通过末尾的"Controller"一词查找类。也是IController implmentations. 这里是以MEF的方法来声明使用微软ComponentModel.Composition.Extensions的惯列。注意scope的使用:
1: [assembly: Discovery(typeof(Conventions))]
2:
3: namespace NerdDinner
4: {
5: public class Conventions : ConventionDiscovery
6: {
7: public Conventions()
8: {
9: Add(PartRegistration.
10: Types(t => t.Name.EndsWith("Controller") && !t.IsAbstract).
11: Exporting((c, t) => c.
12: Contract(typeof (IController)).
13: Metadata("Name", t.Name.Substring(0, t.Name.Length - "controller".Length)).
14: Metadata("Mode", WebScopeMode.Request))
15: );
16: }
17: }
18: }
非常好。
Controllers 和它们的需求项
每当一个更高级的程序员看NerdDinner代码,他们通常会说,他们真的不喜欢这样的:
1: public DinnersController() : this(new DinnerRepository())
2: {
3: }
4:
5: public DinnersController(IDinnerRepository repository)
6: {
7: dinnerRepository = repository;
8: }
第二个构造函数运用IDinnerRepository接口,使我们能够做出不同的实现,但是默认的构造函数写着,“如果没给的情况下,这里有一个具体的实现。”这是一个大滑坡,通过添加默认实现我开始回避使用依赖注入测试控制器,同时使该控制器可测试,。但我尝试将我的controller通过直接依赖关系与DinnerRepository进行连接。有时候这也被称为(蹩脚的IoC),很多人也会说这样确实蛮蹩脚的。那只是诚实的争论罢了,但Hammett采取的立场是删除默认构造函数。
1: public class DinnersController : Controller
2: {
3: private IDinnerRepository dinnerRepository;
4:
5: public DinnersController(IDinnerRepository repository)
6: {
7: dinnerRepository = repository;
8: }
9: //...
10: }
那么DinnersController是怎样连接上IDinnerRepository的呢?并不需要担心controllers 该怎样工作,而关键是看它想不想连接。
MEF对于组件是一项有效的约会服务。 在这里,DinnerRepository表明是有空的,想要结识同样在"IDinnerRepository”的人。
1: [Export(typeof(IDinnerRepository))]
2: public class DinnerRepository : NerdDinner.Models.IDinnerRepository {
[Export(导出)]属性说:“我正在寻找配对,媒人,给我找个合适的吧!”每当controller向MEF发出请求时,它注意到在我们这个case中没有构造函数,然后会找到一个有效的构造函数,说道:“哇,DinnersController也在找寻配对啊,我知道你的风格。”然后它创建一个DinnerRepository,然后通知DinnersController构造函数传入。然后进行依赖注入。
你会经常发现其他的组件通过[Import(导入)]属性为它们的兴趣爱好打广告。但在这个例子中是没有必要用到的,因为所有的Controllers是通过MefControllerFactory创建的。它们不需要任何属性,因为它们已经走在约会服务的大道上。
MEFified的其他服务
最近在审查MVC Music Store Ayende(和在它之前的)被搁置的代码行。这些代码行实际上是创造性地配备了ASP.NET MVC 2内存不足,但并没有写到范例程序中,(尽管他们并没有被改掉), Phil可以在我不参与的情况下表明一些具体的决定,但是参与依赖注入的人都不喜欢这样。这个一个同样有效率的谋略跟上面提到的一样,只是写起来稍微有点不同而已。
1: public class AccountController : Controller
2: {
3: public AccountController()
4: : this(null, null) {
5: }
6: public AccountController(IFormsAuthentication formsAuth, IMembershipService service)
7: {
8: FormsAuth = formsAuth ?? new FormsAuthenticationService();
9: MembershipService = service ?? new AccountMembershipService();
10: }
11: //...
12: }
Hammett 的implementation仅用到了MEF,所以,
1: public AccountController(IFormsAuthentication formsAuth, IMembershipService service)
2: {
3: FormsAuth = formsAuth;
4: MembershipService = service;
5: }
再者,重点是能自动地找出依赖项。 [Export(typeof(IFormsAuthentication))] Export(typeof(IMembershipService))] 都分别标着“可以连接”属性状态。
总之,MEF对ASP.NET MVC应用程序来说,是一个很棒的添加功能。非常感谢Hammett的鼎力帮助 。