ホームページ  >  記事  >  バックエンド開発  >  [ASP.NET MVC Mavericks Road] 04 - 依存性注入 (DI) と Ninject

[ASP.NET MVC Mavericks Road] 04 - 依存性注入 (DI) と Ninject

黄舟
黄舟オリジナル
2016-12-30 14:03:591266ブラウズ

[ASP.NET
MVC Mavericks Road] 04 - 依存関係注入 (DI) と Ninject

この記事のディレクトリ:


依存関係注入が必要な理由

[ASP.NET MVC Mavericks Road] シリーズの記事内MVC パターンの理解について、MVC の重要な特徴は関心の分離であると述べました。アプリケーションのさまざまなコンポーネントができる限り独立し、相互の依存関係ができる限り少なくなるようにしたいと考えています。

私たちの理想的な状況は次のとおりです。コンポーネントは他のコンポーネントを認識せず、気にも留めませんが、提供されたパブリック インターフェイスを通じて他のコンポーネントの関数呼び出しを実現できます。この状況は疎結合と呼ばれます。

簡単な例を教えてください。製品用に「高度な」価格計算ツール LinqValueCalculator をカスタマイズしたいと考えています。この計算ツールは IValueCalculator インターフェイスを実装する必要があります。次のコードに示すように:

public interface IValueCalculator {
    decimal ValueProducts(params Product[] products);
}

public class LinqValueCalculator : IValueCalculator {
    public decimal ValueProducts(params Product[] products) {
        return products.Sum(p => p.Price);
    }
}

Product クラスは、最初の 2 つのブログ投稿で使用されているものと同じです。ショッピング カートの ShoppingCart クラスが追加されました。このクラスには、ショッピング カート内の商品の合計価格を計算できる関数が必要です。ただし、ショッピング カート自体には計算機能がないため、この計算機コンポーネントは LinqValueCalculator コンポーネントにすることができますが、必ずしも LinqValueCalculator コンポーネントである必要はありません (将来、ショッピング カートがアップグレードされる場合)。 、他のより高度な計算が埋め込まれている可能性があります)。次に、ショッピング カートの ShoppingCart クラスを次のように定義できます:

1 public class ShoppingCart {
 2     //计算购物车内商品总价钱
 3     public decimal CalculateStockValue() {
 4         Product[] products = { 
 5             new Product {Name = "西瓜", Category = "水果", Price = 2.3M}, 
 6             new Product {Name = "苹果", Category = "水果", Price = 4.9M}, 
 7             new Product {Name = "空心菜", Category = "蔬菜", Price = 2.2M}, 
 8             new Product {Name = "地瓜", Category = "蔬菜", Price = 1.9M} 
 9         };
10         IValueCalculator calculator = new LinqValueCalculator();
11 
12         //计算商品总价钱 
13         decimal totalValue = calculator.ValueProducts(products);
14 
15         return totalValue;
16     }
17 }


ShoppingCart クラスは、(LinqValueCalculator ではなく) IValueCalculator インターフェイスを通じて製品の合計価格を計算します。将来ショッピング カートをアップグレードするときに、より高度な計算機を使用する必要がある場合は、コードの 10 行目の new 以降のオブジェクトを変更するだけでよく (つまり、LinqValueCalculator を置き換える)、他のコードは変更する必要がありません。変えられる。これにより、ある程度の疎結合が実現されます。このとき、3 つの関係は下図のようになります。

[ASP.NET MVC Mavericks Road] 04 - 依存性注入 (DI) と Ninject

この図は、ShoppingCart クラスが IValueCalculator インターフェイスと LinqValueCalculator クラスの両方に依存していることを示しています。これには問題があります。ショッピング カートに組み込まれている電卓コンポーネントが壊れると、ショッピング カート全体が正常に動作しなくなります。ショッピング カート全体を交換する必要があるのではないでしょうか。最善の方法は、電卓コンポーネントとショッピング カートを完全に分離し、どのコンポーネントが壊れても、対応するコンポーネントのみを交換するだけで済むようにすることです。つまり、解決したい問題は、ShoppingCart コンポーネントと LinqValueCalculator コンポーネントを完全に切り離すことであり、依存関係注入の設計パターンは、この問題を解決することです。

依存関係の注入とは

上記で実装された部分的な疎結合は明らかに必要なものではありません。必要なのは、クラス内で、オブジェクトのインスタンスを作成せずに、パブリック インターフェイスを実装するオブジェクトへの参照を取得できることです。この「必要」はDI(Dependency Injection)と呼ばれ、いわゆるIoC(Inversion of Control)と同じ意味です。

DIは、インターフェースを介した疎結合を実現するデザインパターンです。初心者は、インターネット上に DI に焦点を当てた技術記事がなぜこれほど多いのか疑問に思うかもしれません。それは、DI が、MVC 開発を含むほぼすべてのフレームワークでアプリケーションを効率的に開発するために必要な重要な概念だからです。デカップリングの重要な手段です。

DIモードは2つの部分に分けることができます。 1 つはコンポーネント (上記の例では LinqValueCalculator) への依存関係を削除すること、もう 1 つはクラスのコンストラクター (またはクラスの Setter アクセサー) を介してパブリック インターフェイスを実装するコンポーネントへの参照を渡すことです。次のコードに示すように:

public class ShoppingCart {
    IValueCalculator calculator;
    
    //构造函数,参数为实现了IValueCalculator接口的类的实例
    public ShoppingCart(IValueCalculator calcParam) {
        calculator = calcParam;
    }

    //计算购物车内商品总价钱
    public decimal CalculateStockValue() {
        Product[] products = { 
            new Product {Name = "西瓜", Category = "水果", Price = 2.3M}, 
            new Product {Name = "苹果", Category = "水果", Price = 4.9M}, 
            new Product {Name = "空心菜", Category = "蔬菜", Price = 2.2M}, 
            new Product {Name = "地瓜", Category = "蔬菜", Price = 1.9M} 
        };

        //计算商品总价钱 
        decimal totalValue = calculator.ValueProducts(products);

        return totalValue;
    }
}

このようにして、ShoppingCart と LinqValueCalculator の間の依存関係を完全に切断しました。 IValueCalculator インターフェイス (この例では LinqValueCalculator) を実装するクラスのインスタンス参照は、パラメーターとして ShoppingCart クラスのコンストラクターに渡されます。ただし、ShoppingCart クラスは、IValueCalculator インターフェイスを実装するクラスを認識または考慮せず、このクラスを操作する責任はありません。 現時点では、次の図を使用して、ShoppingCart、LinqValueCalculator、IValueCalculator の関係を説明できます。

在程序运行的时候,依赖被注入到ShoppingCart,这个依赖就是,通过ShoppingCart构造函数传递实现了IValueCalculator接口的类的实例引用。在程序运行之前(或编译时),ShoppingCart和任何实现IValueCalculator接口的类没有任何依赖关系。(注意,程序运行时是有具体依赖关系的。)

注意,上面示例使用的注入方式称为“构造注入”,我们也可以通过属性来实现注入,这种注入被称为“setter 注入”,就不举例了,朋友们可以看看T2噬菌体的文章依赖注入那些事儿来对DI进行更多的了解。

由于经常会在编程时使用到DI,所以出现了一些DI的辅助工具(或叫DI容器),如Unity和Ninject等。由于Ninject的轻量和使用简单,加上本人只用过Ninject,所以本系列文章选择用它来开发MVC应用程序。下面开始介绍Ninject,但在这之前,先来介绍一个安装Ninject需要用到的插件-NuGet。


使用NuGet安装库

NuGet 是一种 Visual Studio 扩展,它能够简化在 Visual Studio 项目中添加、更新和删除库(部署为程序包)的操作。比如你要在项目中使用Log4Net这个库,如果没有NuGet这个扩展,你可能要先到网上搜索Log4Net,再将程序包的内容解压缩到解决方案中的特定位置,然后在各项目工程中依次添加程序集引用,最后还要使用正确的设置更新 web.config。而NuGet可以简化这一切操作。例如我们在讲依赖注入的项目中,若要使用一个NuGet库,可直接右击项目(或引用),选择“管理NuGet程序包”(VS2010下为“Add
Library Package Reference”),如下图:

[ASP.NET MVC Mavericks Road] 04 - 依存性注入 (DI) と Ninject

在弹出如下窗口中选择“联机”,搜索“Ninject”,然后进行相应的操作即可:

[ASP.NET MVC Mavericks Road] 04 - 依存性注入 (DI) と Ninject

在本文中我们只需要知道如何使用NuGet来安装库就可以了。NuGet的详细使用方法可查看MSDN文档:使用 NuGet 管理项目库。


使用Ninject的一般步骤

在使用Ninject前先要创建一个Ninject内核对象,代码如下:

class Program { 
    static void Main(string[] args) { 
        //创建Ninject内核实例
        IKernel ninjectKernel = new StandardKernel(); 
    } 
}

使用Ninject内核对象一般可分为两个步骤。第一步是把一个接口(IValueCalculator)绑定到一个实现该接口的类(LinqValueCalculator),如下:

...
//绑定接口到实现了该接口的类
ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator<(); 
...

这个绑定操作就是告诉Ninject,当接收到一个请求IValueCalculator接口的实现时,就返回一个LinqValueCalculator类的实例。

第二步是用Ninject的Get方法去获取IValueCalculator接口的实现。这一步,Ninject将自动为我们创建LinqValueCalculator类的实例,并返回该实例的引用。然后我们可以把这个引用通过构造函数注入到ShoppingCart类。如下代码所示:

...
// 获得实现接口的对象实例
IValueCalculator calcImpl = ninjectKernel.Get<IValueCalculator>(); 
// 创建ShoppingCart实例并注入依赖
ShoppingCart cart = new ShoppingCart(calcImpl); 
// 计算商品总价钱并输出结果
Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());
...

Ninject的使用的一般步骤就是这样。该示例可正确输出如下结果:

[ASP.NET MVC Mavericks Road] 04 - 依存性注入 (DI) と Ninject

但看上去Ninject的使用好像使得编码变得更加烦琐,朋友们会问,直接使用下面的代码不是更简单吗:

...
IValueCalculator calcImpl = new LinqValueCalculator();
ShoppingCart cart = new ShoppingCart(calcImpl);
Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());
...

的确,对于单个简单的DI,用Ninject确实显得麻烦。但如果添加多个复杂点的依赖关系,使用Ninject则可大大提高编码的工作效率。


Ninject如何提高编码效率

当我们请求Ninject创建某个类型的实例时,它会检查这个类型和其它类型之间的耦合关系。如果存在依赖关系,那么Ninject会根据依赖处理理它们,并创建所有所需类的实例。为了解释这句话和说明使用Ninject编码的便捷,我们再创建一个接口IDiscountHelper和一个实现该接口的类DefaultDiscountHelper,代码如下:

//折扣计算接口
public interface IDiscountHelper {
    decimal ApplyDiscount(decimal totalParam);
}

//默认折扣计算器
public class DefaultDiscountHelper : IDiscountHelper {
    public decimal ApplyDiscount(decimal totalParam) {
        return (totalParam - (1m / 10m * totalParam));
    }
}

IDiscounHelper接口声明了ApplyDiscount方法,DefaultDiscounterHelper实现了该接口,并定义了打9折的ApplyDiscount方法。然后我们可以把IDiscounHelper接口作为依赖添加到LinqValueCalculator类中。代码如下:

public class LinqValueCalculator : IValueCalculator { 
    private IDiscountHelper discounter; 
 
    public LinqValueCalculator(IDiscountHelper discountParam) { 
        discounter = discountParam; 
    } 
 
    public decimal ValueProducts(params Product[] products) { 
        return discounter.ApplyDiscount(products.Sum(p => p.Price)); 
    } 
}

LinqValueCalculator类添加了一个用于接收IDiscountHelper接口的实现的构造函数,然后在ValueProducts方法中调用该接口的ApplyDiscount方法对计算出的商品总价钱进行打折处理,并返回折后总价。

到这,我们先来画个图理一理ShoppingCart、LinqValueCalculator、IValueCalculator以及新添加的IDiscountHelper和DefaultDiscounterHelper之间的关系:

[ASP.NET MVC Mavericks Road] 04 - 依存性注入 (DI) と Ninject

以此,我们还可以添加更多的接口和实现接口的类,接口和类越来越多时,它们的关系图看上去会像一个依赖“链”,和生物学中的分子结构图差不多。

按照前面说的使用Ninject的“二个步骤”,现在我们在Main中的方法中编写用于计算购物车中商品折后总价钱的代码,如下所示:

1 class Program {
 2     static void Main(string[] args) {
 3         IKernel ninjectKernel = new StandardKernel();
 4 
 5         ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
 6         ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>();
 7 
 8         IValueCalculator calcImpl = ninjectKernel.Get<IValueCalculator>();
 9         ShoppingCart cart = new ShoppingCart(calcImpl);
10         Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());
11         Console.ReadKey();
12     }
13 }

输出结果:

[ASP.NET MVC Mavericks Road] 04 - 依存性注入 (DI) と Ninject

代码一目了然,虽然新添加了一个接口和一个类,但Main方法中只增加了第6行一句代码,获取实现IValueCalculator接口的对象实例的代码不需要做任何改变。

定位到代码的第8行,这一行代码,Ninject为我们做的事是:

  当我们需要使用IValueCalculator接口的实现时(通过Get方法),它便为我们创建LinqValueCalculator类的实例。而当创建LinqValueCalculator类的实例时,它检查到这个类依赖IDiscountHelper接口。于是它又创建一个实现了该接口的DefaultDiscounterHelper类的实例,并通过构造函数把该实例注入到LinqValueCalculator类。然后返回LinqValueCalculator类的一个实例,并赋值给IValueCalculator接口的对象(第8行的calcImpl)。

总之,不管依赖“链”有多长有多复杂,Ninject都会按照上面这种方式检查依赖“链”上的每个接口和实现接口的类,并自动创建所需要的类的实例。在依赖“链”越长越复杂的时候,更能显示使用Ninject编码的高效率。

Ninject的绑定方式

我个人将Ninject的绑定方式分为:一般绑定、指定值绑定、自我绑定、派生类绑定和条件绑定。这样分类有点牵强,只是为了本文的写作需要和方便读者阅读而分,[b]并不是官方的分类。[/b]

1、一般绑定

在前文的示例中用Bind和To方法把一个接口绑定到实现该接口的类,这属于一般的绑定。通过前文的示例相信大家已经掌握了,在这就不再累述。

2、[b]指定值绑定[/b]

我们知道,通过Get方法,Ninject会自动帮我们创建我们所需要的类的实例。但有的类在创建实例时需要给它的属性赋值,如下面我们改造了一下的DefaultDiscountHelper类:

public class DefaultDiscountHelper : IDiscountHelper { 
    public decimal DiscountSize { get; set; } 
 
    public decimal ApplyDiscount(decimal totalParam) { 
        return (totalParam - (DiscountSize / 10m * totalParam)); 
    } 
}


给DefaultDiscountHelper类添加了一个DiscountSize属性,实例化时需要指定折扣值(DiscountSize属性值),不然ApplyDiscount方法就没意义。而实例化的动作是Ninject自动完成的,怎么告诉Ninject在实例化类的时候给某属性赋一个指定的值呢?这时就需要用到参数绑定,我们在绑定的时候可以通过给WithPropertyValue方法传参的方式指定DiscountSize属性的值,如下代码所示:

public static void Main() {
    IKernel ninjectKernel = new StandardKernel();

    ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
    ninjectKernel.Bind<IDiscountHelper>()
        .To<DefaultDiscountHelper>().WithPropertyValue("DiscountSize", 5M);

    IValueCalculator calcImpl = ninjectKernel.Get<IValueCalculator>();
    ShoppingCart cart = new ShoppingCart(calcImpl);
    Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());
    Console.ReadKey();
}

只是在Bind和To方法后添加了一个WithPropertyValue方法,其他代码都不用变,再一次见证了用Ninject编码的高效。

WithPropertyValue方法接收了两个参数,一个是属性名(示例中的"DiscountSize"),一个是属性值(示例中的5)。运行结果如下:

[ASP.NET MVC Mavericks Road] 04 - 依存性注入 (DI) と Ninject

如果要给多个属性赋值,则可以在Bind和To方式后添加多个WithPropertyValue(,)方法。

我们还可以在类的实例化的时候为类的构造函数传递参数。为了演示,我们再把DefaultDiscountHelper类改一下:

public class DefaultDiscountHelper : IDiscountHelper { 
    private decimal discountRate; 
 
    public DefaultDiscountHelper(decimal discountParam) { 
        discountRate = discountParam; 
    } 
 
    public decimal ApplyDiscount(decimal totalParam) { 
        return (totalParam - (discountRate/ 10m * totalParam)); 
    } 
}

显然,DefaultDiscountHelper类在实例化的时候必须给构造函数传递一个参数,不然程序会出错。和给属性赋值类似,只是用的方法是WithConstructorArgument(,),绑定方式如下代码所示:

...
ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>(); 
ninjectKernel.Bind<IDiscountHelper>() 
    .To< DefaultDiscountHelper>().WithConstructorArgument("discountParam", 5M);
...

同样,只需要更改一行代码,其他代码原来怎么写还是怎么写。如果构造函数有多个参数,则需在Bind和To方法后面加上多个WithConstructorArgument即可。

3.自我绑定

Niject的一个非常好用的特性就是自绑定。当通过Bind和To方法绑定好接口和类后,可以直接通过ninjectKernel.Get()来获得一个类的实例。

在前面的几个示例中,我们都是像下面这样来创建ShoppingCart类实例的:

...
IValueCalculator calcImpl = ninjectKernel.Get<IValueCalculator>();
ShoppingCart cart = new ShoppingCart(calcImpl);
...

其实有一种更简单的定法,如下:

... 
ShoppingCart cart = ninjectKernel.Get<ShoppingCart>(); 
...

这种写法不需要关心ShoppingCart类依赖哪个接口,也不需要手动去获取该接口的实现(calcImpl)。当通过这句代码请求一个ShoppingCart类的实例的时候,Ninject会自动判断依赖关系,并为我们创建所需接口对应的实现。这种方式看起来有点怪,其实中规中矩的写法是:

...
ninjectKernel.Bind<ShoppingCart>().ToSelf();
ShoppingCart cart = ninjectKernel.Get<ShoppingCart>();
...

这里有自我绑定用的是ToSelf方法,在本示例中可以省略该句。但用ToSelf方法自我绑定的好处是可以在其后面用WithXXX方法指定构造函数参数、属性等等的值。

4.派生类绑定

通过一般绑定,当请求一个接口的实现时,Ninject会帮我们自动创建实现接口的类的实例。我们说某某类实现某某接口,也可以说某某类继承某某接口。如果我们把接口当作一个父类,是不是也可以把父类绑定到一个继承自该父类的子类呢?我们来实验一把。先改造一下ShoppingCart类,给它的CalculateStockValue方法改成虚方法:

public class ShoppingCart {
    protected IValueCalculator calculator;
    protected Product[] products;

    //构造函数,参数为实现了IEmailSender接口的类的实例
    public ShoppingCart(IValueCalculator calcParam) {
        calculator = calcParam;
        products = new[]{ 
            new Product {Name = "西瓜", Category = "水果", Price = 2.3M}, 
            new Product {Name = "苹果", Category = "水果", Price = 4.9M}, 
            new Product {Name = "空心菜", Category = "蔬菜", Price = 2.2M}, 
            new Product {Name = "地瓜", Category = "蔬菜", Price = 1.9M} 
        };
    }

    //计算购物车内商品总价钱
    public virtual decimal CalculateStockValue() {
        //计算商品总价钱 
        decimal totalValue = calculator.ValueProducts(products);
        return totalValue;
    }
}

再添加一个ShoppingCart类的子类:

public class LimitShoppingCart : ShoppingCart {
    public LimitShoppingCart(IValueCalculator calcParam)
        : base(calcParam) {
    }

    public override decimal CalculateStockValue() {
        //过滤价格超过了上限的商品
        var filteredProducts = products.Where(e => e.Price < ItemLimit);

        return calculator.ValueProducts(filteredProducts.ToArray());
    }

    public decimal ItemLimit { get; set; }
}

然后把父类ShoppingCart绑定到子类LimitShoppingCart:

public static void Main() {
    IKernel ninjectKernel = new StandardKernel();

    ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
    ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>()
        .WithPropertyValue("DiscountSize", 5M);
    //派生类绑定
    ninjectKernel.Bind<ShoppingCart>().To<LimitShoppingCart>()
        .WithPropertyValue("ItemLimit", 3M);

    ShoppingCart cart = ninjectKernel.Get<ShoppingCart>();
    Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());
    Console.ReadKey();
}

运行结果:

[ASP.NET MVC Mavericks Road] 04 - 依存性注入 (DI) と Ninject

从运行结果可以看出,cart对象调用的是子类的CalculateStockValue方法,证明了可以把父类绑定到一个继承自该父类的子类。通过派生类绑定,当我们请求父类的时候,Ninject自动帮我们创建一个对应的子类的实例,并将其返回。由于抽象类不能被实例化,所以派生类绑定在使用抽象类的时候非常有用。

5.条件绑定

当一个接口有多个实现或一个类有多个子类的时候,我们可以通过条件绑定来指定使用哪一个实现或子类。为了演示,我们给IValueCalculator接口再添加一个实现,如下:

public class IterativeValueCalculator : IValueCalculator { 
 
    public decimal ValueProducts(params Product[] products) { 
        decimal totalValue = 0; 
        foreach (Product p in products) { 
            totalValue += p.Price; 
        } 
        return totalValue; 
    } 
}

IValueCalculator接口现在有两个实现:IterativeValueCalculator和LinqValueCalculator。我们可以指定,如果是把该接口的实现注入到LimitShoppingCart类,那么就用IterativeValueCalculator,其他情况都用LinqValueCalculator。如下所示:

public static void Main() {
    IKernel ninjectKernel = new StandardKernel();

    ninjectKernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
    ninjectKernel.Bind<IDiscountHelper>().To<DefaultDiscountHelper>()
        .WithPropertyValue("DiscountSize", 5M);
    //派生类绑定
    ninjectKernel.Bind<ShoppingCart>().To<LimitShoppingCart>()
        .WithPropertyValue("ItemLimit", 3M);
    //条件绑定
    ninjectKernel.Bind<IValueCalculator>()
        .To<IterativeValueCalculator>().WhenInjectedInto<LimitShoppingCart>();

    ShoppingCart cart = ninjectKernel.Get<ShoppingCart>();
    Console.WriteLine("Total: {0:c}", cart.CalculateStockValue());
    Console.ReadKey();
}

运行结果:

[ASP.NET MVC Mavericks Road] 04 - 依存性注入 (DI) と Ninject

在ASP.NET MVC中使用Ninject

本文用控制台应用程序演示了Ninject的使用,但要把Ninject集成到ASP.NET MVC中还是有点复杂的。首先要做的事就是创建一个继承System.Web.Mvc.DefaultControllerFactory的类,MVC默认使用这个类来创建Controller类的实例(后续博文会专门讲这个)。代码如下:


NinjectControllerFactory

现在暂时不解释这段代码,大家都看懂就看,看不懂就过,只要知道在ASP.NET MVC中使用Ninject要做这么一件事就行。

添加完这个类后,还要做一件事,就是在MVC框架中注册这个类。一般我们在Global.asax文件中的Application_Start方法中进行注册,如下所示:

protected void Application_Start() {
    AreaRegistration.RegisterAllAreas();

    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);

    ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
}

注册后,MVC框架就会用NinjectControllerFactory类去获取Cotroller类的实例。在后续博文中会具体演示如何在ASP.NET MVC中使用Ninject,这里就不具体演示了,大家知道需要做这么两件事就行。

虽然我们前面花了很大功夫来学习Ninject就是为了在MVC中使用这样一个NinjectControllerFactory类,但是了解Ninject如何工作是非常有必要的。理解好了一种DI容器,可以使得开发和测试更简单、更高效。

 以上就是[ASP.NET MVC 小牛之路]04 - 依赖注入(DI)和Ninject的内容,更多相关内容请关注PHP中文网(www.php.cn)!


声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。