【開源】OSharp3.3框架解說系列(7.1):初始化流程概述


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快速開發框架解說系列


免責聲明!

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



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