欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解C#中的依賴注入和IoC容器

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

在本文中,我們將通過用C#重構(gòu)一個(gè)非常簡(jiǎn)單的代碼示例來(lái)解釋依賴注入和IoC容器。 

簡(jiǎn)介:

依賴注入和IoC乍一看可能相當(dāng)復(fù)雜,但它們非常容易學(xué)習(xí)和理解。

在本文中,我們將通過在C#中重構(gòu)一個(gè)非常簡(jiǎn)單的代碼示例來(lái)解釋依賴注入和IoC容器。

要求:

構(gòu)建一個(gè)允許用戶查看可用產(chǎn)品并按名稱搜索產(chǎn)品的應(yīng)用程序。

第一次嘗試:

我們將從創(chuàng)建分層架構(gòu)開始。使用分層架構(gòu)有多個(gè)好處,但我們不會(huì)在本文中列出它們,因?yàn)槲覀冴P(guān)注的是依賴注入。

下面是應(yīng)用程序的類圖:

首先,我們將從創(chuàng)建一個(gè)Product類開始:

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

然后,我們將創(chuàng)建數(shù)據(jù)訪問層:

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();
  }
}

然后,我們將創(chuàng)建業(yè)務(wù)層:

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);
  }
}

最后,我們將創(chuàng)建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();
  }
}

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

1.我們不能讓三個(gè)不同的團(tuán)隊(duì)在每個(gè)層上工作。

2.業(yè)務(wù)層很難擴(kuò)展,因?yàn)樗蕾囉跀?shù)據(jù)訪問層的實(shí)現(xiàn)。

3.業(yè)務(wù)層很難維護(hù),因?yàn)樗蕾囉跀?shù)據(jù)訪問層的實(shí)現(xiàn)。

4.源代碼很難測(cè)試。

第二次嘗試:

高級(jí)別對(duì)象不應(yīng)該依賴于低級(jí)別對(duì)象。兩者都必須依賴于抽象。那么抽象概念是什么呢?

抽象是功能的定義。在我們的例子中,業(yè)務(wù)層依賴于數(shù)據(jù)訪問層來(lái)檢索圖書。在C#中,我們使用接口實(shí)現(xiàn)抽象。接口表示功能的抽象。

讓我們來(lái)創(chuàng)建抽象。

下面是數(shù)據(jù)訪問層的抽象:

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

我們還需要更新數(shù)據(jù)訪問層:

public class ProductDAL : IProductDAL

我們還需要更新業(yè)務(wù)層。實(shí)際上,我們將更新業(yè)務(wù)層,使其依賴于數(shù)據(jù)訪問層的抽象,而不是依賴于數(shù)據(jù)訪問層的實(shí)現(xiàn):

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);
  }
}

我們還必須創(chuàng)建業(yè)務(wù)層的抽象:

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

我們也需要更新業(yè)務(wù)層:

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();
  }
}

我們?cè)诘诙螄L試中所做的代碼是有效的,但我們?nèi)匀灰蕾囉跀?shù)據(jù)訪問層的具體實(shí)現(xiàn):

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

那么,如何解決呢?

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

最終嘗試

到目前為止,我們所做的工作都與依賴注入無(wú)關(guān)。

為了使處在較高級(jí)別的的業(yè)務(wù)層依賴于較低級(jí)別對(duì)象的功能,而沒有具體的實(shí)現(xiàn),必須由其他人創(chuàng)建類。其他人必須提供底層對(duì)象的具體實(shí)現(xiàn),這就是我們所說(shuō)的依賴注入。它的字面意思是我們將依賴對(duì)象注入到更高級(jí)別的對(duì)象中。實(shí)現(xiàn)依賴項(xiàng)注入的方法之一是使用構(gòu)造函數(shù)進(jìn)行依賴項(xiàng)注入。

讓我們更新業(yè)務(wù)層:

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);
  }
}

