<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.90wlog.com:南乐县| www.paulovarelahairspace.com:鹿邑县| www.nightsailer.com:兰州市| www.hcsp139.com:宁海县| www.33fsfs.com:澄城县| www.tynale.com:东源县| www.huasupu.cn:托克托县| www.mark500.com:阳东县| www.bichengdecoration.com:高碑店市| www.getpoloapp.com:岱山县| www.viralinsocialmedia.com:谢通门县| www.lool82.com:宜宾市| www.bleedingblack.com:余姚市| www.zrh6.com:彭州市| www.yuexiangshipin.com:关岭| www.yiyuanjinshu.com:阳江市| www.tzdqw.com:封开县| www.tongfanglove.com:五大连池市| www.debibaker.org:信丰县| www.woodenfences.org:千阳县| www.foxconn371.com:原阳县| www.safehavenproj.org:高密市| www.youngwon1004.com:天全县| www.radiolauniversal.com:榆林市| www.dcbaowencp.com:怀安县| www.g7552.com:丹寨县| www.aroyalhangover.com:大姚县| www.vosmisi.com:宿松县| www.tsctalk.com:同仁县| www.irenecroce.com:宜宾市| www.enselo.com:白山市| www.agnum100.com:青神县| www.thejoyryders.com:玛纳斯县| www.stonedz.com:永和县| www.687090.com:灯塔市| www.provenzabanquetes.com:衡东县| www.aeroflex-cargo.com:铁岭县| www.snuhctc.com:枣强县| www.tearway.com:响水县| www.hcqidong.com:鄂伦春自治旗| www.ypiasby.com:滨州市| www.jatemweb.com:阿尔山市| www.hsbzd.com:南木林县| www.hellobuynow.com:南宁市| www.kinostream.net:施秉县| www.xnxqw.cn:亳州市| www.banchuan888.com:子长县| www.changdaoresort.com:名山县| www.v9176.com:广平县| www.217765.com:中江县| www.vintage-denim.com:阜城县| www.felixcaneinc.com:东辽县| www.zhongyancheng.com:库车县| www.999cscs.com:松阳县| www.cp3989.com:桐柏县| www.merrtoshop.com:岐山县| www.wangwangla.com:英超| www.mypopularbrand.com:大厂| www.garagedoorsirvine.com:兴隆县| www.tryinghardminimalist.com:普陀区| www.oudeshu.com:清河县| www.healtheworldtour.org:尚义县| www.inattendu32.com:辛集市| www.gigsea.com:株洲县| www.hk211.com:长春市| www.thisdayinmusicapps.com:南靖县| www.tudakozoonline.com:沛县| www.changdaoresort.com:福海县| www.m8986.com:清丰县| www.activeppcturkiye.com:连城县| www.kmtyaf.com:合江县| www.kaihongmtc.com:卢湾区| www.dadatu66.com:甘孜县| www.dachodesign.com:霍林郭勒市| www.vibgyorhr.com:茂名市| www.f5862.com:遂宁市| www.panda-host.net:曲周县| www.hollyflicks.com:永康市| www.kingdabearing.com:灵台县| www.choraliter.com:泗阳县| www.seoaon.com:扎兰屯市| www.showbar8.com:安达市| www.bunnykitten.com:玉屏| www.pengxing18.com:凤翔县|