
OSharpNS全稱OSharp Framework with .NetStandard2.0,是一個基于`.NetStandard2.0`開發的一個`.NetCore`快速開發架構。這個架構使用最新穩定版的`.NetCore SDK`(目前是.NET Core 2.2),對 AspNetCore 的配置、依賴注入、日志、緩存、實體架構、Mvc(WebApi)、身份認證、權限授權等子產品進行更高一級的自動化封裝,并規範了一套業務實作的代碼結構與操作流程,使 .Net Core 架構更易于應用到實際項目開發中。
什麼是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
包名稱 | Nuget版本 | 下載下傳數 |
---|---|---|
OSharpNS.Core | | |
OSharpNS.AspNetCore | | |
OSharpNS.AutoMapper | | |
OSharpNS.EntityFrameworkCore | | |
OSharpNS.EntityFrameworkCore.SqlServer | | |
OSharpNS.EntityFrameworkCore.MySql | | |
OSharpNS.EntityFrameworkCore.Sqlite | | |
OSharpNS.EntityFrameworkCore.PostgreSql | | |
OSharpNS.EntityFrameworkCore.Oracle | | |
OSharpNS.Permissions | | |
OSharpNS.Log4Net | | |
OSharpNS.Redis | | |
OSharpNS.Hangfire | | |
OSharpNS.Exceptionless | | |
OSharpNS.MiniProfiler | | |
OSharpNS.Swagger | | |
OSharpNS | | |
OSharpNS.Template.Mvc_Angular | | |
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),用于對系統的功能權限、資料權限、資料緩存、操作審計 等實用功能提供資料支援。
- 功能點
與MVC的Function
一一對應,是功能權限的最小驗證機關,基于功能點,可以配置: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
- 這樣使用者即可根據擁有的角色,自動擁有子產品對應着的所有功能點的功能權限
[開源]OSharpNS - .net core 快速開發架構 - 簡介
功能權限驗證流程
- 系統初始化時,根據每個角色
配置設定到的子產品Role
,自動初始化每個Module
的權限對應關系并緩存角色 Role - Function[]
- 遊客進入系統時,自動請求所有可匿名通路
的子產品資訊并緩存到浏覽器,浏覽器根據這個緩存的子產品集合,對前端頁面的各個操作點(菜單/按鈕)進行是否隐藏/禁用的狀态控制FunctionAccessType.Anonymouse
- 注冊使用者登入系統時,自動請求所有可執行(包括匿名的
、登入的FunctionAccessType.Anonymouse
、指定角色的FunctionAccessType.Logined
)的子產品資訊并緩存到浏覽器,浏覽器根據這個緩存的子產品集合,對前端頁面的各個操作點(菜單/按鈕)進行是否隐藏/禁用的狀态控制FunctionAccessType.RoleLimit
- 使用者
執行一個功能點User
時,驗證流程如下:Function
- 功能點不存在時,傳回404
- 功能點被鎖定時,傳回423
- 功能點可通路性為匿名
驗證通過FunctionAccessType.Anonymouse
- 功能點可通路性為需要登入
時,使用者未登入,傳回401,已登入則驗證通過FunctionAccessType.Logined
- 功能點可通路性為需要登入
時,流程如下:FunctionAccessType.RoleLimit
- 使用者未登入,傳回401
- 逐個驗證使用者擁有的角色
,根據角色從緩存中取出Role
緩存項,Role-Function[]
包含要驗證的功能點時,驗證通過Function[]
- 由配置設定給使用者的子產品
對應的功能點,擷取到Module
(并緩存),User-Function[]
包含要驗證的功能點時,驗證通過Function[]
- 驗證未通過,傳回403
資料權限授權流程
- 基于 角色
-實體Role
的一一對應關系,配置指定角色對指定資料實體的資料查詢篩選規則,并持久化到資料庫中EntityInfo
- 資料查詢篩選規則組成為 條件組
和條件FilterGroup
,一個條件組 FilterGroup 包含 一個或多個條件 FilterRule 和 一個或多個 條件組FilterRule
,這樣就實作了條件組和條件的無限嵌套,能滿足絕大多數資料篩選規則的組裝需要,如下圖:FilterGroup
資料權限驗證流程
- 系統初始化時,将所有
的資料篩選規則緩存到記憶體中角色-實體
- 進行資料查詢的時候,根據目前使用者的所有
和要查詢的角色 Role
,查找出所有配置的資料篩選規則實體 EntityInfo
,轉換為資料查詢表達式FilterGroup
,各個角色的表達式之間使用Expression<Func<TEntity,bool>>
邏輯進行組合Or
- 将以上生成的
資料查詢表達式,使用資料權限
邏輯組合到使用者的送出的查詢條件生成的表達式中,得到最終的資料查詢表達式,送出到資料庫中進行資料查詢,進而獲得資料權限限制下的合法資料And
6. 內建 Swagger 後端API文檔系統
OSharp 快速啟動模闆的開發模式,內建了
Swagger
API 文檔生成元件,更友善了前後端分離的開發模式中前後端開發人員的資料接口對接工作。基于
Swagger
的工作原理,API的輸入輸出都需使用
強類型
的資料類型,
Swagger
才能發揮更好的作用,而OSharp架構通過
AutoMapper
的
ProjectTo
對業務實體到輸出DTO
IOutputDto
提供了自動映射功能,能有效減輕後端開發中資料對象屬性映射的工作量。
界面展示
OSharp 的這個版本是基于Angular前端架構 NG-ALAIN 開發的,部分界面展示如下:
背景首頁:
功能管理:
資料實體管理: