OSharp是什么?

  OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现。与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现。依赖注入、ORM、对象映射、日志、缓存等等功能,都只定义了一套最基础最通用的抽象封装,提供了一套统一的API、约定与规则,并定义了部分执行流程,主要是让项目在一定的规范下进行开发。所有的功能实现端,都是通过现有的成熟的第三方组件来实现的,除了EntityFramework之外,所有的第三方实现都可以轻松的替换成另一种第三方实现,OSharp框架正是要起隔离作用,保证这种变更不会对业务代码造成影响,使用统一的API来进行业务实现,解除与第三方实现的耦合,保持业务代码的规范与稳定。

本文已同步到系列目录:OSharp快速开发框架解说系列

框架初始化

  相对于OSharp 3.0,3.3版本最大的更新,就是从框架级别定义了初始化流程,对初始化功能进行了抽象与封装,不依赖于第三方实现,第三方实现仅作为可替换的服务实现方案存在。

  例如,依赖注入功能中,接口与其实现类的映射配置,对象容器的构建,对象的解析获取,都将通过框架定义的API来完成,而Autofac,仅作为这些功能的实现方存在,如果不想使用Autofac,则可以很方便的切换成别的IoC组件。

  具体的初始化功能是怎样抽象与定义的,我们将在后续文章中逐个进行详解,这里先从整体来看看整个初始化过程是怎样的。

初始化流程图

  

初始化流程步骤

框架配置信息初始化

  OSharp框架在Web.Config(App.Config)配置文件的configSections节点中定义了类型为 OSharp.Core.Configs.ConfigFile.OSharpFrameworkSection 的配置信息,主要节点如下所示:

  1. <osharp xmlns="http://file.osharp.org/schemas/osharp.xsd">
  2. <data>
  3. <contexts>
  4. <context name="default" enabled="true" dataLoggingEnabled="true" connectionStringName="default"
  5. type="OSharp.Core.Data.Entity.DefaultDbContext, OSharp.Core.Data.Entity">
  6. <initializer type="OSharp.Core.Data.Entity.DefaultDbContextInitializer, OSharp.Core.Data.Entity"
  7. mapperFiles="OSharp.Demo.Core">
  8. <createInitializer type="OSharp.Demo.Data.CreateDatabaseIfNotExistsWithSeed,OSharp.Demo.Core" />
  9. </initializer>
  10. </context>
  11. <context name="logging" enabled="true" dataLoggingEnabled="false" connectionStringName="default"
  12. type="OSharp.Core.Data.Entity.Logging.LoggingDbContext, OSharp.Core.Data.Entity">
  13. <initializer type="OSharp.Core.Data.Entity.Logging.LoggingDbContextInitializer, OSharp.Core.Data.Entity"
  14. mapperFiles="OSharp.Core.Data.Entity" />
  15. </context>
  16. </contexts>
  17. </data>
  18. <logging>
  19. <entry enabled="true" level="Debug" />
  20. <basic>
  21. <adapters>
  22. <adapter name="log4net" enabled="true"
  23. type="OSharp.Logging.Log4Net.Log4NetLoggerAdapter, OSharp.Logging.Log4Net" />
  24. </adapters>
  25. </basic>
  26. </logging>
  27. </osharp>

 使用的时候,需要在configSections节点中增加名为“osharp”的节点信息:

  1.   <section name="osharp" type="OSharp.Core.Configs.ConfigFile.OSharpFrameworkSection, OSharp.Core" />

  通过以上配置,系统初始化之后,就能通过 OSharpConfig.Instance 这个单例来获取OSharpConfig的配置信息供框架初始化的时候使用。

