通过《ASP.NET Web API的Controller是如何被创建的?》的介绍我们知道默认ASP.NET Web API在Self Host寄宿模式下用于解析程序集的AssembliesResolver是一个DefaultAssembliesResolver对象,它只会提供当前应用程序域已经加载的程序集。如果我们将HttpController定义在非寄宿程序所在的程序集中(实际上在采用Self Host寄宿模式下,我们基本上都会选择在独立的项目定义HttpController类型),即使我们将它们部属在宿主程序运行的目录中,宿主程序启动的时候也不会主动去加载这些程序集。由于当前应用程序域中并不曾加载这些程序集,HttpController类型解析将会失败,HttpController的激活自然就无法实现。[本文已经同步到《How ASP.NET Web API Works?》]

我们可以通过一个简单的实例来证实这个问题。我们在一个解决方案中定义了如右图所示的4个项目,其中Foo、Bar和Baz为类库项目,相应的HttpController类型就定义在这3个项目之中。Hosting是一个作为宿主的控制台程序,它具有对上述3个项目的引用。我们分别在项目Foo、Bar和Baz中定义了三个继承自ApiController的HttpController类型FooController、BarController和BazController。如下面的代码片断所示,我们在这3个HttpController类型中定义了唯一的Action方法Get并让它返回当前HttpController类型的AssemblyQualifiedName。

   1: public class FooController : ApiController

   2: {

   3:     public string Get()

   4:     {

   5:         return this.GetType().AssemblyQualifiedName;

   6:     }

   7: }

   8:  

   9: public class BarController : ApiController

  10: {

  11:     public string Get()

  12:     {

  13:         return this.GetType().AssemblyQualifiedName;

  14:     }

  15: }

  16:  

  17: public class BarController : ApiController

  18: {

  19:     public string Get()

  20:     {

  21:         return this.GetType().AssemblyQualifiedName;

  22:     }

  23: }

我们在作为宿主的Hosting程序中利用如下的代码以Self Host模式实现了针对Web API的寄宿。我们针对基地址“http://127.0.0.1:3721”创建了一个HttpSelfHostServer,在开启之前我们注册了一个URL模板为“api/{controller}/{id}”的路由。

   1: class Program

   2: {

   3:     static void Main(string[] args)

   4:     {

   5:         Uri baseAddress = new Uri("http://127.0.0.1:3721");

   6:         using (HttpSelfHostServer httpServer = new HttpSelfHostServer(new HttpSelfHostConfiguration(baseAddress)))

   7:         {

   8:             httpServer.Configuration.Routes.MapHttpRoute(

   9:                 name             : "DefaultApi",

  10:                 routeTemplate    : "api/{controller}/{id}",

  11:                 defaults         : new { id = RouteParameter.Optional });

  12:  

  13:             httpServer.OpenAsync().Wait();

  14:             Console.Read();

  15:         }

  16:     }

  17: }

在启动宿主程序后,我们试图通过浏览器对分别定义在FooController、BarController和BazController中的Action方法Get发起调用,不幸的是我们会得到如图4-4所示的结果。从显示在浏览器中的消息我们很清楚问题的症结所在:根据路由解析得到HttpController名称并不能得到匹配的类型。

导致上述这个问题的原因我们在上面已经分析过了:默认注册的DefaultAssembliesResolver仅仅提供当前应用程序域加载的程序集。我们可以通过自定义的AssembliesResolver来解决这个问题。我们的解决思路是让需要预先加载的程序集可配置,具体来说可以采用具有如下结构的配置来设置需要预先加载的程序集。

   1: <configuration>

   2:    <configSections>

   3:      <section name="preLoadedAssemblies" 

   4:               type="Hosting.PreLoadedAssembliesSettings, Hosting"/>

   5:    </configSections>

   6: <preLoadedAssemblies>

   7:   <add assemblyName ="Foo.dll"/>

   8:   <add assemblyName ="Bar.dll"/>

   9:   <add assemblyName ="Baz.dll"/>

  10: </preLoadedAssemblies>

  11: </configuration>