基礎(chǔ)設(shè)施必須提供對(duì)實(shí)現(xiàn)的依賴:

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();
  }
}

創(chuàng)建數(shù)據(jù)訪問層的控制與基礎(chǔ)設(shè)施結(jié)合在一起。這也稱為控制反轉(zhuǎn)。我們不是在業(yè)務(wù)層中創(chuàng)建數(shù)據(jù)訪問層的實(shí)例,而是在基礎(chǔ)設(shè)施的中創(chuàng)建它。 Main方法將把實(shí)例注入到業(yè)務(wù)邏輯層。因此,我們將低層對(duì)象的實(shí)例注入到高層對(duì)象的實(shí)例中。

這叫做依賴注入。

現(xiàn)在,如果我們看一下代碼,我們只依賴于業(yè)務(wù)訪問層中數(shù)據(jù)訪問層的抽象,而業(yè)務(wù)訪問層是使用的是數(shù)據(jù)訪問層實(shí)現(xiàn)的接口。因此,我們遵循了更高層次對(duì)象和更低層次對(duì)象都依賴于抽象的原則,抽象是更高層次對(duì)象和更低層次對(duì)象之間的契約。

現(xiàn)在,我們可以讓不同的團(tuán)隊(duì)在不同的層上工作。我們可以讓一個(gè)團(tuán)隊(duì)處理數(shù)據(jù)訪問層,一個(gè)團(tuán)隊(duì)處理業(yè)務(wù)層,一個(gè)團(tuán)隊(duì)處理UI。

接下來(lái)就顯示了可維護(hù)性和可擴(kuò)展性的好處。例如,如果我們想為SQL Server創(chuàng)建一個(gè)新的數(shù)據(jù)訪問層,我們只需實(shí)現(xiàn)數(shù)據(jù)訪問層的抽象并將實(shí)例注入基礎(chǔ)設(shè)施中。

最后,源代碼現(xiàn)在是可測(cè)試的了。因?yàn)槲覀冊(cè)谌魏蔚胤蕉际褂媒涌?,所以我們可以很容易地在較低的單元測(cè)試中提供另一個(gè)實(shí)現(xiàn)。這意味著較低的測(cè)試將更容易設(shè)置。

現(xiàn)在,讓我們測(cè)試業(yè)務(wù)層。

我們將使用xUnit進(jìn)行單元測(cè)試,使用Moq模擬數(shù)據(jù)訪問層。

下面是業(yè)務(wù)層的單元測(cè)試:

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);
  }
}

你可以看到,使用依賴項(xiàng)注入很容易設(shè)置單元測(cè)試。

IoC容器

容器只是幫助實(shí)現(xiàn)依賴注入的東西。容器,通常實(shí)現(xiàn)三種不同的功能:

1.注冊(cè)接口和具體實(shí)現(xiàn)之間的映射

2.創(chuàng)建對(duì)象并解析依賴關(guān)系

3.釋放

讓我們實(shí)現(xiàn)一個(gè)簡(jiǎn)單的容器來(lái)注冊(cè)映射并創(chuàng)建對(duì)象。

首先,我們需要一個(gè)存儲(chǔ)映射的數(shù)據(jù)結(jié)構(gòu)。我們將選擇Hashtable。該數(shù)據(jù)結(jié)構(gòu)將存儲(chǔ)映射。

首先,我們將在容器的構(gòu)造函數(shù)中初始化Hashtable。然后,我們將創(chuàng)建一個(gè)RegisterTransient方法來(lái)注冊(cè)映射。最后,我們會(huì)創(chuàng)建一個(gè)創(chuàng)建對(duì)象的方法 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);
  }
}

最終,我們會(huì)更新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();
  }
}

