<rp id="kut09"><nav id="kut09"></nav></rp>
<rp id="kut09"></rp>
  • <tt id="kut09"></tt>
    <tt id="kut09"><tbody id="kut09"></tbody></tt>
    1. <u id="kut09"></u>
    2. <tt id="kut09"><noscript id="kut09"></noscript></tt>
    3. 詳解C#中的依賴注入和IoC容器

       更新時間:2020年12月29日 14:17:30   作者:碼農驛站  
      這篇文章主要介紹了C#中的依賴注入和IoC容器的相關資料,幫助大家更好的理解和使用c#,感興趣的朋友可以了解下

      在本文中,我們將通過用C#重構一個非常簡單的代碼示例來解釋依賴注入和IoC容器。 

      簡介:

      依賴注入和IoC乍一看可能相當復雜,但它們非常容易學習和理解。

      在本文中,我們將通過在C#中重構一個非常簡單的代碼示例來解釋依賴注入和IoC容器。

      要求:

      構建一個允許用戶查看可用產品并按名稱搜索產品的應用程序。

      第一次嘗試:

      我們將從創建分層架構開始。使用分層架構有多個好處,但我們不會在本文中列出它們,因為我們關注的是依賴注入。

      下面是應用程序的類圖:

      首先,我們將從創建一個Product類開始:

      public class Product
      {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
      }

      然后,我們將創建數據訪問層:

      public class ProductDAL
      {
        private readonly List<Product> _products;
      
        public ProductDAL()
        {
          _products = new List<Product>
          {
            new Product { Id = Guid.NewGuid(), Name= "iPhone 9", 
                   Description = "iPhone 9 mobile phone" },
            new Product { Id = Guid.NewGuid(), Name= "iPhone X", 
                   Description = "iPhone X mobile phone" }
          };
        }
      
        public IEnumerable<Product> GetProducts()
        {
          return _products;
        }
      
        public IEnumerable<Product> GetProducts(string name)
        {
          return _products
            .Where(p => p.Name.Contains(name))
            .ToList();
        }
      }

      然后,我們將創建業務層:

      public class ProductBL
      {
        private readonly ProductDAL _productDAL;
      
        public ProductBL()
        {
          _productDAL = new ProductDAL();
        }
      
        public IEnumerable<Product> GetProducts()
        {
          return _productDAL.GetProducts();
        }
      
        public IEnumerable<Product> GetProducts(string name)
        {
          return _productDAL.GetProducts(name);
        }
      }

      最后,我們將創建UI:

      class Program
      {
        static void Main(string[] args)
        {
          ProductBL productBL = new ProductBL();
      
          var products = productBL.GetProducts();
      
          foreach (var product in products)
          {
            Console.WriteLine(product.Name);
          }
      
          Console.ReadKey();
        }
      }

      我們已經寫在第一次嘗試的代碼是良好的工作成果,但有幾個問題:

      1.我們不能讓三個不同的團隊在每個層上工作。

      2.業務層很難擴展,因為它依賴于數據訪問層的實現。

      3.業務層很難維護,因為它依賴于數據訪問層的實現。

      4.源代碼很難測試。

      第二次嘗試:

      高級別對象不應該依賴于低級別對象。兩者都必須依賴于抽象。那么抽象概念是什么呢?

      抽象是功能的定義。在我們的例子中,業務層依賴于數據訪問層來檢索圖書。在C#中,我們使用接口實現抽象。接口表示功能的抽象。

      讓我們來創建抽象。

      下面是數據訪問層的抽象:

      public interface IProductDAL
      {
        IEnumerable<Product> GetProducts();
        IEnumerable<Product> GetProducts(string name);
      }

      我們還需要更新數據訪問層:

      public class ProductDAL : IProductDAL

      我們還需要更新業務層。實際上,我們將更新業務層,使其依賴于數據訪問層的抽象,而不是依賴于數據訪問層的實現:

      public class ProductBL
      {
        private readonly IProductDAL _productDAL;
      
        public ProductBL()
        {
          _productDAL = new ProductDAL();
        }
      
        public IEnumerable<Product> GetProducts()
        {
          return _productDAL.GetProducts();
        }
      
        public IEnumerable<Product> GetProducts(string name)
        {
          return _productDAL.GetProducts(name);
        }
      }

      我們還必須創建業務層的抽象:

      public interface IProductBL
      {
        IEnumerable<Product> GetProducts();
        IEnumerable<Product> GetProducts(string name);
      }

      我們也需要更新業務層:

      public class ProductBL : IProductBL

      最終我們需要更新UI:

      class Program
      {
        static void Main(string[] args)
        {
          IProductBL productBL = new ProductBL();
      
          var products = productBL.GetProducts();
      
          foreach (var product in products)
          {
            Console.WriteLine(product.Name);
          }
      
          Console.ReadKey();
        }
      }

      我們在第二次嘗試中所做的代碼是有效的,但我們仍然依賴于數據訪問層的具體實現:

      public ProductBL()
      {
        _productDAL = new ProductDAL();
      }

      那么,如何解決呢?

      這就是依賴注入模式發揮作用的地方。

      最終嘗試

      到目前為止,我們所做的工作都與依賴注入無關。

      為了使處在較高級別的的業務層依賴于較低級別對象的功能,而沒有具體的實現,必須由其他人創建類。其他人必須提供底層對象的具體實現,這就是我們所說的依賴注入。它的字面意思是我們將依賴對象注入到更高級別的對象中。實現依賴項注入的方法之一是使用構造函數進行依賴項注入。

      讓我們更新業務層:

      public class ProductBL : IProductBL
      {
        private readonly IProductDAL _productDAL;
      
        public ProductBL(IProductDAL productDAL)
        {
          _productDAL = productDAL;
        }
      
        public IEnumerable<Product> GetProducts()
        {
          return _productDAL.GetProducts();
        }
      
        public IEnumerable<Product> GetProducts(string name)
        {
          return _productDAL.GetProducts(name);
        }
      }

      基礎設施必須提供對實現的依賴:

      class Program
      {
        static void Main(string[] args)
        {
          IProductBL productBL = new ProductBL(new ProductDAL());
      
          var products = productBL.GetProducts();
      
          foreach (var product in products)
          {
            Console.WriteLine(product.Name);
          }
      
          Console.ReadKey();
        }
      }

      創建數據訪問層的控制與基礎設施結合在一起。這也稱為控制反轉。我們不是在業務層中創建數據訪問層的實例,而是在基礎設施的中創建它。 Main方法將把實例注入到業務邏輯層。因此,我們將低層對象的實例注入到高層對象的實例中。

      這叫做依賴注入。

      現在,如果我們看一下代碼,我們只依賴于業務訪問層中數據訪問層的抽象,而業務訪問層是使用的是數據訪問層實現的接口。因此,我們遵循了更高層次對象和更低層次對象都依賴于抽象的原則,抽象是更高層次對象和更低層次對象之間的契約。

      現在,我們可以讓不同的團隊在不同的層上工作。我們可以讓一個團隊處理數據訪問層,一個團隊處理業務層,一個團隊處理UI。

      接下來就顯示了可維護性和可擴展性的好處。例如,如果我們想為SQL Server創建一個新的數據訪問層,我們只需實現數據訪問層的抽象并將實例注入基礎設施中。

      最后,源代碼現在是可測試的了。因為我們在任何地方都使用接口,所以我們可以很容易地在較低的單元測試中提供另一個實現。這意味著較低的測試將更容易設置。

      現在,讓我們測試業務層。

      我們將使用xUnit進行單元測試,使用Moq模擬數據訪問層。

      下面是業務層的單元測試:

      public class ProductBLTest
      {
        private readonly List<Product> _products = new List<Product>
        {
          new Product { Id = Guid.NewGuid(), Name= "iPhone 9", 
                 Description = "iPhone 9 mobile phone" },
          new Product { Id = Guid.NewGuid(), Name= "iPhone X", 
                 Description = "iPhone X mobile phone" }
        };
        private readonly ProductBL _productBL;
      
        public ProductBLTest()
        {
          var mockProductDAL = new Mock<IProductDAL>();
          mockProductDAL
            .Setup(dal => dal.GetProducts())
            .Returns(_products);
          mockProductDAL
            .Setup(dal => dal.GetProducts(It.IsAny<string>()))
            .Returns<string>(name => _products.Where(p => p.Name.Contains(name)).ToList());
      
          _productBL = new ProductBL(mockProductDAL.Object);
        }
      
        [Fact]
        public void GetProductsTest()
        {
          var products = _productBL.GetProducts();
          Assert.Equal(2, products.Count());
        }
      
        [Fact]
        public void SearchProductsTest()
        {
          var products = _productBL.GetProducts("X");
          Assert.Single(products);
        }
      }

      你可以看到,使用依賴項注入很容易設置單元測試。

      IoC容器

      容器只是幫助實現依賴注入的東西。容器,通常實現三種不同的功能:

      1.注冊接口和具體實現之間的映射

      2.創建對象并解析依賴關系

      3.釋放

      讓我們實現一個簡單的容器來注冊映射并創建對象。

      首先,我們需要一個存儲映射的數據結構。我們將選擇Hashtable。該數據結構將存儲映射。

      首先,我們將在容器的構造函數中初始化Hashtable。然后,我們將創建一個RegisterTransient方法來注冊映射。最后,我們會創建一個創建對象的方法 Create :

      public class Container
      {
        private readonly Hashtable _registrations;
      
        public Container()
        {
          _registrations = new Hashtable();
        }
      
        public void RegisterTransient<TInterface, TImplementation>()
        {
          _registrations.Add(typeof(TInterface), typeof(TImplementation));
        }
      
        public TInterface Create<TInterface>()
        {
          var typeOfImpl = (Type)_registrations[typeof(TInterface)];
          if (typeOfImpl == null)
          {
            throw new ApplicationException($"Failed to resolve {typeof(TInterface).Name}");
          }
          return (TInterface)Activator.CreateInstance(typeOfImpl);
        }
      }

      最終,我們會更新UI:

      class Program
      {
        static void Main(string[] args)
        {
          var container = new Container();
          container.RegisterTransient<IProductDAL, ProductDAL>();
      
          IProductBL productBL = new ProductBL(container.Create<IProductDAL>());
          var products = productBL.GetProducts();
      
          foreach (var product in products)
          {
            Console.WriteLine(product.Name);
          }
      
          Console.ReadKey();
        }
      }

      現在,讓我們在容器中實現Resolve方法。此方法將解決依賴關系。

      Resolve方法如下:

      public T Resolve<T>()
      {
          var ctor = ((Type)_registrations[typeof(T)]).GetConstructors()[0];
          var dep = ctor.GetParameters()[0].ParameterType;
          var mi = typeof(Container).GetMethod("Create");
          var gm = mi.MakeGenericMethod(dep);
          return (T)ctor.Invoke(new object[] { gm.Invoke(this, null) });
      }

      然后我們可以在UI中使用如下Resolve方法:

      class Program
      {
          static void Main(string[] args)
          {
              var container = new Container();
              container.RegisterTransient<IProductDAL, ProductDAL>();
              container.RegisterTransient<IProductBL, ProductBL>();
      
              var productBL = container.Resolve<IProductBL>();
              var products = productBL.GetProducts();
      
              foreach (var product in products)
              {
                  Console.WriteLine(product.Name);
              }
      
              Console.ReadKey();
          }
      }

      在上面的源代碼中,容器使用container.Resolve<IProductBL>()方法創建ProductBL類的一個對象。ProductBL類是IProductDAL的一個依賴項。因此,container.Resolve<IProductBL>() 通過自動創建并在其中注入一個ProductDAL對象返回ProductBL類的一個對象。這一切都在幕后進行。創建和注入ProductDAL對象是因為我們用IProductDAL注冊了ProductDAL類型。

      這是一個非常簡單和基本的IoC容器,它向你展示了IoC容器背后的內容。就是這樣。我希望你喜歡閱讀這篇文章。

      以上就是詳解C#中的依賴注入和IoC容器的詳細內容,更多關于C# 依賴注入和IoC容器的資料請關注腳本之家其它相關文章!

      相關文章

      • C#實現在Form里面內嵌dos窗體的方法

        C#實現在Form里面內嵌dos窗體的方法

        這篇文章主要介紹了C#實現在Form里面內嵌dos窗體的方法,涉及C#針對Form窗體的設置及使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下
        2015-09-09
      • C#中實現屏蔽Ctrl+C的方法

        C#中實現屏蔽Ctrl+C的方法

        這篇文章主要介紹了C#中實現屏蔽Ctrl+C的方法,在C#應用程序開發中有一定的實用價值,需要的朋友可以參考下
        2014-08-08
      • 簡述C#枚舉高級戰術

        簡述C#枚舉高級戰術

        這篇文章主要介紹了簡述C#枚舉高級戰術,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
        2020-10-10
      • C#中參數個數可變的方法實例分析

        C#中參數個數可變的方法實例分析

        這篇文章主要介紹了C#中參數個數可變的方法,以一個簡單實例分析了C#中參數個數可變的方法,主要是使用params關鍵字來實現的,是C#編程中比較實用的技巧,需要的朋友可以參考下
        2014-11-11
      • C#簡單查詢SQLite數據庫是否存在數據的方法

        C#簡單查詢SQLite數據庫是否存在數據的方法

        這篇文章主要介紹了C#簡單查詢SQLite數據庫是否存在數據的方法,涉及C#調用SQLite組件及針對SQLite數據庫基本的連接、查詢、關閉等使用技巧,需要的朋友可以參考下
        2016-07-07
      • c#3.0實現延遲賦值示例

        c#3.0實現延遲賦值示例

        這篇文章主要介紹了c#3.0實現延遲賦值示例,大家參考使用吧
        2014-01-01
      • C#注釋的一些使用方法淺談

        C#注釋的一些使用方法淺談

        C#注釋的一些使用方法淺談,需要的朋友可以參考一下
        2013-04-04
      • 使用C#調用百度地圖并實現坐標點的設置以及讀取示例

        使用C#調用百度地圖并實現坐標點的設置以及讀取示例

        這篇文章主要介紹了使用C#調用百度地圖并實現坐標點的設置以及讀取示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
        2020-07-07
      • c# 中文轉拼音without CJK

        c# 中文轉拼音without CJK

        本文主要介紹了中文轉拼音without CJK,文章篇尾附上源碼下載。具有一定的參考價值,下面跟著小編一起來看下吧
        2017-02-02
      • C#分屏控件用法實例

        C#分屏控件用法實例

        這篇文章主要介紹了C#分屏控件用法實例,需要的朋友可以參考下
        2014-08-08

      最新評論

      218彩票 www.tjshuxin.com:盐津县| www.hooterspanama.com:交城县| www.storevalentine.com:宽城| www.nillinternational.com:凤台县| www.fromussr.com:偏关县| www.cp1107.com:厦门市| www.amnestyforanimals.org:长垣县| www.vermord.com:河津市| www.greenvocational.com:乌兰县| www.thsxled.com:广昌县| www.gzmaituo.com:资讯| www.balchdercymru.com:祁东县| www.jljxg.cn:铜梁县| www.cleanhouselimpeza.com:界首市| www.781312.com:延安市| www.suenoshumedos.com:濉溪县| www.brqxbjgs.com:沂南县| www.wisataboyolali.com:江油市| www.723421.com:裕民县| www.whatssparkling.com:虞城县| www.logosheji8.com:罗甸县| www.xashanjia.com:万山特区| www.kinostream.net:册亨县| www.amdc49.com:亚东县| www.brand-gate.com:正蓝旗| www.4008557888.com:香河县| www.glitznglow.com:日土县| www.jd2002.net:临海市| www.comapt.com:都匀市| www.gjcuk.com:伊川县| www.vmorepro.com:庄河市| www.troop100bsa.com:新和县| www.shnanyabxg.com:美姑县| www.commonelementllc.com:西平县| www.newclassicsingers.org:英吉沙县| www.arzummodaevi.com:永兴县| www.maskanshomal.com:晋州市| www.wodacorp.com:梨树县| www.asiannet21.org:上饶市| www.hirdavatciyiz.com:莱芜市| www.nation-wide-building.com:贵定县| www.adipexdietpillblog.com:高雄县| www.tao2u.com:游戏| www.sijiecn.com:沾益县| www.dizdiva.com:东阿县| www.raysh-ic.com:兖州市| www.rentanaudience.com:金寨县| www.getallsites.com:黄大仙区| www.dlm-music.com:平果县| www.xkfan.com:洛浦县| www.tiehimup.com:荔浦县| www.airotours.com:喀喇| www.david-bird.com:稷山县| www.autocity-curacao.com:高阳县| www.hzjjqp.com:汝阳县| www.internationalchalice.com:澳门| www.dementiaonourminds.com:新余市| www.tangoporteno.org:夹江县| www.charitybackpackers.com:任丘市| www.xirunjiaoyu.com:保德县| www.sertep.com:屏东市| www.ipadwallpaperhd.com:顺平县| www.cp2776.com:都兰县| www.cyber-sst.com:安顺市| www.catchyenough.com:茌平县| www.coachyn.com:稷山县| www.520lei.com:佛教| www.s9893.com:新化县| www.emedicalweb.com:芮城县| www.ramadawg.com:苍溪县| www.slooking.com:香河县| www.vsexpesenok.net:两当县| www.myqccoupons.com:福州市| www.lizsalmon.com:鹿泉市| www.theconeyisland.com:老河口市| www.zen-moa-massage.com:镇宁| www.rdrpw.cn:阿鲁科尔沁旗| www.museumsinhoustontx.com:穆棱市| www.toygrc.com:宝应县| www.ef787.com:苍南县| www.toto-ya.com:宣城市| www.xawydz.com:平泉县| www.fionatate.com:纳雍县| www.wine2africa.com:金阳县|