在创建自定义的AssembliesResolver之前我们先得为这段配置定义相应的配置节和配置元素类型。相关的类型(PreLoadedAssembliesSettings、AssemblyElementCollection和AssemblyElement)定义如下所示,由于配置结构比较简单,在这里我们不对它们作详细介绍了。

   1: public class PreLoadedAssembliesSettings: ConfigurationSection

   2: {

   3:     [ConfigurationProperty("", IsDefaultCollection = true)]

   4:     public AssemblyElementCollection AssemblyNames

   5:     {

   6:         get { return (AssemblyElementCollection)this[""]; }

   7:     }

   8:  

   9:     public static PreLoadedAssembliesSettings GetSection()

  10:     {

  11:         return ConfigurationManager.GetSection("preLoadedAssemblies") 

  12:             as PreLoadedAssembliesSettings;

  13:     }

  14: }

  15:  

  16: public class AssemblyElementCollection : ConfigurationElementCollection

  17: {

  18:     protected override ConfigurationElement CreateNewElement()

  19:     {

  20:         return new AssemblyElement();

  21:     }

  22:     protected override object GetElementKey(ConfigurationElement element)

  23:     {

  24:         AssemblyElement serviceTypeElement = (AssemblyElement)element;

  25:         return serviceTypeElement.AssemblyName;

  26:     }

  27: }

  28:     

  29: public class AssemblyElement : ConfigurationElement

  30: {

  31:     [ConfigurationProperty("assemblyName", IsRequired = true)]

  32:     public string AssemblyName

  33:     {

  34:         get { return (string)this["assemblyName"]; }

  35:         set { this["assemblyName"] = value; }

  36:     }

  37: }

由于我们自定义的AssembliesResolver是对现有DefaultAssembliesResolver的扩展(尽管其程序集提供机制仅仅通过一句代码来实现),我们将类型命名为ExtendedDefaultAssembliesResolver。如下面的代码片断所示,ExtendedDefaultAssembliesResolver继承自DefaultAssembliesResolver,在重写的GetAssemblies方法中我们先通过分析上述的配置并主动加载尚未加载的程序集,然后调用基类的同名方法来提供最终的程序集。

   1: public class ExtendedDefaultAssembliesResolver : DefaultAssembliesResolver

   2: {

   3:     public override ICollection<Assembly> GetAssemblies()

   4:     {

   5:         PreLoadedAssembliesSettings settings = PreLoadedAssembliesSettings.GetSection();

   6:         if (null != settings)

   7:         {

   8:             foreach (AssemblyElement element in settings.AssemblyNames)

   9:             {

  10:                 AssemblyName assemblyName = AssemblyName.GetAssemblyName(element.AssemblyName);

  11:                 if(!AppDomain.CurrentDomain.GetAssemblies().Any(assembly=>AssemblyName.ReferenceMatchesDefinition(assembly.GetName(),assemblyName)))

  12:                 {

  13:                     AppDomain.CurrentDomain.Load(assemblyName);

  14:                 }

  15:             }

  16:         }

  17:         return base.GetAssemblies();

  18:     }

  19: }

我们在作为宿主的Hosting程序中利用如下的代码将一个ExtendedDefaultAssembliesResolver对象注册到当前HttpConfiguration的ServicesContainer上。

   1: class Program

   2: {

   3:     static void Main(string[] args)

   4:     {

   5:         Uri baseAddress = new Uri("http://127.0.0.1:3721");

   6:         using (HttpSelfHostServer httpServer = new HttpSelfHostServer(new HttpSelfHostConfiguration(baseAddress)))

   7:         {

   8:             httpServer.Configuration.Services.Replace(typeof(IAssembliesResolver),new ExtendedDefaultAssembliesResolver());

   9:             //其他操作

  10:         }

  11:     }

  12: }

重新启动宿主程序后再次在浏览器输入对应的地址来访问分别定义在FooController、BarController和BazController中的Action方法Get,我们会得到如下图所示的输出结果,这正是目标Action方法执行的结果。

