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
1. 模塊化的組件系統設計
OSharp框架設計了一個模塊(Pack)系統,每個Pack以一個實現了模塊基類(OsharpPack)的類作為入口,這個類完成本模塊的服務添加(AddService)和模塊初始化工作(UserPack)。一個Pack是一系列高內聚低耦合的服務組織,物件提供一個功能(如快取功能,日誌功能,資料儲存功能)或完成一組業務處理(如身份認證,權限授權)。
一個Pack入口類的代碼如下:
public class XXXPack : OsharpPack
{
public override PackLevel Level => PackLevel.Core;
public override int Order => 2;
public override IServiceCollection AddServices(IServiceCollection services)
{
return services;
}
public override void UsePack(IServiceProvider provider)
{
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
{ }
這個示例代碼將在系統初始化時執行如下的註入行為:
services.TryAdd(new ServiceDescriptor(typeof(IXXXService),
typeof(XXXService), ServiceLifetime.Singleton));
DependencyAttribute特性標註方式
空接口的標註方式,只能指定服務的註冊生命周期型別,而不能進行更多的配置,因此增加了[Dependency]特性的標註方式。通過[Dependency]
,可以進行 服務註冊的生命周期型別、是否是TryAdd方式註冊、是否替換已存在的服務、是否註冊自身 等配置,使用起來更加靈活方便。
一個示例代碼如下:
[Dependency(ServiceLifetime.Singleton, ReplaceExisting = true, AddSelf = true)]
public XXXService : IXXXService
{ }
這個示例代碼將在系統初始化時執行如下的註入行為:
services.Replace(new ServiceDescriptor(typeof(IXXXService),
typeof(XXXService), ServiceLifetime.Singleton));
services.TryAdd(new ServiceDescriptor(typeof(XXXService),
typeof(XXXService), ServiceLifetime.Singleton));
自動化的註冊機制
系統初始化時,通過反射檢索程式集的方式,檢索出所有服務型別(ServiceType)與服務實現(ImplementationType)及生命周期型別(ServiceLifetime)的相關資料,將依賴註入服務註冊到服務容器ServiceCollection
中。
3. UnitOfWork-Repository樣式,EFCore背景關係動態構建
- 資料模塊使用了UnitOfWork-Repository的樣式來設計,設計了一個泛型的物體倉儲接口IRepository,避免每個物體都需實現一個倉儲的繁瑣操作。設計了IUnitOfWorkManager接口來管理多資料庫連接事務,每個IUnitOfWork,通過IUnitOfWork樣式管理DbContext的創建與快取,使同連接物件的多個背景關係共享事務,達到多背景關係的事務同步能力。
- 基於MVC的ActionFilter的UnitOfWorkAttribute AOP 事務自動提交,業務中不再需要關心事務的生命周期。
- 系統初始化時,通過反射檢索程式集的方式,檢索出各個物體與背景關係的映射關係,向背景關係中動態添加物體類來構建背景關係型別,以達到背景關係型別與業務物體解耦的目的。通過統一基類EntityTypeConfigurationBase的FluentAPI物體映射,自由配置每個物體與資料庫映射的每一個細節。
4. 基於AspNetCore的Identity的身份認證設計系統
- 使用AspNetCore原生的用戶身份認證框架,身份認證相關操作統一使用UserManager
,RoleMamanger兩個入口,保持了原生Identity的體系強大性與功能完整性。
- 重新設計了用戶儲存UserStore和角色儲存RoleStore,使用框架內設計的IRepository資料倉儲接口來實現對資料的倉儲操作,使Identity身份認證系統與框架完美結合,避免了使用官方的Microsoft.AspNetCore.Identity.EntityFrameworkCore造成多個背景關係或者被強制使用Identity背景關係作為系統資料背景關係來實現業務造成的尷尬。
5. 設計了一個強大的功能權限與資料權限的授權體系
- 從底層開始,自動收集了系統的所有業務點(IFunction)和資料物體(IEntityInfo),用於對系統的功能權限、資料權限、資料快取、操作審計 等實用功能提供資料支持。
- 功能點
Function
與MVC的Area/Controller/Action
一一對應,是功能權限的最小驗證單位,基於功能點,可以配置:- 功能訪問型別(匿名訪問、登錄訪問、限定角色訪問)
- 功能的資料快取時間及快取過期方式(絕對過期、相對過期)
- 是否開啟操作審計(XXX人員XXX時間做了XXX操作)
- 是否開啟資料審計(操作引起的資料變化詳情(新增、更新、刪除))
- 資料物體
EntityInfo
與資料庫中的各個資料物體一一對應,基於資料物體,可以配置:- 是否開啟資料審計,與
Function
上的同配置級別不同,如果指定物體未開放審計,則不審計當前物體。 - 實現資料權限,基於
角色 - 物體
的資料權限設計,通過配置實現 XXX角色是否有權訪問XXX物體資料(的XX屬性)
- 是否開啟資料審計,與
- 設計了一個樹形結構的業務模塊體系(Module),對應著後端向前端(選單/按鈕)開放的API,一個模塊可由一個或多個功能點構成,模塊是對外開放的特殊功能點,是進行 角色/用戶功能授權 的單位。把一個模塊授權給角色,角色即擁有了一個或多個功能點的操作權限。
功能權限授權流程
功能權限驗證流程
- 系統初始化時,根據每個角色
Role
分配到的模塊Module
,自動初始化每個角色 Role - Function[]
的權限對應關係並快取 - 游客進入系統時,自動請求所有可匿名訪問
FunctionAccessType.Anonymouse
的模塊信息並快取到瀏覽器,瀏覽器根據這個快取的模塊集合,對前端頁面的各個操作點(選單/按鈕)進行是否隱藏/禁用的狀態控制 - 註冊用戶登錄系統時,自動請求所有可執行(包括匿名的
FunctionAccessType.Anonymouse
、登錄的FunctionAccessType.Logined
、指定角色的FunctionAccessType.RoleLimit
)的模塊信息並快取到瀏覽器,瀏覽器根據這個快取的模塊集合,對前端頁面的各個操作點(選單/按鈕)進行是否隱藏/禁用的狀態控制
- 用戶
User
執行一個功能點Function
時,驗證流程如下:- 用戶未登錄,傳回401
- 逐個驗證用戶擁有的角色
Role
,根據角色從快取中取出Role-Function[]
快取項,Function[]
包含要驗證的功能點時,驗證通過 - 由分配給用戶的模塊
Module
對應的功能點,獲取到User-Function[]
(並快取),Function[]
包含要驗證的功能點時,驗證通過 - 驗證未通過,傳回403
- 功能點不存在時,傳回404
- 功能點被鎖定時,傳回423
- 功能點可訪問性為匿名
FunctionAccessType.Anonymouse
驗證通過 - 功能點可訪問性為需要登錄
FunctionAccessType.Logined
時,用戶未登錄,傳回401,已登錄則驗證通過 - 功能點可訪問性為需要登錄
FunctionAccessType.RoleLimit
時,流程如下:
資料權限授權流程
- 基於 角色
Role
-物體EntityInfo
的一一對應關係,配置指定角色對指定資料物體的資料查詢篩選規則,並持久化到資料庫中 - 資料查詢篩選規則組成為 條件組
FilterGroup
和條件FilterRule
,一個條件組 FilterGroup 包含 一個或多個條件 FilterRule 和 一個或多個 條件組FilterGroup
,這樣就實現了條件組和條件的無限嵌套,能滿足絕大多數資料篩選規則的組裝需要,如下圖:
資料權限驗證流程
- 系統初始化時,將所有
角色-物體
的資料篩選規則快取到記憶體中 - 進行資料查詢的時候,根據當前用戶的所有
角色 Role
和要查詢的物體 EntityInfo
,查找出所有配置的資料篩選規則FilterGroup
,轉換為資料查詢運算式Expression>
,各個角色的運算式之間使用Or
邏輯進行組合 - 將以上生成的
資料權限
資料查詢運算式,使用And
邏輯組合到用戶的提交的查詢條件生成的運算式中,得到最終的資料查詢運算式,提交到資料庫中進行資料查詢,從而獲得資料權限限制下的合法資料
6. 集成 Swagger 後端API文件系統
OSharp 快速啟動模板的開發樣式,集成了Swagger
API 文件生成組件,更方便了前後端分離的開發樣式中前後端開發人員的資料接口對接工作。基於Swagger
的工作原理,API的輸入輸出都需使用強型別
的資料型別,Swagger
才能發揮更好的作用,而OSharp框架通過AutoMapper
的ProjectTo
對業務物體到輸出DTOIOutputDto
提供了自動映射功能,能有效減輕後端開發中資料物件屬性映射的工作量。
OSharp 的這個版本是基於Angular前端框架 NG-ALAIN 開發的,部分界面展示如下:
後臺主頁:
功能管理:
朋友會在“發現-看一看”看到你“在看”的內容