Background

近段时间一直有Github网友联系我, 咨询关于Nancy支持Hot-plugging代码补丁的事情,于是便有了此篇博客。

Recently, someone contacted me for help on making Nancy support Hot-plugging; so I decided to write this post to share with you guys.

去年,我有用到微软的技术栈做一些东西,其中用到了开源的Nancy框架。可惜的是它并不支持Hot-plugging,鉴于此,我阅读它的源码之后,做了一些简单的代码补丁已达到支持热插拔的目的。

Last year I used Microsoft's technology stack to build some projects, which are based on open source framework of Nancy. Unfortunately Nancy does not support Hot-plugging; in view of this, I research the source code of Nancy Core and wrote the code patch below to support module Hot-plugging.

注意:本补丁并未在生产环境中使用,仅做了热插拔测试,步骤很简单仅用英文描述。
NOTE: The Hot-plugging test has passed, but the code patch has not been put into the production environment.

Code Patch

SETP 1

File IRouteCache.cs

src/Nancy/Routing/IRouteCache.cs

Add new method

    void Refresh ();

File RouteCache.cs

/src/Nancy/Routing/RouteCache.cs

Change RouteCache : Dictionary to ConcurrentDictionary;
Add new lines to class

    private readonly INancyContextFactory contextFactory;
    private readonly INancyModuleCatalog moduleCatalog;

Add new lines to line 1 of class constructor

    this.contextFactory = contextFactory;
    this.moduleCatalog = moduleCatalog;

Move the code of line 39-44 to new method

    public void Refresh ()
    {
        var request = new Request ("GET", "/", "http");
        using (var context = contextFactory.Create (request)) {
            this.BuildCache (moduleCatalog.GetAllModules (context));
        }
    } 

SETP 2

File IRouteResolver.cs

/src/Nancy/Routing/IRouteResolver.cs

Add new method

    void RebuildTireAndCache ();

File DefaultRouteResolver.cs

src/Nancy/Routing/DefaultRouteResolver.cs

Add new method

    public void RebuildTireAndCache ()
    {
        routeCache.Refresh ();
        this.BuildTrie ();
    }

SETP 3

File IRequestDispatcher.cs

/src/Nancy/Routing/IRequestDispatcher.cs

Add new method

    void Update ();

File DefaultRequestDispatcher.cs

src/Nancy/Routing/DefaultRequestDispatcher.cs

Add new method

    public void Update ()
    {
        routeResolver.RebuildTireAndCache ();
    }

SETP 4

File INancyBootstrapper.cs

src/Nancy/Bootstrapper/INancyBootstrapper.cs

Add new method

    void RegisterModule (Type moduleType);

File NancyBootstrapperBase.cs

src/Nancy/Bootstrapper/NancyBootstrapperBase.cs

Add new method

    public abstract void RegisterModule (Type moduleType);

File DefaultNancyBootstrapper.cs

/src/Nancy/DefaultNancyBootstrapper.cs

Add new method

    public override void RegisterModule (Type moduleType)
    {
        //  var modules = AppDomainAssemblyTypeScanner
        //      .TypesOf<INancyModule> (ScanMode.ExcludeNancy)
        //      .NotOfType<DiagnosticModule> ()
        //      .Select (t => new ModuleRegistration (t))
        //      .Where (c => !Modules.Contains ())
        //      .ToArray ();
        var existingModule = Modules.Any (c => c.ModuleType == moduleType);
        if (existingModule) {
            throw new ArgumentException ("Duplicate NancyModule.");
        }
        var mr = new ModuleRegistration (moduleType);
        var newModules = Modules.ToList ();
        newModules.Add (mr);
        RegisterModules (this.Container, newModules);
        RegisterRequestContainerModules (this.Container, new[] { mr });
    }

SETP 5

File INancyEngine.cs

/src/Nancy/INancyEngine.cs Add new method

    void Update ();

File NancyEngine.cs

/src/Nancy/NancyEngine.cs Add new method

    public void Update ()
    {
        dispatcher.Update ();
    }

SETP 6

Add the code below to your NancyHost, such as Nancy.Hosting.Self

    public void AddModuleFromAssemblyPath (string moduleAssemblyPath)
    {
        var moduleAssembly = Assembly.LoadFrom (moduleAssemblyPath);
        AppDomainAssemblyTypeScanner.AddAssembliesToScan (moduleAssembly);
        var modules = moduleAssembly.GetExportedTypes ().Where (c => c.IsSubclassOf (typeof(NancyModule)) || c.IsAssignableFrom (typeof(NancyModule)));
        foreach (var module in modules) 
        {
            this.bootstrapper.RegisterModule (module);
            this.engine.Update ();
        }
    }

HOW TO USE?

yourHost.AddModuleFromAssemblyPath(yourNewModuleAssemblyPath);