[ASP.NET Web API]如何Host定义在独立程序集中的Controller的更多相关文章

  1. asp.net web api 2 host in a windows service推荐阅读

    最简单的例子(官方)在控制台app里面运行: http://www.asp.net/web-api/overview/hosting-aspnet-web-api/use-owin-to-self-h ...

  2. How ASP.NET Web API 2.0 Works?[持续更新中…]

    一.概述 RESTful Web API [Web标准篇]RESTful Web API [设计篇] 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用 二.路由 ...

  3. 【ASP.NET Web API教程】3.2 通过.NET客户端调用Web API(C#)

    原文:[ASP.NET Web API教程]3.2 通过.NET客户端调用Web API(C#) 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的 ...

  4. 【ASP.NET Web API教程】3.3 通过WPF应用程序调用Web API(C#)

    原文:[ASP.NET Web API教程]3.3 通过WPF应用程序调用Web API(C#) 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本博客文章,请先看前面的 ...

  5. Self-Host c#学习笔记之Application.DoEvents应用 不用IIS也能執行ASP.NET Web API

    Self-Host   寄宿Web API 不一定需要IIS 的支持,我们可以采用Self Host 的方式使用任意类型的应用程序(控制台.Windows Forms 应用.WPF 应用甚至是Wind ...

  6. 打造属于自己的支持版本迭代的Asp.Net Web Api Route

    在目前的主流架构中,我们越来越多的看到web Api的存在,小巧,灵活,基于Http协议,使它在越来越多的微服务项目或者移动项目充当很好的service endpoint. 问题 以Asp.Net W ...

  7. ASP.NET Web API路由系统:Web Host下的URL路由

    ASP.NET Web API提供了一个独立于执行环境的抽象化的HTTP请求处理管道,而ASP.NET Web API自身的路由系统也不依赖于ASP.NET路由系统,所以它可以采用不同的寄宿方式运行于 ...

  8. Self Host模式下的ASP. NET Web API是如何进行请求的监听与处理的?

    构成ASP.NET Web API核心框架的消息处理管道既不关心请求消息来源于何处,也不需要考虑响应消息归于何方.当我们采用Web Host模式将一个ASP.NET应用作为目标Web API的宿主时, ...

  9. ASP.NET Web API消息处理管道:Self Host下的消息处理管道[下篇]

    ASP.NET Web API消息处理管道:Self Host下的消息处理管道[下篇] 我们知道ASP.NET Web API借助于HttpSelfHostServer以Self Host模式寄宿于当 ...

随机推荐

  1. Zookeeper操作

    Zookeeper操作 注意搭建: 1.集群规模不小于3个节点 2.服务器之间系统时间要保持一致 1.搭建步骤: 1.解压安装包 2.设置zookeeper环境变量 3.修改配置文件————zoo.c ...

  2. webrtc中APM(AudioProcessing module)的使用2

    这个其实就是从Audio_processing.h中拿出来的. APM should be placed in the signal chain as close to the audio hardw ...

  3. 消息队列之ZeroMQ(C++)

    ZMQ是什么? 这是个类似于Socket的一系列接口,他跟Socket的区别是:普通 的socket是端到端的(1:1的关系),而ZMQ却是可以N:M 的关系,人们对BSD套接字的了解较多的是点对点的 ...

  4. vs2012 发布web应用程序

    Visual Studio 2012 Visual Studio Express 2012 for Web 与 的Visual Studio 2010  Visual Studio Web发布更新 与 ...

  5. install hdp 2.2 on ubuntu 14.04

    http://www.swiss-scalability.com/2014/12/install-hdp-22-on-ubuntu-1404-trusty.html 在新加节点上运行 sed -e & ...

  6. 测试--jmeter的使用

    jmeter用于压力测试 首先我们要区别压力和攻击,当设立了不适当的线程数量和准备时长,就容易造成攻击. 线程数:虚拟用户数.一个虚拟用户占用一个进程或线程.设置多少虚拟用户数在这里也就是设置多少个线 ...

  7. C# 中对 ArrayList 的排序

    ArrayList 元素 //目录条目类 public class FolderItem { public string filename; public string filetype; publi ...

  8. Python中的map( )和reduce( )

      1.变量可以指向函数,也可以使用变量和参数的形式完成函数调用.   2.那么函数名是什么呢?函数名其实就是指向函数的变量!对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算 ...

  9. Ubuntu Java 环境变量

    方法1:修改/etc/profile 文件所有用户的 shell都有权使用这些环境变量<1>在 shell终端执行命令:vi /etc/profile<2>在 profile文 ...

  10. html+css笔记

    文档结构 1.html文档结构 ①文档类型声明 严格型(标准模式):    <!DOCTYpE HTML>   HTML5 XHTML 1.0:<!DOCTYpE html pUbL ...