扩展NoteService,支持NancyFx
出于对DDD的喜爱,从很早就开始追Daxnet兄的博客,并期待着Apworks框架的成型。在最近发布的《在ASP.NET MVC Web API中使用Apworks开发第一个HTTP服务》一文中,我终于看到了Apworks框架的实际应用。马上下载下来把玩,出于学习的目的,以及对于Daxnet兄的贡献表示感激,我决定在他的基础上增加NancyFx的实现,脱离Web API(虽然微软总算想通走轻量化路线,搞出了Web API,不过我认为和其他框架相比还不够轻。。。),最近正好接触了NancyFx框架,看它的首页我就被吸引了,一句话就把Routing和Presenting搞定了,还可以脱离ASP.NET做Self Hosting...,还有文档齐全,移植方便等好处,真是甚合我意(如果您是一位缺乏耐心的朋友,事实上我也是。。。建议你看一下这个演示视频,相信你会对NancyFx有一个初步的认识)。好了,不多说了,赶紧操练起来吧。
创建新项目
首先新建一个ASP.NET Empty Web Application,如图所示(为了方便起见,这里还是先用ASP.NET作为底层支持):
确认后,我们会看到一个空的ASP.NET项目:
添加Nuget引用
通过在Reference处右击选择“Manage Nuget Packages”来安装Nuget包,我们需要安装的Nancy相关组件有Nancy
,Nancy.Bootstrapper.Unity
,Nancy.Hosting.Aspnet
,当然还有Apworks系列,这部分请参考原文,这里就不复述了。
创建领域模型
和Web API项目一样,我们需要一个领域模型,我们可以在项目下建立一个名为Model的文件夹,将领域模型放在里面。
创建DbContext
同样,将Web API项目里的DbContext和NoteServiceInitializer相关代码复制过来,让EntityFramework在这个项目里能正常工作。
创建NancyModule
好了,让我们来进入正题,作为Nancy Web App的核心,重要的指令都在这里集中,直接来看下面的代码:
using Nancy; namespace NoteServiceNancy { public class NoteModule : NancyModule { public NoteModule() { Get["/"] = _ => "Hello World"; } } }
这就是一个最简单的Module了,只需要添加using,并继承NancyModule这个基类,然后在构造里直接定义HTTP方法和路由即可,就是这么简单,如果你愿意,也可以抛弃MVC结构,事实上本例也是这么做的。
接下来就是完整的代码:
using System; using Apworks.Repositories; using Nancy; using NoteServiceNancy.Model; using Nancy.ModelBinding; using Nancy.Responses.Negotiation; namespace NoteServiceNancy { public class NoteModule : NancyModule { readonly IRepository<Note> _noteRepository; public NoteModule(IRepository<Note> noteRepository) { _noteRepository = noteRepository; Get["/"] = _ => "Hello world"; Get["api/notes/"] = AllNotesResponse; Get["api/notes/{id}"] = GetNoteByIDResponse; Post["api/notes/"] = AddResponse; Put["api/notes/{id}"] = UpdateResponse; Delete["api/notes/{id}"] = DeleteResponse; } private Negotiator AllNotesResponse(dynamic _) { var notes = _noteRepository.FindAll(); return Negotiate .WithStatusCode(HttpStatusCode.OK) .WithModel(notes); } private Negotiator GetNoteByIDResponse(dynamic parameters) { Guid id = parameters.id; var note = _noteRepository.GetByKey(id); return Negotiate .WithStatusCode(HttpStatusCode.OK) .WithModel(note); } private Negotiator AddResponse(dynamic _) { var note = this.Bind<Note>(); _noteRepository.Add(note); _noteRepository.Context.Commit(); return Negotiate .WithStatusCode(HttpStatusCode.OK) .WithModel(note); } private Negotiator UpdateResponse(dynamic parameters) { Guid id = parameters.id; var note = _noteRepository.GetByKey(id); var value = this.Bind<Note>(); note.Title = value.Title; note.Content = value.Content; note.CreatedDate = value.CreatedDate; note.Weather = value.Weather; _noteRepository.Update(note); _noteRepository.Context.Commit(); return Negotiate .WithStatusCode(HttpStatusCode.OK) .WithModel(note); } private Negotiator DeleteResponse(dynamic parameters) { Guid id = parameters.id; var note = _noteRepository.GetByKey(id); _noteRepository.Remove(note); _noteRepository.Context.Commit(); return Negotiate .WithStatusCode(HttpStatusCode.OK) .WithModel(note); } } }
简单到不需要注释。。。完全体现了.Net动态类型和表达式树的神奇魔法。不过这里要提一点,大家可能已经注意到类里有一个私有成员_noteRepository
,通过类的构造函数对其赋值,有经验的玩家可能已经懂了,肯定是要通过Ioc容器注入了嘛,您说的没错。但是怎么配置呢?问的好!这就需要Nancy的Bootstrapper机制帮忙了。
我感觉Nancy的Bootstrapper机制有点像ASP.Net项目里的Global.asax,继承了NancyBootstrapper类后可以重写基类的许多方法,比如ApplicationStartup
,因为我们使用了Unity作为Ioc容器,前文已经提到了,我们已经通过了Nuget加载了Nancy.Bootstrapper.Unity
这个包,所以我们已经满足了配置条件,可以干活了:
using System.Data.Entity; using Apworks.Application; using Apworks.Config.Fluent; using Apworks.Repositories; using Apworks.Repositories.EntityFramework; using Microsoft.Practices.Unity; using Nancy.Bootstrappers.Unity; namespace NoteServiceNancy { public class NoteBootstrapper : UnityNancyBootstrapper { protected override void ConfigureApplicationContainer(IUnityContainer existingContainer) { existingContainer.RegisterInstance(new NoteServiceDbContext(), new PerResolveLifetimeManager()) .RegisterType<IRepositoryContext, EntityFrameworkRepositoryContext>(new HierarchicalLifetimeManager(), new InjectionConstructor(new ResolvedParameter<NoteServiceDbContext>())) .RegisterType(typeof(IRepository<>), typeof(EntityFrameworkRepository<>)); } protected override void ApplicationStartup(IUnityContainer container, Nancy.Bootstrapper.IPipelines pipelines) { base.ApplicationStartup(container, pipelines); Database.SetInitializer(new NoteServiceInitializer()); AppRuntime.Instance .ConfigureApworks() .UsingUnityContainerWithDefaultSettings() .Create() .Start(); } } }
大家可以看到配置方法和Web API项目略有不同,只是把Global.asax的Application_Start
方法的内部实现拆成了两部分,把Unity注册的部分放到了ConfigureApplicationContainer
这个方法里,这都是Nancy为我们预制好的,这样看起来更清晰,不是吗?
完成
好了,没有了,就这么简单!大家可以在测试项目里找到相应的测试方法,这里我就偷个懒,不解释了,大家自己玩吧。
代码我已经上传至github,地址在此:https://github.com/FerminYang/NoteService