現(xiàn)在,讓我們?cè)谌萜髦袑?shí)現(xiàn)Resolve方法。此方法將解決依賴關(guān)系。

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>()方法創(chuàng)建ProductBL類的一個(gè)對(duì)象。ProductBL類是IProductDAL的一個(gè)依賴項(xiàng)。因此,container.Resolve<IProductBL>() 通過自動(dòng)創(chuàng)建并在其中注入一個(gè)ProductDAL對(duì)象返回ProductBL類的一個(gè)對(duì)象。這一切都在幕后進(jìn)行。創(chuàng)建和注入ProductDAL對(duì)象是因?yàn)槲覀冇肐ProductDAL注冊(cè)了ProductDAL類型。

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

以上就是詳解C#中的依賴注入和IoC容器的詳細(xì)內(nèi)容,更多關(guān)于C# 依賴注入和IoC容器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C# 泛型的約束

    C# 泛型的約束

    本文將詳細(xì)介紹C# 泛型的約束:引用類型約束;值類型約束;構(gòu)造函數(shù)類型約束;轉(zhuǎn)換類型約束;組合約束的相關(guān)知識(shí)。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-02-02
  • C#中backgroundworker的使用教程

    C#中backgroundworker的使用教程

    這篇文章主要介紹了C#中backgroundworker的使用教程,在文中有兩點(diǎn)需要注意的,大家可以看下
    2018-04-04
  • DevExpress之ChartControl的SeriesTemplate實(shí)例

    DevExpress之ChartControl的SeriesTemplate實(shí)例

    這篇文章主要介紹了DevExpress之ChartControl的SeriesTemplate用法實(shí)例,實(shí)現(xiàn)了餅狀Series百分比顯示的效果,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2014-10-10
  • C#實(shí)現(xiàn)自定義定時(shí)組件的方法

    C#實(shí)現(xiàn)自定義定時(shí)組件的方法

    這篇文章主要介紹了C#實(shí)現(xiàn)自定義定時(shí)組件的方法,很實(shí)用的功能,需要的朋友可以參考下
    2014-08-08
  • C#中datatable去重的方法

    C#中datatable去重的方法

    這篇文章主要介紹了C#中datatable去重的方法,通過兩種不同的方法對(duì)比分析了datatable去重的技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2014-10-10
  • C#用遞歸算法解決八皇后問題

    C#用遞歸算法解決八皇后問題

    在軟件編程中,這種思路確是一種解決問題最簡(jiǎn)單的算法,它通過一種類似于蠻干的思路,一步一步地往前走,每走一步都更靠近目標(biāo)結(jié)果一些,直到遇到障礙物,我們才考慮往回走。
    2016-06-06
  • 淺析C#更改令牌ChangeToken

    淺析C#更改令牌ChangeToken

    這篇文章主要介紹了C#更改令牌ChangeToken,文中運(yùn)用大量代碼講解的非常詳細(xì),感興趣的小伙伴一起來(lái)看看這篇文章吧
    2021-09-09
  • Winform實(shí)現(xiàn)抓取web頁(yè)面內(nèi)容的方法

    Winform實(shí)現(xiàn)抓取web頁(yè)面內(nèi)容的方法

    這篇文章主要介紹了Winform實(shí)現(xiàn)抓取web頁(yè)面內(nèi)容的方法,代碼只有短短幾行,但是功能很實(shí)用,需要的朋友可以參考下
    2014-09-09
  • SQL+C#實(shí)現(xiàn)獲得當(dāng)前月的第一天與最后一天

    SQL+C#實(shí)現(xiàn)獲得當(dāng)前月的第一天與最后一天

    本文分享了SQL+C#獲得當(dāng)前月的第一天與最后一天的代碼實(shí)例,代碼簡(jiǎn)潔,適合初學(xué)者參考。需要的朋友可以看下
    2016-12-12
  • 用C#寫的ADSL撥號(hào)程序的代碼示例

    用C#寫的ADSL撥號(hào)程序的代碼示例

    用C#寫的ADSL撥號(hào)程序的代碼示例...
    2007-11-11

最新評(píng)論