依赖注入服务映射信息提取

  OSharp的依赖注入功能,是参考ASP.NET 5 中的 Microsoft.Framework.DependencyInjection.Abstractions 来设计完成的,依赖注入架构的思路,也参考了 ASP.NET 5 的“一切皆服务”的架构思想。首先收集所有系统中各个模块中需要使用到的注入点、接口与实现类的映射等信息,封装成描述服务映射信息的 ServiceDescriptor ,并构建成映射信息的集合 IServiceCollection ,然后后面的依赖注入容器的构建,将以这个映射信息集合为基础进行构建。

  参照传统,对于依赖注入对象的生命周期描述,OSharp中定义了实时模式的(Transient)、局部模式的(Scoped)、单例模式的(Singleton)三种生命周期,为了方便构建服务与服务实现之间的映射关系,同时定义了 ITransientDependency 、 IScopeDependency 、 ISingletonDependency 三个空接口来标注映射的生命周期类型。在系统初始化的时候,只需要遍历程序集中的所有类型,查找所有这三个空接口的实现类型,即可很方便的构建服务与服务实现的映射描述的 ServiceDescriptor 对象。

  自动构建映射关系,只需要如下2行代码即可完成:

  1.   IServicesBuilder builder = new ServicesBuilder(new ServiceBuildOptions());
  2.   IServiceCollection services = builder.Build();

  大部分映射关系,可以使用上面所说的三个接口来标注进行创建,但对于部分特殊的映射类型,或者某些需要后期来决定是否启用的模块,不利于使用自动检索程序集构建的映射关系,可通过在各个模块中构建,然后手动添加到 IServiceCollection 中来进行创建:

  1.   services.AddLog4NetServices(); //添加log4net的日志模块服务
  2.   services.AddDataServices(); //添加数据访问模块的服务

  这样,只需要简单的操作,我们即可将系统中所有的服务与服务实现的映射信息提取出来。

Mvc/WebApi/SignalR平台初始化

  平台初始化功能,主要是通过 FrameworkInitializer 初始化类来完成的。主要做的工作有以下几点:

  1. 依赖注入容器初始化:不同于MVC6将所有平台都整合了,MVC5、WebApi5与SignalR2这三个不同平台,使用的是各自不同的依赖注入容器,所以需要各种分别进行依赖注入初始化。
  2. 日志功能初始化:依照OSharpConfig中的配置进行日志功能初始化。日志功能作为框架的基础,只需要初始化一次即可,因而要检查是否已被初始化,只有未初始化时,才执行,否则跳过。
  3. 数据库初始化:依照OSharpConfig中的配置进行数据库功能初始化。同一个系统中,数据库初始化也只进行一次。
  4. 实体信息初始化:收集系统中所有实体类的信息,以数据的形式存入数据库中,同一系统中,实体信息收集也只进行一次。
  5. 功能信息初始化:收集系统中所有业务功能点的信息,以数据的形式存入数据库中,由于各个平台提取功能的方式不一样,因而需要不同平台分别进行初始化。
  6. 第2、3、4点需要的只执行一次的判断,主要通过在 FrameworkInitializer 中定义的三个静态私有字段来判断,这样即使创建多个 FrameworkInitializer 对象,也能准确判断是否执行过了。

   FrameworkInitializer 类型实现如下:

  1. /// <summary>
  2. /// 框架初始化
  3. /// </summary>
  4. public class FrameworkInitializer : IFrameworkInitializer
  5. {
  6. //基础模块,只初始化一次
  7. private static bool _basicLoggingInitialized;
  8. private static bool _databaseInitialized;
  9. private static bool _entityInfoInitialized;
  10.  
  11. /// <summary>
  12. /// 开始执行框架初始化
  13. /// </summary>
  14. /// <param name="iocBuilder">依赖注入构建器</param>
  15. public void Initialize(IIocBuilder iocBuilder)
  16. {
  17. iocBuilder.CheckNotNull("iocBuilder");
  18.  
  19. OSharpConfig config = OSharpConfig.Instance;
  20.  
  21. //依赖注入初始化
  22. IServiceProvider provider = iocBuilder.Build();
  23.  
  24. //日志功能初始化
  25. IBasicLoggingInitializer loggingInitializer = provider.GetService<IBasicLoggingInitializer>();
  26. if (!_basicLoggingInitialized && loggingInitializer != null)
  27. {
  28. loggingInitializer.Initialize(config.LoggingConfig);
  29. _basicLoggingInitialized = true;
  30. }
  31.  
  32. //数据库初始化
  33. IDatabaseInitializer databaseInitializer = provider.GetService<IDatabaseInitializer>();
  34. if (!_databaseInitialized)
  35. {
  36. if (databaseInitializer == null)
  37. {
  38. throw new InvalidOperationException(Resources.FrameworkInitializerBase_DatabaseInitializeIsNull);
  39. }
  40. databaseInitializer.Initialize(config.DataConfig);
  41. _databaseInitialized = true;
  42. }
  43.  
  44. //实体信息初始化
  45. if (!_entityInfoInitialized)
  46. {
  47. IEntityInfoHandler entityInfoHandler = provider.GetService<IEntityInfoHandler>();
  48. if (entityInfoHandler == null)
  49. {
  50. throw new InvalidOperationException(Resources.FrameworkInitializerBase_EntityInfoHandlerIsNull);
  51. }
  52. entityInfoHandler.Initialize();
  53. _entityInfoInitialized = true;
  54. }
  55. //功能信息初始化
  56. IFunctionHandler functionHandler = provider.GetService<IFunctionHandler>();
  57. if (functionHandler == null)
  58. {
  59. throw new InvalidOperationException(Resources.FrameworkInitializerBase_FunctionHandlerIsNull);
  60. }
  61. functionHandler.Initialize();
  62. }
  63. }

  平台初始化步骤:

  1. 读取框架配置信息 OSharpConfig
  2. 使用服务映射集合的“副本”进行相应平台的依赖注入容器创建,返回  IServiceProvider 类型的依赖注入服务提供者
  3. 从 IServiceProvider 获取注入的日志模块初始化器,进行日志功能的初始化
  4. 从 IServiceProvider 获取注入的数据模块初始化器,进行数据功能的初始化
  5. 从 IServiceProvider 获取注入的实体信息初始化器,进行实体信息的初始化
  6. 从 IServiceProvider 获取注入的平台功能信息初始化器,进行平台功能信息的初始化

  有了这个初始化实现,我们使用初始化功能的时候,只需执行如下几行代码即可:

  1. IFrameworkInitializer initializer = new FrameworkInitializer();
  2. initializer.Initialize(new MvcAutofacIocBuilder(services));
  3. initializer.Initialize(new WebApiAutofacIocBuilder(services));
  4. initializer.Initialize(new SignalRAutofacIocBuilder(services));

