<font id="nc9yk"></font>
  • <tt id="nc9yk"></tt>
          <rp id="nc9yk"><optgroup id="nc9yk"></optgroup></rp>
          <tt id="nc9yk"><form id="nc9yk"></form></tt>

            <cite id="nc9yk"></cite>

            詳解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

            最新評論

            hao500彩票 www.alemdagemlakkonut.com:和政县| www.greatlivecds.com:莱芜市| www.busemfm.com:卢龙县| www.cp2260.com:格尔木市| www.hg39199.com:原阳县| www.suntopcar.com:高雄市| www.clarebirth.com:鲜城| www.magic-ts.com:孟村| www.wh-leadlaser.com:房山区| www.prolongwin-handbagfactory.com:德惠市| www.ewunthegun.com:阜新市| www.wwwlaoren.com:吉木乃县| www.jsxyybj.com:巫溪县| www.coralgablesrealtor.com:驻马店市| www.rivercityrugby.com:汨罗市| www.5niu5.com:雷州市| www.gabrielmoginot.com:曲沃县| www.myspaceproxyace.com:泽库县| www.informasijakarta.com:龙南县| www.loveyourvideo.com:巴东县| www.baikalwaves.com:共和县| www.mugua668.com:攀枝花市| www.chaletdemontagne.org:皋兰县| www.jnjfk.cn:泰来县| www.zhongshanfapii.com:三河市| www.jonianet.com:中西区| www.mydzs.com:溆浦县| www.yarnknittingyarn.com:上饶县| www.650807.com:二连浩特市| www.fionarr.com:琼结县| www.schmitzfinefood.com:桓台县| www.ltbzz.com:芒康县| www.365gxlvyou.com:邓州市| www.jieseteng.com:抚宁县| www.corpicontusi.com:德州市| www.cp7330.com:汕头市| www.sky161.com:涿鹿县| www.syyunshengs.com:宣恩县| www.jisemm.com:井冈山市| www.5005560.com:阳新县| www.hysmzx.com:香格里拉县| www.f3n3.com:酒泉市| www.cqwjwz.com:榆中县| www.jnddq.com:宜宾市| www.hirfigyelo.com:淄博市| www.anmsn.com:赤水市| www.lool82.com:滕州市| www.926379.com:扬中市| www.louisgh.com:临猗县| www.sunmastering.com:师宗县| www.bashmaistora-bg.com:岳阳县| www.killdevilhillbrooklyn.com:苏州市| www.bjahwt.com:从江县| www.szqtouch.com:徐水县| www.tjdongtai.com:钦州市| www.zjxklpme.com:类乌齐县| www.my-crusher.com:南安市| www.xjzsxx.com:万盛区| www.564rr.com:云龙县| www.minamihompi.com:新源县| www.juegosdraculaura.com:珲春市| www.ligu-coating.com:夏津县| www.themossmagazine.com:永善县| www.elitetrainingca.com:丁青县| www.georgepappasltd.com:凉山| www.xtremeracing.net:林芝县| www.feastbookstore.com:大宁县| www.freeportluxembourg.com:旅游| www.rgepi.com:康定县| www.290428.com:获嘉县| www.marketsizeinfo.com:来安县| www.afewbestmen.com:双城市| www.tearway.com:永仁县| www.dannyquattro.com:南乐县| www.xsxonline.com:汉阴县| www.xskongtiao.com:兰考县| www.the-youngers.org:香格里拉县| www.speed28.com:调兵山市| www.hg30789.com:五寨县| www.bisutekirevere.com:大丰市| www.ptbtw.cn:黑河市| www.airmaxshoesnike.net:云浮市| www.ypqkw.cn:牡丹江市| www.yang-xx.com:长治市|