日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【半小时大话.net依赖注入】(一)理论基础+实战控制台程序实现AutoFac注入

發布時間:2023/12/4 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【半小时大话.net依赖注入】(一)理论基础+实战控制台程序实现AutoFac注入 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
  • 第一章|理論基礎+實戰控制臺程序實現AutoFac注入

  • 第二章|AutoFac的常見使用套路

  • 第三章|實戰Asp.Net Framework Web程序實現AutoFac注入

  • 第四章|實戰Asp.Net Core自帶DI實現依賴注入

  • 第五章|實戰Asp.Net Core引入AutoFac的兩種方式

  • 簡介

    該系列共5篇文章,旨在以實戰模式,在.net下的

    • 控制臺程序

    • Framework Mvc程序

    • Framework WebApi程序

    • Core Api程序

    分別實現依賴注入。

    其中.Net Framework框架主要以如何引入AutoFac作為容器以及如何運用AuotoFac為主,.Net Core框架除了研究引入AutoFac的兩種方式,同時也運用反射技巧對其自帶的DI框架進行了初步封裝,實現了相同的依賴注入效果。
    項目架構如下圖:

    Ray.EssayNotes.AutoFac.Infrastructure.CoreIocCore容器類庫.NET Core 2.2
    Ray.EssayNotes.AutoFac.Infrastructure.IocFramework容器類庫.NET Framework 4.5
    Ray.EssayNotes.AutoFac.Model實體層類庫.NET Framework 4.5
    Ray.EssayNotes.AutoFac.Repository倉儲層類庫.NET Framework 4.5
    Ray.EssayNotes.AutoFac.Service業務邏輯層類庫.NET Framework 4.5
    Ray.EssayNotes.AutoFac.ConsoleApp控制臺主程序控制臺項目.NET Framework 4.5
    Ray.EssayNotes.AutoFac.CoreApiCore WebApi主程序Core Api項目.NET Core 2.2
    Ray.EssayNotes.AutoFac.NetFrameworkApiFramework WebApi主程序Framework WebApi項目.NET Framework 4.5
    Ray.EssayNotes.AutoFac.NetFrameworkMvcFramework MVC主程序Framework MVC項目.NET Framework 4.5

    GitHub源碼地址:https://github.com/WangRui321/Ray.EssayNotes.AutoFac

    Welcome to fork me~(歡迎來叉我~)

    適用對象

    該項目主要實戰為主,理論部分我會結合例子和代碼,深入淺出地闡述,如果你是:

    • 從來沒聽過IoC、DI這些勞什子

    • 了解一些依賴注入的理論知識但是缺乏實戰

    • 在.Net Framework下已熟練運用依賴注入,但在.Net Core還比較陌生

    只要你花上半個小時認真讀完每一句話,我有信心這篇文章一定會對你有所幫助。

    如果你是:

    那么也歡迎閱讀,雖然可能對你幫助并不大,但是歡迎提供寶貴的意見,有寫的不好的地方可以互相交流~

    下面開始第一章《理論知識+實戰控制臺程序實現AutoFac注入》


    依賴

    依賴,簡單說就是,當一個類需要另一個類協作來完成工作的時候就產生了依賴。這也是耦合的一種形式。

    舉個例子,比如標準的三層架構模式

    界面層(UI)負責展示數據StudentController
    業務邏輯層(BLL)負責業務邏輯運算StudentService
    數據訪問層(DAL)負責提供數據StudentRepository

    數據訪問層(DAL)代碼:

    public class StudentRepository { public string GetName(long id) { return "學生張三"; } }

    業務層(BLL)代碼:

    public class StudentService { private readonly StudentRepository _studentRepository; public StudentService() { _studentRepository = new StudentRepository(); } public string GetStuName(long id) { var stu = _studentRepository.Get(id); return stu.Name; } }

    其中,StudentService的實現,就必須要依賴于StudentRepository。而且這是一種緊耦合,一旦StudentRepository有任何更改,必然導致StudentService的代碼同樣也需要更改,這種情況是程序員們不愿意看到的。

    接口驅動

    接口驅動是為了實現一個設計原則:要依賴于抽象,而不是具體的實現。
    還拿上面的例子說明,現在我添加一個DAL的接口層,IStudentRepository,抽象出所需方法:

    public interface IStudentRepository { string GetName(long id); }

    然后讓StudentRepository去實現這個接口:

    public class StudentRepository : IStudentRepository { public string GetName(long id) { return "學生張三"; } }

    然后在StudentService里只依賴于IStudentRepository,以后的增刪改查都通過IStudentRepository這個抽象來做:

    public class StudentService { private readonly IStudentRepository _studentRepository; public StudentService() { _studentRepository = new StudentRepository(); } public string GetStuName(long id) { var stu = _studentRepository.Get(id); return stu.Name; } }

    這樣做的好處有兩個,一個是低耦合,一個是職責清晰。如果對此還有懷疑的話,我們可以想象一個情景,就是負責寫StudentService的是程序員A,負責寫StudentRepository的是另一個程序員B,那么:

    • 針對程序員A

    我(程序員A)只需要關注業務邏輯層面,如果我需要從倉儲層拿數據庫的數據,比如我需要根據Id獲取學生實體,那么我只需要去IStudentRepository找Get(long id)函數就可以了,至于實現它的倉儲怎么實現這個方法我完全不用管,你怎么從數據庫拿數據不是我該關心的事情。

    • 針對程序員B

    我(程序員B)的工作就是實現IStudentRepository接口的所有方法就行了,簡單而明確,至于誰來調用我,我不用管。IStudentRepository里有根據Id獲取學生姓名的方法,我實現了就行,至于業務邏輯層拿這個名字干啥,那不是我要關心的事情。

    這樣看的話是不是彼此的職責就清晰多了,更進一步再舉個極端的例子:
    比如程序員B是個實習生,整天劃水摸魚,技術停留在上個世紀,結果他寫的倉儲層讀取數據庫全部用的手寫sql語句的方式,極難維護,后來被領導發現領了盒飯,公司安排了另一個程序員C來重寫倉儲層,C這時不需要動其他代碼,只需要新建一個倉儲StudentNewRepository,然后實現之前的IStudentRepository,C使用Dapper或者EF,寫完新的倉儲層之后,剩下的只需要在StudentService里改一個地方就行了:

    public StudentService() { _studentRepository = new StudentNewRepository(); }

    是不是很清晰,耦合不會像以前那么重。
    其實對于這個小例子來說,接口驅動的優勢還不太明顯,但是在系統層面優勢就會被放大。比如上面換倉儲的例子,雖然職責是清晰了,但是項目里有幾個Service就需要改幾個地方,還是很麻煩。原因就是上面講的,這是一種依賴關系,Service要依賴Repository,有沒有一種方法可以讓這種控制關系反轉過來呢?當Service需要使用Repository,有沒有辦法讓我需要的Repository自己注入到我這里來?
    當然有,這就是我們將要實現的依賴注入。使用依賴注入后你會發現,當C寫完新的倉儲后,業務邏輯層(StudentService)是不需要改任何代碼的,所有的Service都不需要一個一個去改,直接在注入的時候修改規則,不要注入以前老的直接注入新的倉儲就可以了。

    面向接口后的架構:

    界面層(UI)負責展示數據StudentController
    業務邏輯抽象層(InterfaceBLL)業務邏輯運算抽象接口IStudentService
    業務邏輯層(BLL)負責業務邏輯運算StudentService
    數據訪問抽象層(InterfaceDAL)數據訪問抽象接口IStudentRepository
    數據訪問層(DAL)負責提供數據StudentRepository

    什么是IoC

    IoC,全稱Inversion of Control,即“控制反轉”,是一種設計原則,最早由Martin Fowler提出,因為其理論提出時間和成熟時間相對較晚,所以并沒有被包含在GoF的《設計模式》中。

    什么是DI

    DI,全稱Dependency Injection,即依賴注入,是實現IoC的其中一種設計方法。
    其特征是通過一些技巧,將依賴的對象注入到調用者當中。(比如把Repository注入到Service當中)
    這里說的技巧目前主要指的就是引入容器,先把所有會產生依賴的對象統一添加到容器當中,比如StudentRepository和StudentService,把分配權限交給容器,當StudentService內部需要使用StudentRepository時,這時不應該讓它自己new出來一個,而是通過容器,把StudentRepository注入到StudentService當中。
    這就是名稱“依賴注入”的由來。

    DI和IoC有什么區別

    這是個老生常談的問題了,而且這兩個名字經常在各種大牛和偽大牛的吹逼現場頻繁出現 ,聽的新手云里霧里,莫名感到神圣不可侵犯。那么DI和IoC是同一個東西嗎?如果不是,它們又有什么區別呢?
    回答很簡單:不是一個東西。
    區別也很簡單,一句話概括就是:IoC是一種很寬泛的理念,DI是實現了IoC的其中一種方法。
    說到這里我已經感覺到屏幕后的你性感地添了一下嘴唇,囤積好口水,準備開始噴我了。
    先別慌,我有證據,我們先來看下微軟怎么說:

    ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies.

    地址:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2

    翻譯過來就是“ASP.NET Core支持依賴注入(DI)的軟件設計模式,該模式是一種在類和它依賴的對象之間實現了控制反轉(IoC)的技術”。

    如果有人覺得辣雞微軟不夠權威,那我們去看下IoC以及DI這兩個概念的發明人——Martin Fowler怎么說:

    幾位輕量級容器的作者曾驕傲地對我說:這些容器非常有用,因為它們實現了控制反轉。這樣的說辭讓我深感迷惑:控制反轉是框架所共有的特征,如果僅僅因為使用了控制反轉就認為這些輕量級容器與眾不同,就好象在說我的轎車是與眾不同的,因為它有四個輪子。
    因此,我想我們需要給這個模式起一個更能說明其特點的名字——”控制反轉”這個名字太泛了,常常讓人有些迷惑。經與多位IoC 愛好者討論之后,我們決定將這個模式叫做”依賴注入”(Dependency Injection)。

    地址:http://insights.thoughtworkers.org/injection/

    Martin Fowler說的比較委婉,其實說白了就是建議我們,不要亂用IoC裝逼,IoC是一種設計理念,很寬泛,你把程序里的一個寫死的變量改成從配置文件里讀取也是一種控制反轉(由程序控制反轉為由框架控制),你把這個配置改成用戶UI界面的一個輸入文本框由用戶輸入也是一種控制反轉(由框架控制反轉為由用戶自己控制)。
    所以,如果確定討論的模式是DI,那么就表述為DI,還是盡量少用IoC這種寬泛的表達。

    AutoFac

    AutoFac是一個開源的輕量級的DI容器,也是.net下最受大家歡迎的實現依賴注入的工具之一,通過AutoFac我們可以很方便的實現一些DI的騷操作。

    目標很簡單,就是控制臺程序啟動后,將學生姓名打印出來。
    程序啟動流程是,控制臺主程序調用Service層,Service層調用Repository層獲取數據(示例項目的倉儲層沒有連接數據庫,只是直接造個假數據返回)。
    沒有依賴注入的情況下,肯定是主程序會new一個StudentService,StudentService里會new一個StudentRepository,現在引入依賴注入后,就不應該這么new出來了,而是通過容器注入,也就是容器會把StudentRepository自動注入到StudentService當中。

    架構

    實體層

    學生實體類StudentEntity:

    namespace Ray.EssayNotes.AutoFac.Model{ public class StudentEntity { public long Id { get; set; } public string Name { get; set; } public int Grade { get; set; } }}

    倉儲層

    IStudentRepository接口:

    using Ray.EssayNotes.AutoFac.Model;namespace Ray.EssayNotes.AutoFac.Repository.IRepository{ public interface IStudentRepository { string GetName(long id); }}

    StudentRepository倉儲類:

    using Ray.EssayNotes.AutoFac.Model;using Ray.EssayNotes.AutoFac.Repository.IRepository;namespace Ray.EssayNotes.AutoFac.Repository.Repository{ public class StudentRepository : IStudentRepository { public string GetName(long id) { return "學生張三"; } }}

    Service層

    IStudentService接口

    namespace Ray.EssayNotes.AutoFac.Service.IService{ public interface IStudentService { string GetStuName(long id); }}

    StudentService類:

    using Ray.EssayNotes.AutoFac.Repository.IRepository;using Ray.EssayNotes.AutoFac.Repository.Repository;using Ray.EssayNotes.AutoFac.Service.IService;namespace Ray.EssayNotes.AutoFac.Service.Service{ public class StudentService : IStudentService { private readonly IStudentRepository _studentRepository; public StudentService(IStudentRepository studentRepository) { _studentRepository = studentRepository; } public string GetStuName(long id) { var stu = _studentRepository.Get(id); return stu.Name; } }}

    其中構造函數是一個有參的函數,參數是學生倉儲,這個后面依賴注入時會用。

    AutoFac容器

    需要先通過Nuget導入Autofac包:

    using System;using System.Reflection;using Autofac;using Autofac.Core;using Ray.EssayNotes.AutoFac.Repository.IRepository;using Ray.EssayNotes.AutoFac.Repository.Repository;using Ray.EssayNotes.AutoFac.Service.IService;using Ray.EssayNotes.AutoFac.Service.Service;namespace Ray.EssayNotes.AutoFac.Infrastructure.Ioc{ public static class Container { public static IContainer Instance; public static void Init() { var builder = new ContainerBuilder(); MyBuild(builder); Instance = builder.Build(); } public static void MyBuild(ContainerBuilder builder) { builder.RegisterType<StudentRepository>().As<IStudentRepository>(); builder.RegisterType<StudentService>().As<IStudentService>(); } }}

    其中:

    • public static IContainer Instance
      為單例容器

    • Init()方法
      用于初始化容器,即往容器中添加對象,我們把這個添加的過程稱為注冊(Register)。
      ContainerBuilder為AutoFac定義的容器構造器,我們通過使用它往容器內注冊對象。

    • MyBuild(ContainerBuilder builder)方法
      我們具體注冊的實現函數。RegisterType是AutoFac封裝的一種最基本的注冊方法,傳入的泛型(StudentService)就是我們欲添加到容器的對象;As函數負責綁定注冊對象的暴露類型,一般是以其實現的接口類型暴露,這個暴露類型是我們后面去容器內查找對象時使用的搜索標識,我們從容器外部只有通過暴露類型才能找到容器內的對象。

      主程序

    需要先Nuget導入AutoFac程序包:

    using System;using Autofac;using Ray.EssayNotes.AutoFac.Infrastructure.Ioc;using Ray.EssayNotes.AutoFac.Service.IService;namespace Ray.EssayNotes.AutoFac.ConsoleApp{ class Program { static void Main(string[] args) { Container.Init(); PrintStudentName(10001); Console.ReadKey(); } public static void PrintStudentName(long id) { IStudentService stuService = Container.Instance.Resolve<IStudentService>(); string name = stuService.GetStuName(id); Console.WriteLine(name); } } }

    進入Main函數,先調用容器的初始化函數,該函數執行成功后,StudentRepository和StudentService就被注冊到容器中了。
    然后調用打印學生姓名的函數,其中Resolve()方法是AutoFac封裝的容器的解析方法,傳入的泛型就是之前注冊時的暴露類型,下面可以詳細看下這一步到底發生了哪些事情:

    • 容器根據暴露類型解析對象

    也就是容器會根據暴露類型IStudentService去容器內部找到其對應類(即StudentService),找到后會試圖實例化一個對象出來。

    • 實例化StudentService

    AutoFac容器在解析StudentService的時候,會調用StudentService的構造函數進行實例化。

    • 構造注入

    AutoFac容器發現StudentService的構造函數需要一個IStudnetRepository類型的參數,于是會自動去容器內尋找,根據這個暴露類型找到對應的StudnetRepository后,自動將其注入到了StudentService當中

    經過這幾步,一個簡單的基于依賴注入的程序就完成了。

    結果

    我們將控制臺程序設置為啟動項目,點擊運行,如圖調用成功:

    如果把調試斷點加在容器初始化函數里,可以很清晰的看到哪些對象被注冊到了容器里:

    使用控制臺程序本來是為了突出容器的概念,但是容易造成一些誤解,DI的最終形態可以參考源碼里的Api項目和MVC項目,本來想循序漸進,先第一章控制臺引入容器的概念,然后第二章講批量注冊、注入泛型、生命周期域管理,第三章講Api和MVC項目,最后兩章講下.net core的DI,但是這里還是先說下吧:

    • 誤解1:每次添加Service和Repository都要去注冊,不是更麻煩?

    其實是不需要一個一個注冊的,運用批量注冊后容器內部的代碼是這樣的,可以直接批量注冊所有的:

    public static class MvcContainer { public static IContainer Instance; public static void Init(Func<ContainerBuilder, ContainerBuilder> func = null) { var builder = new ContainerBuilder(); MyBuild(builder); func?.Invoke(builder); Instance = builder.Build(); System.Web.Mvc.DependencyResolver.SetResolver(new AutofacDependencyResolver(Instance)); } public static void MyBuild(ContainerBuilder builder) { Assembly[] assemblies = Helpers.ReflectionHelper.GetAllAssembliesWeb(); builder.RegisterAssemblyTypes(assemblies) .Where(cc => cc.Name.EndsWith("Repository") | cc.Name.EndsWith("Service")) .PublicOnly() .Where(cc => cc.IsClass) .AsImplementedInterfaces(); builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IBaseRepository<>)); Assembly mvcAssembly = assemblies.FirstOrDefault(x => x.FullName.Contains(".NetFrameworkMvc")); builder.RegisterControllers(mvcAssembly); } }

    誤解2:每次使用都要解析下,還不如直接new
    好吧,其實也是不需要自己去解析的,最終形態的Controller入口是這樣的,直接在構造函數里寫就行了:

    public class StudentController : Controller { private readonly IStudentService _studentService; public StudentController(IStudentService studentService) { _studentService = studentService; } public string GetStuNameById(long id) { return _studentService.GetStuName(id); } }

    就是直接在構造函數里注入就可以了。

    • 誤解3:依賴注入是不是過度設計?

    首先DI是一個設計模式(design pattern),其本身完全不存在過不過度的問題,這完全取決于用的人和怎么用。
    另外,在.NET Core中,DI被提到了一個很重要的地位,如果想要了解.NET Core,理解DI是必不可少的。

    原文地址:https://www.cnblogs.com/RayWang/p/11128554.html


    .NET社區新聞,深度好文,歡迎訪問公眾號文章匯總?http://www.csharpkit.com?

    總結

    以上是生活随笔為你收集整理的【半小时大话.net依赖注入】(一)理论基础+实战控制台程序实现AutoFac注入的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。