OSharpNS全稱OSharp Framework with .NetStandard2.0,是一個基於`.NetStandard2.0`開發的一個`.NetCore`快速開發框架。這個框架使用最新穩定版的`.NetCore SDK`(當前是.NET Core 2.2),對 AspNetCore... ...
什麼是OSharp
OSharpNS全稱OSharp Framework with .NetStandard2.0,是一個基於.NetStandard2.0
開發的一個.NetCore
快速開發框架。這個框架使用最新穩定版的.NetCore SDK
(當前是.NET Core 2.2),對 AspNetCore 的配置、依賴註入、日誌、緩存、實體框架、Mvc(WebApi)、身份認證、許可權授權等模塊進行更高一級的自動化封裝,並規範了一套業務實現的代碼結構與操作流程,使 .Net Core 框架更易於應用到實際項目開發中。
- 開源地址:https://github.com/i66soft/osharp
- 官方示例:https://www.osharp.org
- 文檔中心:https://docs.osharp.org
- VS 插件:https://marketplace.visualstudio.com/items?itemName=LiuliuSoft.osharp
框架工程組織
框架的工程組織結構如下:
各工程簡介
- OSharp【框架核心組件】:框架的核心組件,包含一系列快速開發中經常用到的Utility輔助工具功能,框架各個組件的核心介面定義,部分核心功能的實現
- OSharp.AspNetCore【AspNetCore組件】:AspNetCore組件,提供AspNetCore的服務端功能的封裝
- OSharp.AutoMapper【對象映射組件】:AutoMapper 對象映射組件,封裝基於AutoMapper的對象映射實現
- OSharp.EntityFrameworkCore【EFCore 數據組件】:EFCore數據訪問組件,封裝EntityFrameworkCore數據訪問功能的實現
- OSharp.EntityFrameworkCore.MySql【EFCore MySql 數據組件】:EFCore MySql數據訪問組件,封裝MySql的EntityFrameworkCore數據訪問功能的實現
- OSharp.EntityFrameworkCore.SqlServer【EFCore SqlServer 數據組件】:EFCore SqlServer數據訪問組件,封裝SqlServer的EntityFrameworkCore數據訪問功能的實現
- OSharp.EntityFrameworkCore.Sqlite【EFCore Sqlite 數據組件】:EFCore Sqlite數據訪問組件,封裝Sqlite的EntityFrameworkCore數據訪問功能的實現
- OSharp.EntityFrameworkCore.PostgreSql【EFCore PostgreSql 數據組件】:EFCore PostgreSql數據訪問組件,封裝PostgreSql的EntityFrameworkCore數據訪問功能的實現
- OSharp.EntityFrameworkCore.Oracle【EFCore PostgreSql 數據組件】:EFCore Oracle數據訪問組件,封裝Oracle的EntityFrameworkCore數據訪問功能的實現
- OSharp.Permissions【許可權組件】:使用AspNetCore的Identity為基礎實現身份認證的封裝,以Security為基礎實現以角色-功能、用戶-功能的功能許可權實現,以角色-數據,用戶-數據的數據許可權的封裝
- OSharp.Log4Net【日誌組件】:基於Log4Net的日誌記錄組件
- OSharp.Redis【緩存組件】:基於Redis的分散式緩存客戶端組件
- OSharp.Hangfire【後臺任務組件】:封裝基於Hangfire後臺任務的服務端實現
- OSharp.MiniProfiler【MiniProfiler組件】:基於MiniProfiler實現的性能監測組件
- OSharp.Swagger【SwaggerAPI組件】:基於Swagger生成MVC的Action的API測試介面信息
- OSharp.Exceptionless【Exceptionless分散式日誌組件】:封裝基於Exceptionless 分散式日誌記錄實現
Nuget Packages
OSharp框架特性
1. 模塊化的組件系統設計
OSharp框架設計了一個模塊(Pack)系統,每個Pack以一個實現了模塊基類(OsharpPack)的類作為入口,這個類完成本模塊的服務添加(AddService)和模塊初始化工作(UserPack)。一個Pack是一系列高內聚低耦合的服務組織,對象提供一個功能(如緩存功能,日誌功能,數據存儲功能)或完成一組業務處理(如身份認證,許可權授權)。
一個Pack入口類的代碼如下:
public class XXXPack : OsharpPack
{
/// <summary>
/// 獲取 模塊級別
/// </summary>
public override PackLevel Level => PackLevel.Core;
/// <summary>
/// 獲取 模塊啟動順序,模塊啟動的順序先按級別啟動,級別內部再按此順序啟動
/// </summary>
public override int Order => 2;
/// <summary>
/// 將模塊服務添加到依賴註入服務容器中
/// </summary>
/// <param name="services">依賴註入服務容器</param>
/// <returns></returns>
public override IServiceCollection AddServices(IServiceCollection services)
{
// TODO: 在這裡添加本模塊涉及的各種依賴註入服務
return services;
}
/// <summary>
/// 應用模塊服務
/// </summary>
/// <param name="provider">服務提供者</param>
public override void UsePack(IServiceProvider provider)
{
// TODO: 在這裡進行模塊的初始化操作
IsEnabled = true;
}
}
當前框架的模塊組成如下圖:
名稱 | 類型 | 級別 |
---|---|---|
OSharp核心模塊 | OSharp.Core.Packs.OsharpCorePack | Core |
依賴註入模塊 | OSharp.Dependency.DependencyPack | Core |
Log4Net模塊 | OSharp.Log4Net.Log4NetPack | Core |
AspNetCore模塊 | OSharp.AspNetCore.AspNetCorePack | Core |
事件匯流排模塊 | OSharp.EventBuses.EventBusPack | Core |
AutoMapper模塊 | OSharp.AutoMapper.AutoMapperPack | Framework |
Hangfire後臺任務模塊 | OSharp.Hangfire.HangfirePack | Framework |
Redis模塊 | OSharp.Redis.RedisPack | Framework |
MySqlEntityFrameworkCore模塊 | OSharp.Entity.MySql.MySqlEntityFrameworkCorePack | Framework |
SqliteEntityFrameworkCore模塊 | OSharp.Entity.Sqlite.SqliteEntityFrameworkCorePack | Framework |
SqlServerEntityFrameworkCore模塊 | OSharp.Entity.SqlServer.SqlServerEntityFrameworkCorePack | Framework |
SqlServer-DefaultDbContext遷移模塊 | OSharp.Site.Web.Startups.SqlServerDefaultDbContextMigrationPack | Framework |
MVC功能點模塊 | OSharp.AspNetCore.Mvc.MvcFunctionPack | Application |
數據實體模塊 | OSharp.Core.EntityInfos.EntityInfoPack | Application |
系統信息模塊 | OSharp.Systems.SystemPack | Application |
身份認證模塊 | OSharp.Site.Identity.IdentityPack | Application |
MVC模塊 | OSharp.Site.Web.Startups.AspNetCoreMvcPack | Application |
SignalR模塊 | OSharp.Site.Web.Startups.SignalRPack | Application |
許可權安全模塊 | OSharp.Site.Security.SecurityPack | Application |
代碼生成模塊 | OSharp.Site.Web.Startups.CodeGeneratorPack | Application |
SwaggerApi模塊 | OSharp.Swagger.SwaggerPack | Application |
審計模塊 | OSharp.Site.Systems.AuditPack | Application |
2. 自動化的依賴註入註冊機制
空介面標註方式
框架定義了ISingletonDependency,IScopeDependency,ITransientDependency 三個空介面,對應著依賴註入的ServiceLifetime.Singleton
、ServiceLifetime.Scoped
、ServiceLifetime.Transient
三種服務生命周期。按需要實現了空介面的服務類,將在系統初始化時被檢索出來進行實現類與其介面的依賴註入服務註冊。
空介面的標註方式,統一使用TryAdd來進行註入
一個示例代碼如下:
public XXXService : IXXXService, ISingletonDependency
{ }
這個示例代碼將在系統初始化時執行如下的註入行為:
// 空介面的標註方式,統一使用TryAdd來進行註入
services.TryAdd(new ServiceDescriptor(typeof(IXXXService),
typeof(XXXService), ServiceLifetime.Singleton));
DependencyAttribute特性標註方式
空介面的標註方式,只能指定服務的註冊生命周期類型,而不能進行更多的配置,因此增加了[Dependency]特性的標註方式。通過[Dependency]
,可以進行 服務註冊的生命周期類型、是否是TryAdd方式註冊、是否替換已存在的服務、是否註冊自身 等配置,使用起來更加靈活方便。
一個示例代碼如下:
[Dependency(ServiceLifetime.Singleton, ReplaceExisting = true, AddSelf = true)]
public XXXService : IXXXService
{ }
這個示例代碼將在系統初始化時執行如下的註入行為:
// replace
services.Replace(new ServiceDescriptor(typeof(IXXXService),
typeof(XXXService), ServiceLifetime.Singleton));
// add self
services.TryAdd(new ServiceDescriptor(typeof(XXXService),
typeof(XXXService), ServiceLifetime.Singleton));
自動化的註冊機制
系統初始化時,通過反射檢索程式集的方式,檢索出所有服務類型(ServiceType)與服務實現(ImplementationType)及生命周期類型(ServiceLifetime)的相關數據,將依賴註入服務註冊到服務容器ServiceCollection
中。
3. UnitOfWork-Repository模式,EFCore上下文動態構建
- 數據模塊使用了UnitOfWork-Repository的模式來設計,設計了一個泛型的實體倉儲介面IRepository<TEntity,TKey>,避免每個實體都需實現一個倉儲的繁瑣操作。設計了IUnitOfWorkManager介面來管理多資料庫連接事務,每個IUnitOfWork,通過IUnitOfWork模式管理DbContext的創建與緩存,使同連接對象的多個上下文共用事務,達到多上下文的事務同步能力。
- 基於MVC的ActionFilter的UnitOfWorkAttribute AOP 事務自動提交,業務中不再需要關心事務的生命周期。
- 系統初始化時,通過反射檢索程式集的方式,檢索出各個實體與上下文的映射關係,向上下文中動態添加實體類來構建上下文類型,以達到上下文類型與業務實體解耦的目的。通過統一基類EntityTypeConfigurationBase<TEntity, TKey>的FluentAPI實體映射,自由配置每個實體與資料庫映射的每一個細節。
4. 基於AspNetCore的Identity的身份認證設計系統
- 使用AspNetCore原生的用戶身份認證框架,身份認證相關操作統一使用UserManager
,RoleMamanger 兩個入口,保持了原生Identity的體系強大性與功能完整性。 - 重新設計了用戶存儲UserStore和角色存儲RoleStore,使用框架內設計的IRepository<TEntity,TKey>數據倉儲介面來實現對數據的倉儲操作,使Identity身份認證系統與框架完美結合,避免了使用官方的Microsoft.AspNetCore.Identity.EntityFrameworkCore造成多個上下文或者被強制使用Identity上下文作為系統數據上下文來實現業務造成的尷尬。
5. 設計了一個強大的功能許可權與數據許可權的授權體系
- 從底層開始,自動收集了系統的所有業務點(IFunction)和數據實體(IEntityInfo),用於對系統的功能許可權、數據許可權、數據緩存、操作審計 等實用功能提供數據支持。
- 功能點
Function
與MVC的Area/Controller/Action
一一對應,是功能許可權的最小驗證單位,基於功能點,可以配置:- 功能訪問類型(匿名訪問、登錄訪問、限定角色訪問)
- 功能的數據緩存時間及緩存過期方式(絕對過期、相對過期)
- 是否開啟操作審計(XXX人員XXX時間做了XXX操作)
- 是否開啟數據審計(操作引起的數據變化詳情(新增、更新、刪除))
- 數據實體
EntityInfo
與資料庫中的各個數據實體一一對應,基於數據實體,可以配置:- 是否開啟數據審計,與
Function
上的同配置級別不同,如果指定實體未開放審計,則不審計當前實體。 - 實現數據許可權,基於
角色 - 實體
的數據許可權設計,通過配置實現 XXX角色是否有權訪問XXX實體數據(的XX屬性)
- 是否開啟數據審計,與
- 設計了一個樹形結構的業務模塊體系(Module),對應著後端向前端(菜單/按鈕)開放的API,一個模塊可由一個或多個功能點構成,模塊是對外開放的特殊功能點,是進行 角色/用戶功能授權 的單位。把一個模塊授權給角色,角色即擁有了一個或多個功能點的操作許可權。
功能許可權授權流程
- [自動] 創建MVC的各個
Area/Controller/Action
的功能點Function
信息,存儲到資料庫 - [自動] 創建樹形模塊
Module
信息,並創建模塊與功能點(一個或多個)的分配關係,存儲到資料庫 - 將模塊
Module
分配給角色Role
- 將角色
Role
分配給用戶User
- 可將模塊
Module
分配給用戶User
,解決特權問題 - 這樣用戶即可根據擁有的角色,自動擁有模塊對應著的所有功能點的功能許可權
功能許可權驗證流程
- 系統初始化時,根據每個角色
Role
分配到的模塊Module
,自動初始化每個角色 Role - Function[]
的許可權對應關係並緩存 - 游客進入系統時,自動請求所有可匿名訪問
FunctionAccessType.Anonymouse
的模塊信息並緩存到瀏覽器,瀏覽器根據這個緩存的模塊集合,對前端頁面的各個操作點(菜單/按鈕)進行是否隱藏/禁用的狀態控制 - 註冊用戶登錄系統時,自動請求所有可執行(包括匿名的
FunctionAccessType.Anonymouse
、登錄的FunctionAccessType.Logined
、指定角色的FunctionAccessType.RoleLimit
)的模塊信息並緩存到瀏覽器,瀏覽器根據這個緩存的模塊集合,對前端頁面的各個操作點(菜單/按鈕)進行是否隱藏/禁用的狀態控制
- 用戶
User
執行一個功能點Function
時,驗證流程如下:- 功能點不存在時,返回404
- 功能點被鎖定時,返回423
- 功能點可訪問性為匿名
FunctionAccessType.Anonymouse
驗證通過 - 功能點可訪問性為需要登錄
FunctionAccessType.Logined
時,用戶未登錄,返回401,已登錄則驗證通過 - 功能點可訪問性為需要登錄
FunctionAccessType.RoleLimit
時,流程如下:- 用戶未登錄,返回401
- 逐個驗證用戶擁有的角色
Role
,根據角色從緩存中取出Role-Function[]
緩存項,Function[]
包含要驗證的功能點時,驗證通過 - 由分配給用戶的模塊
Module
對應的功能點,獲取到User-Function[]
(並緩存),Function[]
包含要驗證的功能點時,驗證通過 - 驗證未通過,返回403
數據許可權授權流程
- 基於 角色
Role
-實體EntityInfo
的一一對應關係,配置指定角色對指定數據實體的數據查詢篩選規則,並持久化到資料庫中 - 數據查詢篩選規則組成為 條件組
FilterGroup
和條件FilterRule
,一個條件組 FilterGroup 包含 一個或多個條件 FilterRule 和 一個或多個 條件組FilterGroup
,這樣就實現了條件組和條件的無限嵌套,能滿足絕大多數數據篩選規則的組裝需要,如下圖:
數據許可權驗證流程
- 系統初始化時,將所有
角色-實體
的數據篩選規則緩存到記憶體中 - 進行數據查詢的時候,根據當前用戶的所有
角色 Role
和要查詢的實體 EntityInfo
,查找出所有配置的數據篩選規則FilterGroup
,轉換為數據查詢表達式Expression<Func<TEntity,bool>>
,各個角色的表達式之間使用Or
邏輯進行組合 - 將以上生成的
數據許可權
數據查詢表達式,使用And
邏輯組合到用戶的提交的查詢條件生成的表達式中,得到最終的數據查詢表達式,提交到資料庫中進行數據查詢,從而獲得數據許可權限制下的合法數據
6. 集成 Swagger 後端API文檔系統
OSharp 快速啟動模板的開發模式,集成了Swagger
API 文檔生成組件,更方便了前後端分離的開發模式中前後端開發人員的數據介面對接工作。基於Swagger
的工作原理,API的輸入輸出都需使用強類型
的數據類型,Swagger
才能發揮更好的作用,而OSharp框架通過AutoMapper
的ProjectTo
對業務實體到輸出DTOIOutputDto
提供了自動映射功能,能有效減輕後端開發中數據對象屬性映射的工作量。
界面展示
OSharp 的這個版本是基於Angular前端框架 NG-ALAIN 開發的,部分界面展示如下:
後臺主頁:
功能管理:
數據實體管理: