基於SqlSugar的開發框架循序漸進介紹(5)-- 在服務層使用介面註入方式實現IOC控制反轉

来源:https://www.cnblogs.com/wuhuacong/archive/2022/05/24/16304615.html
-Advertisement-
Play Games

在前面隨筆,我們介紹過這個基於SqlSugar的開發框架,我們區分Interface、Modal、Service三個目錄來放置不同的內容,其中Modal是SqlSugar的映射實體,Interface是定義訪問介面,Service是提供具體的數據操作實現。在Service層中,往往除了本身的一些增刪... ...


在前面隨筆,我們介紹過這個基於SqlSugar的開發框架,我們區分Interface、Modal、Service三個目錄來放置不同的內容,其中Modal是SqlSugar的映射實體,Interface是定義訪問介面,Service是提供具體的數據操作實現。在Service層中,往往除了本身的一些增刪改查等處理操作外,也需要涉及到相關業務的服務介面,這些服務介面我們通過利用.net 的介面註入方式,實現IOC控制反轉的處理的。

1、框架Service層的模塊

如下麵的VS中的項目服務層,包含很多業務表的服務介面實現,如下所示。

 

 我們以其中簡單的Customer業務表為例,它的服務類代碼如下所示(主要關註服務類的定義即可)。

    /// <summary>
    /// 客戶信息應用層服務介面實現
    /// </summary>
    public class CustomerService : MyCrudService<CustomerInfo, string, CustomerPagedDto>, ICustomerService
    {
       ...............
    }

它除了在泛型約束中增加SqlSugar實體類,主鍵類型,分頁條件對象外,還繼承介面 ICustomerService ,這個介面就是我們實現IOC的第一步,服務層繼承指定的介面實現,對我們實現IOC控制反轉提供便利。

    /// <summary>
    /// 客戶信息服務介面
    /// </summary>
    public interface ICustomerService : IMyCrudService<CustomerInfo, string, CustomerPagedDto>, ITransientDependency
    {

    }

這個客戶信息業務處理,是比較典型的單表處理案例,它沒有涉及到相關服務介面的整合,如果我們在其中服務介面中需要調用其他服務介面,那麼我們就需要通過構造函數註入介面對象的方式獲得對象的實例,如下我們說介紹的就是服務調用其他相關介面的實現。

 

2、服務層的介面註入

如對於角色服務介面來說,它往往和用戶、機構有關係,因此我們在角色的服務介面層,可以整合用戶、機構的對應服務介面,如下代碼所示。

    /// <summary>
    /// 角色信息 應用層服務介面實現
    /// </summary>
    public class RoleService : MyCrudService<RoleInfo,int, RolePagedDto>, IRoleService
    {
        private IOuService _ouService;
        private IUserService _userService;

        /// <summary>
        /// 預設構造函數
        /// </summary>
        /// <param name="ouService">機構服務介面</param>
        /// <param name="userService">用戶服務介面</param>
        public RoleService(IOuService ouService, IUserService userService)
        {
            this._ouService = ouService;
            this._userService = userService;
        }

}

通過構造函數的註入,我們就可以獲得對應介面實現的實例,進行調用它的服務層方法使用了。

這樣我們在角色的服務介面實現中,就可以調用其他如用戶、機構相關的服務介面了。

 其他模塊的處理方式也是類似,如字典項目中,使用字典類型的服務介面。

    /// <summary>
    /// 應用層服務介面實現
    /// </summary>
    public class DictDataService : MyCrudService<DictDataInfo, string, DictDataPagedDto> , IDictDataService
    {
        /// <summary>
        /// 測試字典類型介面
        /// </summary>
        protected IDictTypeService _dictTypeService;

        /// <summary>
        /// 註入方式獲取介面
        /// </summary>
        /// <param name="dictTypeService">字典類型處理</param>
        public DictDataService(IDictTypeService dictTypeService)
        {
            this._dictTypeService = dictTypeService;
        }
}

這裡值得註意的是,由於介面層是同級對象,因此要避免介面的相互引用而導致出錯,依賴關係要清晰,才不會發生這個情況。

 

3、服務介面的實例的容器註冊

在服務層中,我們是通過參數化構造函數的方式,引入對應的介面的,這個操作方式是構造函數的註入處理。

不過在此之前,我們需要在.net 的內置IOC容器中註冊對應的介面實例,否則參數化構造函數會因為找不到介面實例而出錯。

.net 的內置Ioc容器及註冊處理,我們需要在nuget引入下麵兩個引用。

1、Microsoft.Extensions.DependencyInjection
2、Microsoft.Extensions.DependencyInjection.Abstractions

.net 中 負責依賴註入和控制反轉的核心組件有兩個:IServiceCollection和IServiceProvider。其中,IServiceCollection負責註冊,IServiceProvider負責提供實例。

在註冊介面和類時,IServiceCollection提供了三種註冊方法,如下所示:

1、services.AddTransient<IDictDataService, DictDataService>();  // 瞬時生命周期
2、services.AddScoped<IDictDataService, DictDataService>();     // 域生命周期
3、services.AddSingleton<IDictDataService, DictDataService>();  // 全局單例生命周期

如果使用AddTransient方法註冊,IServiceProvider每次都會通過GetService方法創建一個新的實例;

如果使用AddScoped方法註冊, 在同一個域(Scope)內,IServiceProvider每次都會通過GetService方法調用同一個實例,可以理解為在局部實現了單例模式;

如果使用AddSingleton方法註冊, 在整個應用程式生命周期內,IServiceProvider只會創建一個實例。

我們為了在註冊的時候方便通過遍歷方式處理介面實例的註冊,因此我們根據這幾種關係定義了幾個基類介面,便於根據特定的介面方式來構建介面實例。

namespace WHC.Framework.ControlUtil
{
    //用於定義這三種生命周期的標識介面

    /// <summary>
    /// 三種標識介面的基類介面
    /// </summary>
    public interface IDependency
    {
    }
    /// <summary>
    /// 瞬時(每次都重新實例)
    /// </summary>
    public interface ITransientDependency : IDependency
    {
    }
    /// <summary>
    /// 單例(全局唯一)
    /// </summary>
    public interface ISingletonDependency : IDependency
    {        
    }
    /// <summary>
    /// 一個請求內唯一(線程內唯一)
    /// </summary>
    public interface IScopedDependency : IDependency
    {
    }
}

這樣我們在定義註冊類型的時候,通過它的介面指定屬於上面那種類型。如對於字典項目的服務層,我們約定採用瞬時的註冊方式,那麼它的介面定義如下所示。

    /// <summary>
    /// 字典項目服務介面
    /// </summary>
    public interface IDictDataService : IMyCrudService<DictDataInfo, string, DictDataPagedDto>, ITransientDependency
    {
    }

配置自動註冊介面的時候,我們添加如下函數處理即可。

        /// <summary>
        /// 配置依賴註入對象
        /// </summary>
        /// <param name="services"></param>
        public static void ConfigureRepository(IServiceCollection services)
        {
            #region 自動註入對應的服務介面
            //services.AddSingleton<IDictDataService, DictDataService>();//services.AddScoped<IUserService, UserService>();

            var baseType = typeof(IDependency);
            var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
            var getFiles = Directory.GetFiles(path, "*.dll").Where(Match);  //.Where(o=>o.Match())
            var referencedAssemblies = getFiles.Select(Assembly.LoadFrom).ToList();  //.Select(o=> Assembly.LoadFrom(o))         

            var ss = referencedAssemblies.SelectMany(o => o.GetTypes());

            var types = referencedAssemblies
                .SelectMany(a => a.DefinedTypes)
                .Select(type => type.AsType())
                .Where(x => x != baseType && baseType.IsAssignableFrom(x)).ToList();
            var implementTypes = types.Where(x => x.IsClass).ToList();
            var interfaceTypes = types.Where(x => x.IsInterface).ToList();
            foreach (var implementType in implementTypes)
            {
                if (typeof(IScopedDependency).IsAssignableFrom(implementType))
                {
                    var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
                    if (interfaceType != null)
                        services.AddScoped(interfaceType, implementType);
                }
                else if (typeof(ISingletonDependency).IsAssignableFrom(implementType))
                {
                    var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
                    if (interfaceType != null)
                        services.AddSingleton(interfaceType, implementType);
                }
                else
                {
                    var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
                    if (interfaceType != null)
                        services.AddTransient(interfaceType, implementType);
                }
            }
            #endregion
        }

上面根據我們自定義介面的不同,適當的採用不同的註冊方式來加入Ioc容器中,從而實現了介面的註冊,在服務層中就可以通過構造函數註入的方式獲得對應的介面實例了。

這樣,不管是在WInform的啟動模塊中,還是在Web API的啟動模塊中,我們在IOC容器中加入對應的介面即可,如下所示。

