扩展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

posted @ 2013-05-21 19:32 止.觀. 阅读(...) 评论(...) 编辑 收藏