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 開發的,部分介面展示如下:
後臺主頁:
功能管理:
朋友會在“發現-看一看”看到你“在看”的內容