/// <summary>
/// 應用程式的主入口點。
/// </summary>
[STAThread]
static void Main()
{
    // IServiceCollection負責註冊
    IServiceCollection services = new ServiceCollection();
    //services.AddSingleton<IDictDataService, DictDataService>();
    //services.AddSingleton<IDictTypeService, DictTypeService>();

    //添加IApiUserSession實現類
    services.AddSingleton<IApiUserSession, ApiUserPrincipal>();

    //調用自定義的服務註冊
    ServiceInjection.ConfigureRepository(services);

    // IServiceProvider負責提供實例
    IServiceProvider provider = services.BuildServiceProvider();
    services.AddSingleton(provider);//註冊到服務集合中,需要可以在Service中構造函數中註入使用

Web API中的代碼如下所示

//添加HTTP上下文訪問
builder.Services.AddHttpContextAccessor();

//配置依賴註入訪問資料庫
ServiceInjection.ConfigureRepository(builder.Services);

//添加IApiUserSession實現類
builder.Services.AddSingleton<IApiUserSession, ApiUserPrincipal>();

var app = builder.Build();

都是類似的處理方式。

同樣在Web API項目中的控制器處理中,也是一樣通過構造函數註入的方式使用介面的,如下所示。

namespace WebApi.Controllers
{
    /// <summary>
    /// 客戶信息的控制器對象
    /// </summary>
    public class CustomerController : BusinessController<CustomerInfo, string, CustomerPagedDto>
    {
        private ICustomerService _customerService;

        /// <summary>
        /// 構造函數,並註入基礎介面對象
        /// </summary>
        /// <param name="customerService"></param>
        public CustomerController(ICustomerService customerService) :base(customerService)
        {
            this._customerService = customerService;
        }
    }
}

或者登錄處理的控制器定義如下。

    /// <summary>
    /// 登錄獲取令牌授權的處理
    /// </summary>
    [Route("api/[controller]")]
    [ApiController]
    public class LoginController : ControllerBase
    {
        private readonly IHttpContextAccessor _contextAccessor;
        private readonly IConfiguration _configuration;
        private readonly IUserService _userService;

        /// <summary>
        /// 令牌失效天數,預設令牌7天有效期
        /// </summary>
        protected const int expiredDays = 7;

        /// <summary>
        /// 構造函數,註入所需介面
        /// </summary>
        /// <param name="configuration">配置對象</param>
        /// <param name="httpContext">HTTP上下文對象</param>
        /// <param name="userService">用戶信息</param>
        public LoginController(IConfiguration configuration, IHttpContextAccessor httpContext, 
            IUserService userService)
        {
            this._configuration = configuration;
            this._contextAccessor = httpContext;
            this._userService = userService;
        }

 

系列文章:

基於SqlSugar的開發框架的循序漸進介紹(1)--框架基礎類的設計和使用

基於SqlSugar的開發框架循序漸進介紹(2)-- 基於中間表的查詢處理

基於SqlSugar的開發框架循序漸進介紹(3)-- 實現代碼生成工具Database2Sharp的整合開發

基於SqlSugar的開發框架循序漸進介紹(4)-- 在數據訪問基類中對GUID主鍵進行自動賦值處理 

基於SqlSugar的開發框架循序漸進介紹(5)-- 在服務層使用介面註入方式實現IOC控制反轉

 《基於SqlSugar的開發框架循序漸進介紹(6)-- 在基類介面中註入用戶身份信息介面 

 

主要研究技術:代碼生成工具、會員管理系統、客戶關係管理軟體、病人資料管理軟體、Visio二次開發、酒店管理系統、倉庫管理系統等共用軟體開發
專註於Winform開發框架/混合式開發框架Web開發框架Bootstrap開發框架微信門戶開發框架的研究及應用
  轉載請註明出處:
撰寫人:伍華聰  http://www.iqidi.com 
    

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 每日一句 軍人天生就捨棄了戰鬥的意義! 概述 RabitMQ 發佈確認,保證消息在磁碟上。 前提條件 1。隊列必須持久化 隊列持久化 2。隊列中的消息必須持久化 消息持久化 使用 三種發佈確認的方式: 1。單個發佈確認 2。批量發佈確認 3。非同步批量發佈確認 開啟發佈確認的方法 //創建一個連接工廠 ...
  • 使用FFmpeg庫做的項目,調試項目的時候發現,連續解視頻進行播放,會有明顯記憶體增加問題。連續工作10個小時後就會 被linux 內核kill掉。 通過逐步註掉代碼和網上查閱資料。最後發現記憶體泄漏有一些幾個地方: 一、av_read_frame的問題 從網上查閱大神們的經驗,主要是av_read_f ...
  • 在Java 9中又新增了一些API來幫助便捷的創建不可變集合,以減少代碼複雜度。 本期配套視頻:Java 9 新特性:快速定義不可變集合 常規寫法 以往我們創建一些不可變集合的時候,通常是這樣寫的: // 不可變的Set Set<String> set = new HashSet<>(); set. ...
  • 反向代理(2022/03/31) 簡單記錄 Nginx 反向代理相關的一些配置文件,描述不足之處請自行查閱相關資料。 1. HTTP 配置 upstream web { server domain.com:80; } server { # 監聽 tcp4 listen 80; # 監聽 tcp6 l ...
  • 爬蟲代理 IP 池及隧道代理 日常開發中,偶爾會遇到爬取網頁數據的需求,為了隱藏本機真實 IP,常常會用到代理 IP 池,本文將基於 openresty 與代理 IP 池搭建更為易用的隧道代理。 1. 代理 IP 池 1.1 簡介 代理 IP 池即在資料庫中維護一個可用的 IP 代理隊列,一般實現思 ...
  • 來源:www.cnblogs.com/keyyang/p/4128424.html 我們可能經常會用到 Thread.Sleep 函數來使線程掛起一段時間。那麼你有沒有正確的理解這個函數的用法呢?思考下麵這兩個問題: 假設現在是 2008-4-7 12:00:00.000,如果我調用一下 Threa ...
  • 1.Docker基本介紹? Docker就是虛擬化的一種輕量級替代技術,基於Go語言的開源應用容器引擎。Docker的容器技術不依賴任何語言、框架或系統,可以將應用程式變成一種標準化的、可移植的、自管理的組件,並脫離伺服器硬體在任何主流系統中開發、調試和運行。 光看這個介紹還不足以知道Docker是 ...
  • 在基於SqlSugar的開發框架中,我們設計了一些系統服務層的基類,在基類中會有很多涉及到相關的數據處理操作的,如果需要跟蹤具體是那個用戶進行操作的,那麼就需要獲得當前用戶的身份信息,包括在Web API的控制器中也是一樣,需要獲得對應的用戶身份信息,才能進行相關的身份鑒別和處理操作。本篇隨筆介紹基... ...
一周排行
    -Advertisement-
    Play Games
  • 前言 插件化的需求主要源於對軟體架構靈活性的追求,特別是在開發大型、複雜或需要不斷更新的軟體系統時,插件化可以提高軟體系統的可擴展性、可定製性、隔離性、安全性、可維護性、模塊化、易於升級和更新以及支持第三方開發等方面的能力,從而滿足不斷變化的業務需求和技術挑戰。 一、插件化探索 在WPF中我們想要開 ...
  • 歡迎ReaLTaiizor是一個用戶友好的、以設計為中心的.NET WinForms項目控制項庫,包含廣泛的組件。您可以使用不同的主題選項對項目進行個性化設置,並自定義用戶控制項,以使您的應用程式更加專業。 項目地址:https://github.com/Taiizor/ReaLTaiizor 步驟1: ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • Channel 是乾什麼的 The System.Threading.Channels namespace provides a set of synchronization data structures for passing data between producers and consume ...
  • efcore如何優雅的實現按年分庫按月分表 介紹 本文ShardinfCore版本 本期主角: ShardingCore 一款ef-core下高性能、輕量級針對分表分庫讀寫分離的解決方案,具有零依賴、零學習成本、零業務代碼入侵適配 距離上次發文.net相關的已經有很久了,期間一直在從事java相關的 ...
  • 前言 Spacesniffer 是一個免費的文件掃描工具,通過使用樹狀圖可視化佈局,可以立即瞭解大文件夾的位置,幫助用戶處理找到這些文件夾 當前系統C盤空間 清理後系統C盤空間 下載 Spacesniffer 下載地址:https://spacesniffer.en.softonic.com/dow ...
  • EDP是一套集組織架構,許可權框架【功能許可權,操作許可權,數據訪問許可權,WebApi許可權】,自動化日誌,動態Interface,WebApi管理等基礎功能於一體的,基於.net的企業應用開發框架。通過友好的編碼方式實現數據行、列許可權的管控。 ...
  • 一、ReZero簡介 ReZero是一款.NET中間件 : 全網唯一開源界面操作就能生成API , 可以集成到任何.NET6+ API項目,無破壞性,也可讓非.NET用戶使用exe文件 免費開源:MIT最寬鬆協議 , 一直從事開源事業十年,一直堅持開源 1.1 純ReZero開發 適合.Net Co ...
  • 一:背景 1. 講故事 停了一個月沒有更新文章了,主要是忙於寫 C#內功修煉系列的PPT,現在基本上接近尾聲,可以回頭繼續更新這段時間分析dump的一些事故報告,有朋友微信上找到我,說他們的系統出現了大量的http超時,程式不響應處理了,讓我幫忙看下怎麼回事,dump也抓到了。 二:WinDbg分析 ...
  • 開始做項目管理了(本人3年java,來到這邊之後真沒想到...),天天開會溝通整理需求,他們講話的時候忙裡偷閑整理一下常用的方法,其實語言還是有共通性的,基本上看到方法名就大概能猜出來用法。出去打水的時候看到外面太陽好好,真想在外面坐著曬太陽,回來的時候好兄弟三年前送給我的鍵盤D鍵不靈了,在打"等待 ...