zoukankan      html  css  js  c++  java
  • 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有跨平台功能,但没有去进行验证,大家有兴趣可以去验证下

  • 相关阅读:
    <转>Npoi导入导出Excel操作<载>
    将DataTable导出为Excel C#
    错误 X “X1”不包含“XX2”的定义,并且找不到可接受类型为“X1”的第一个参数的扩展方法“XX2”(是否缺少 using 指令或程序集引用?)
    错误 1 未知的服务器标记“asp:ScriptManager”。
    分析器错误消息: 类型“test.test.testx”不明确: 它可能来自程序集“F: estProjectin est.test.DLL”或程序集“F: estProjectin estProject.DLL”。请在类型名称中显式指定程序集。
    There are no interfaces on which a capture can be done.
    基础知识系列☞GET和POST→及相关知识
    基础知识系列☞Abstract和Virtual→及相关知识
    同源策略
    26Mybatis_一级缓存及其测试
  • 原文地址:https://www.cnblogs.com/tiaoshuidenong/p/6768316.html
Copyright © 2011-2022 走看看