两种初始化方式

Global.Application_Start方式

  整个框架的初始化代码,如下所示,我们只需要在Global的Application_Start方法中执行如下代码,即可完成框架初始化:

  1. private static void Initialize()
  2. {
  3. IServicesBuilder builder = new ServicesBuilder();
  4. IServiceCollection services = builder.Build();
  5. services.AddLog4NetServices();
  6. services.AddDataServices();
  7.  
  8. IFrameworkInitializer initializer = new FrameworkInitializer();
  9. initializer.Initialize(new MvcAutofacIocBuilder(services));
  10. initializer.Initialize(new WebApiAutofacIocBuilder(services));
  11. initializer.Initialize(new SignalRAutofacIocBuilder(services));
  12. }

Owin方式

  框架中还提供了基于Owin的初始化方式,执行代码如下所示,主要是通过三个 IAppBuilder 的扩展方法来完成的:

  1. public partial class Startup
  2. {
  3. public void Configuration(IAppBuilder app)
  4. {
  5. // 有关如何配置应用程序的详细信息,请访问 http://go.microsoft.com/fwlink/?LinkID=316888
  6.  
  7. IServicesBuilder builder = new ServicesBuilder();
  8. IServiceCollection services = builder.Build();
  9. services.AddLog4NetServices();
  10. services.AddDataServices();
  11.  
  12. app.UseOsharpMvc(new MvcAutofacIocBuilder(services));
  13. app.UseOsharpWebApi(new WebApiAutofacIocBuilder(services));
  14. app.UseOsharpSignalR(new SignalRAutofacIocBuilder(services));
  15.  
  16. ConfigurationWebApi(app);
  17. ConfigureSignalR(app);
  18. }
  19. }

  本篇仅是简单的介绍下OSharp框架的初始化流程,具体的每个环节的设计细节,将在后续的几篇文章中进行详解。

开源说明

github.com

  OSharp项目已在github.com上开源,地址为:https://github.com/i66soft/osharp,欢迎阅读代码,欢迎 Watch(关注),欢迎 Star(推荐),如果您认同 OSharp 项目的设计思想,欢迎参与 OSharp 项目的开发。

  在Visual Studio 2013中,可直接获取 OSharp 的最新源代码,获取方式如下,地址为:https://github.com/i66soft/osharp.git

  

开源项目参与方式

  很多童鞋想参与开源项目,为项目做贡献,但又不知道如何做,这里我简单说下参与OSharp的步骤吧:

  1. https://github.com/i66soft/osharp 右上角 Fork 一下项目源码,在你的账户下会有一份代码的副本
  2. 使用VisualStudio Clone 你账户下的代码到本地,更改代码,再提交,就完成代码的更改了
  3. 如果觉得有并入 i66soft 主干的价值,可以向主干提交 pull request申请,如果我审核通过,就可以合并到主干了,这就形成了一次开源代码的贡献了
  4. 如果我没有接受合并,你也可以在你的账户上按你的风格去发展osharp
  5. 我也会经常浏览各个Fork版本对项目的更改,如果觉得有价值,也会主动合并到主干代码中,也能形成一次对开源的贡献
  6. 为保证提交的质量,也便于对代码的合并,每次更改与提交应该只做一件事,只提交必要的更改

