Web API 之SelfHost與OwinSelfHots加載外部程序


   下面就一些web api的一些基礎內容進行闡述,然后就web api宿主承載中的實際業務問題進行解決
HttpController
     HttpController的激活是由處於消息處理管道尾端的HttpRoutingDispatcher來完成的,具體來說是HttpRoutingDispatcher利用HttpControllerDispatcher實現了針對目標HttpController的激活和執行。激活目標HttpController的前提是能夠正確解析出HttpController的真實類型,而類型解析需要針對加載的程序集,所以我們需要先來了解一個用於解析程序集的對象AssembliesResolver。在ASP.NET Web API的HttpController激活系統中,AssembliesResolver為目標HttpController的類型解析提供候選的程序集。換句話說,候選HttpController類型的選擇范圍僅限於定義在通過AssembliesResolver提供的程序集中的所有實現了IHttpController接口的類型
AssembliesResolver
     所有的AssembliesResolver均實現了IAssembliesResolver接口。根據程序反射得到如下代碼片段,根據代碼片段得知,IAssembliesResolver提供的是程序集列表
  
public interface IAssembliesResolver
{
    ICollection<Assembly> GetAssemblies();
}
   
DefaultAssembliesResolver
     默認的AssembliesResolver為DefaultAssembliesResolver,根據以下代碼片段得知,默認返回的是當前應用程序域的程序集
  
public class DefaultAssembliesResolver : IAssembliesResolver
{
    public virtual ICollection<Assembly> GetAssemblies()
    {
        return AppDomain.CurrentDomain.GetAssemblies().ToList<Assembly>();
    }
}  
 
ServicesContainer
      默認的AssembliesResolver就是通過ServicesContainer類型確定
      Web Api的請求相當於一個管道,類似於流水線作業,每個環節都會注冊自己的實現類組件來完成自己的工作。這些組件都會實現自己特定的接口,當然在預置的組件無法滿足我們的業務需求時,可以自己來實現組件代碼,並進行注冊,ServicesContainer其實可以簡單的理解為這些組件的一個IOC容器
 
     上面所介紹的就代表着,如果采用selfHost承載web api,而且web api與宿主程序不在同一程序集中的情況,如果在用上篇的代碼, 將會發現報以下錯誤  No type was found that matches the controller named 'User'.
    上面所說的情況,分兩種,第一種,單個api程序集,多個api程序集
    1.如果api程序集全部寫在一個單獨的程序集中的情況
    1.1 直接通過Assembly加載外部程序集
        
        static void Main(string[] args)
        {
            // 如果 API 處於外部程序集,可通過以下代碼加載 CM.API為其他程序集
            //Assembly.Load("CM.API, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");    //加載外部程序集
            string baseAddress = "http://localhost:9000/";           
            // 啟動 OWIN host
            WebApp.Start<Startup>(url: baseAddress);
            Console.WriteLine("程序已啟動,按任意鍵退出");
            Console.ReadLine();
        }

        宿主項目還是需要引用CM.API程序集

       1.2通過自定義AssembliesResolber來進行注冊      
         
    public class UserResolver: DefaultAssembliesResolver
    {
        public override ICollection<Assembly> GetAssemblies()
        {
            ICollection<Assembly> baseAssemblies = base.GetAssemblies();
            List<Assembly> assemblies = new List<Assembly>(baseAssemblies);
            var assembly = AppDomain.CurrentDomain.BaseDirectory;
            string path = assembly + "\\" + "CM.API.dll";
            var controllersAssembly = Assembly.LoadFrom(path);
            baseAssemblies.Add(controllersAssembly);
            return assemblies;
        }
    }
         然后宿主啟動時進行注冊        
         
        static void Main(string[] args)
        {           
            var config = new HttpSelfHostConfiguration("http://localhost:8083");
            config.Routes.MapHttpRoute(
                "API Default", "api/{controller}/{id}",
                new { id = RouteParameter.Optional });
            //注冊UserResolver,加載程序集
            config.Services.Replace(typeof(IAssembliesResolver), new UserResolver());
            using (var server = new HttpSelfHostServer(config))
            {
                server.OpenAsync().Wait();
                Console.WriteLine("Press Enter to quit.");
                Console.ReadLine();
            }
        }
      2.當然在業務開發中如果API被分散開發到多個程序集中,這種情況當然根據項目框架區分,各有利弊
      2.1 通過上述的UserServoler進行加載      
    public class UserResolver: DefaultAssembliesResolver
    {
        public override ICollection<Assembly> GetAssemblies()
        {
            ICollection<Assembly> baseAssemblies = base.GetAssemblies();
            List<Assembly> assemblies = new List<Assembly>(baseAssemblies);
            var assembly = AppDomain.CurrentDomain.BaseDirectory;
            string path = assembly + "\\" + "CM.API.dll";
            var controllersAssembly = Assembly.LoadFrom(path);
            baseAssemblies.Add(controllersAssembly);
            //加載多個程序集   不建議寫死
            string path2 = assembly + "\\" + "CM.API2.dll";
            var controllersAssembly2 = Assembly.LoadFrom(path2);
            baseAssemblies.Add(controllersAssembly2);            
            return assemblies;
        }
    }
       上述代碼只是展示加載的一種方式,只是得到程序集的方式需要去進行配置編碼,不建議直接寫死在代碼里
       2.2 通過自定配置類,進行配置文件讀取加載
            添加自定義配置類        
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Configuration;
using System.Reflection;
 