nuget

  OSharp的相关类库已经发布到nuget上,欢迎试用,直接在nuget上搜索 “osharp” 关键字即可找到

系列导航

本文已同步到系列目录:OSharp快速开发框架解说系列

【开源】OSharp3.3框架解说系列(7.1):初始化流程概述的更多相关文章

  1. 【开源】OSharp3.0框架解说系列:新版本说明及新功能规划预览

    OSharp是什么? OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现.与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现.依 ...

  2. 【开源】OSharp3.0框架解说系列(6.2):操作日志与数据日志

    OSharp是什么? OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现.与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现.依 ...

  3. 【开源】OSharp框架解说系列(1):总体设计及系列导航

    系列文章导航 [开源]OSharp框架解说系列(1):总体设计 [开源]OSharp框架解说系列(2.1):EasyUI的后台界面搭建及极致重构 [开源]OSharp框架解说系列(2.2):EasyU ...

  4. 【开源】OSharp3.3框架解说系列:重新开源及3.3版本新特性

    OSharp是什么? OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现.与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现.依 ...

  5. 【开源】OSharp3.3框架解说系列:开发计划与进度

    OSharp是什么? OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现.与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现.依 ...

  6. 【开源】OSharp框架解说系列(6.1):日志系统设计

    OSharp是什么? OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现.与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现.依 ...

  7. 【开源】OSharp框架解说系列(5.2):EntityFramework数据层实现

    OSharp是什么? OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现.与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现.依 ...

  8. 【开源】OSharp框架解说系列(5.1):EntityFramework数据层设计

    OSharp是什么? OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现.与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现.依 ...

  9. 【开源】OSharp框架解说系列(4):架构分层及IoC

    OSharp是什么? OSharp是个快速开发框架,但不是一个大而全的包罗万象的框架,严格的说,OSharp中什么都没有实现.与其他大而全的框架最大的不同点,就是OSharp只做抽象封装,不做实现.依 ...

随机推荐

  1. bzoj1008快速面

    快速面裸题(显然的m^n-m*(m-1)^(n-1)) 然后,,,就没有然后了 #include <cstdio> #define wzf2000 100003 long long n,m ...

  2. XDocument获取指定节点

    string xmlFile = @"D:\Documents\Visual Studio 2013\Projects\Jesee.Web.Test\ConsoleApplication1\ ...

  3. sql2000分享 批量建表dev_编号

    批量建表dev_3970000000014到dev_3970000000035 declare @i bigint declare @j int ) ) ) ) set @sql = '' set @ ...

  4. 文档ID:某某 模板文件不存在,无法解析文档!

    如果是生成栏目列表时出现这样的问题]:   1.可以修改include/arc.listview.class.php这个文件. 2.复制代码     echo "模板文件不存在,无法解析文档 ...

  5. Codeforces Round #384 (Div. 2) 734E Vladik and cards

    E. Vladik and cards time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  6. C#按照指定长度分割中英文字符串

    最近有一个需求:玩家发的不同长度文字,需要自适应行数. 初步实现想法很简单,直接获取字符数均分行数,再利用string.substring()切割即可.但是显而易见,由于一般字体下,中文显示宽度一般是 ...

  7. CentOS7 编译安装 Git 服务器 Centos 7.0 + Git 2.2.0 + gitosis (实测 笔记)

    环境: 系统硬件:vmware vsphere (CPU:2*4核,内存2G,双网卡) 系统版本:CentOS-7.0-1406-x86_64-DVD.iso 服务器IP:192.168.1.31 域 ...

  8. Daily Scrum02 12.15

    今天会议主要还是大家汇报进度与任务.由于团队中有两位成员在周一会有其他事情处理,暂不分配任务,因而这些事情要交给其他成员处理…… 由于要反复修改,查看效果,所以要花费很长的时间,但大家最近都很忙,我们 ...

  9. Mimikatz 使用Tips

    1.记录 Mimikatz输出: C:\>mimikatz.exe ""privilege::debug"" ""log sekurl ...

  10. Centos 编写 service 启动

    Centos 系统服务脚本目录: [html] view plaincopyprint?   /usr/lib/systemd/ 有系统(system)和用户(user)之分, 如需要开机没有登陆情况 ...