namespace CM.SelfHost
{
    public class AssembliesLoad : ConfigurationSection
    {
        [ConfigurationProperty("", IsDefaultCollection = true)]
        public AssemblyElementCollection AssemblyNames
        {
            get { return (AssemblyElementCollection)this[""]; }
        }
 
        public static AssembliesLoad GetSection()
        {
            return ConfigurationManager.GetSection("AssembliesLoad") as AssembliesLoad;
        }
    }
    public class AssemblyElementCollection : ConfigurationElementCollection
    {
        protected override ConfigurationElement CreateNewElement()
        {
            return new AssemblyElement();
        }
        protected override object GetElementKey(ConfigurationElement element)
        {
            AssemblyElement serviceTypeElement = (AssemblyElement)element;
            return serviceTypeElement.AssemblyName;
        }
    }
 
    public class AssemblyElement : ConfigurationElement
    {
        [ConfigurationProperty("assemblyName", IsRequired = true)]
        public string AssemblyName
        {
            get { return (string)this["assemblyName"]; }
            set { this["assemblyName"] = value; }
        }
    }
} 
        添加配置文件,如下,加入ConfigSections節點以及AssembliesLoad節點
       
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <configSections>
    <section name="AssembliesLoad" type="CM.SelfHost.AssembliesLoad,CM.SelfHost"/>
  </configSections>
  <AssembliesLoad>
    <add assemblyName="CM.API.dll" />
  </AssembliesLoad>
</configuration>
       添加AssemblyResolver類,並注冊
     
    public class WebApiResolver: DefaultAssembliesResolver
    {
        public override ICollection<Assembly> GetAssemblies()
        {
            AssembliesLoad settings = AssembliesLoad.GetSection();
            if (null != settings)
            {
                foreach (AssemblyElement element in settings.AssemblyNames)
                {
                    AssemblyName assemblyName = AssemblyName.GetAssemblyName(element.AssemblyName);
                    if (!AppDomain.CurrentDomain.GetAssemblies().Any(assembly =>   
                        AssemblyName.ReferenceMatchesDefinition(assembly.GetName(), assemblyName)))
                    {
                        AppDomain.CurrentDomain.Load(assemblyName);
                    }
                }
            }
            return base.GetAssemblies();
        }
    }
      注冊Resolver         
        static void Main(string[] args)
        {
            //Assembly.Load("CM.API, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");    //加載外部程序集
            var config = new HttpSelfHostConfiguration("http://localhost:8083");
            config.Routes.MapHttpRoute(
                "API Default", "api/{controller}/{id}",
                new { id = RouteParameter.Optional });
            config.Services.Replace(typeof(IAssembliesResolver), new UserResolver());
            using (var server = new HttpSelfHostServer(config))
            {
                server.OpenAsync().Wait();
                Console.WriteLine("Press Enter to quit.");
                Console.ReadLine();
            }
        }
      運行會報錯  其他信息: 配置系統未能初始化
      查閱一些說明之后發現,如果存在自定義配置ConfigSections節點,那么必須在第一個節點,修改app.confg如下
     
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="AssembliesLoad" type="CM.SelfHost.AssembliesLoad,CM.SelfHost"/>
  </configSections>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <AssembliesLoad>
    <add assemblyName="CM.API.dll" />
  </AssembliesLoad>
</configuration>

  至此,web api的承載方式就已經闡述完畢,說一下個人傾向,通過兩篇文章對承載的了解,我喜歡使用OwinselfHost承載,並采用自定義配置文件的方式加載外部api程序集,Owin有跨平台功能,但沒有去進行驗證,大家有興趣可以去驗證下


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM