在没有任何 DI 库的情况下使用依赖注入 [英] Using Dependency Injection without any DI Library

查看:20
本文介绍了在没有任何 DI 库的情况下使用依赖注入的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 Repository 和 DI 的新手,正在尝试在我的 MVC 5 项目中实现.

I am new to Repository and DI and trying to implement in my MVC 5 project.

我实现了构造函数注入,其中在我的控制器中有一个像这样的构造函数:

I implemented Constructor Injection where in my controller has a constructor like this:

IBook _ibook;
public Test(IBook ibook)
{
   _ibook = ibook;
}

如果没有任何 DI 库,它会抛出一个错误:没有空的构造函数.

Without any DI library, it throws an error: There is no empty constructor.

为了避免这种情况,我又添加了一个构造函数,如下所示:

To avoid this, I added one more constructor as below:

public Test ():this(new Book())
{    
}

由于我是 DI 的新手,我不想因为使用 DI 库来冒险我的项目,这可能会在以后抛出一些我可能无法解决的错误.

Since I am new to DI, I don't want to risk my project by using DI library which can later throw some error that I may not be able to resolve.

我想知道如果我不使用 DI 库可能会遇到什么问题.

I want to know what issues I might encounter if I am not using DI library.

如果推荐,哪个DI库适合初学者?我看过的 NInject 和 Unity 的视频很少.

In case it is recommended, which DI library is good for beginners? I have seen few videos of NInject and Unity.

推荐答案

最好将使用某种工具或库的决定推迟到 最后的责任时刻.有了好的设计,您以后可以添加 DI 库.这意味着您练习Pure DI.

It is a good idea to delay any decision to use some kind of tool or library until the last responsible moment. With a good design you can add a DI library later on. This means that you practice Pure DI.

MVC 中首选的拦截点是 IControllerFactory 抽象,因为它允许您拦截 MVC 控制器的创建,这样做可以防止您必须实现第二个构造函数(是一种反模式).尽管可以使用 IDependencyResolver,但使用该抽象的方便得多,因为 MVC 也调用它来解决您通常不感兴趣的事情.

The preferred interception point in MVC is the IControllerFactory abstraction since it allows you to intercept the creation of MVC controllers, and doing so prevents you from having to implement a second constructor (which is an anti-pattern). Although it is possible to use IDependencyResolver, the use of that abstraction is much less convenient because it is also called by MVC to resolve things you are typically not interested in.

一个自定义 IControllerFactory,它将充当您的 组合根可以实现如下:

A custom IControllerFactory that will act as your Composition Root can be implemented as follows:

public sealed class CompositionRoot : DefaultControllerFactory
{
    private static string connectionString = 
        ConfigurationManager.ConnectionStrings["app"].ConnectionString;
    private static Func<BooksContext> bookContextProvider = GetCurrentBooksContext;
    private static IBookRepository bookRepo = new BookRepository(bookContextProvider);
    private static IOrderBookHandler orderBookHandler = new OrderBookHandler(bookRepo);

    protected override IController GetControllerInstance(RequestContext _, Type type) {
        // Unfortunately, because of MVC's design, controllers are not stateless, and 
        // you will have to create them per request.

        if (type == typeof(OrderBookController))
            return new HomeController(orderBookHandler);

        if (type == typeof(BooksController))
            return new BooksController(bookRepo);

        // [other controllers here]

        return base.GetControllerInstance(_, type);
    }

    private static BooksContext GetCurrentBooksContext() {
        return GetRequestItem<BooksContext>(() => new BooksContext(connectionString));
    }

    private static T GetRequestItem<T>(Func<T> valueFactory) where T : class {
        var context = HttpContext.Current;
        if (context == null) throw new InvalidOperationException("No web request.");
        var val = (T)context.Items[typeof(T).Name];
        if (val == null) context.Items[typeof(T).Name] = val = valueFactory();
        return val;
    }
}

您的新控制器工厂可以按如下方式连接到 MVC 中:

Your new controller factory can be hooked into MVC as follows:

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start() {
        ControllerBuilder.Current.SetControllerFactory(new CompositionRoot());

        // the usual stuff here
    }
}

当您练习 Pure DI 时,您通常会看到您的 Composition Root 由一大堆 if 语句组成.应用程序中的每个根对象一条语句.

When you practice Pure DI, you will typically see your Composition Root consist of a big list of if statements. One statement per root object in your application.

从 Pure DI 开始有一些有趣的优势.最突出的是编译时支持,因为当您开始使用 DI 库时,您将立即失去这一点.一些库通过允许您以编译器会做的方式验证配置来尝试最小化这种损失;但这种验证是在运行时完成的,反馈周期永远不会像编译器能给你的那么短.

Starting off with Pure DI has some interesting advantages. The most prominent one is compile time support, because this is something you will lose immediately when you start using a DI library. Some libraries try minimize this loss by allowing you to verify your configuration in a way that the compiler would do; but this verification is done at runtime and the feedback cycle is never as short as that which the compiler can give you.

请不要试图通过实现一些允许使用反射创建类型的机制来简化开发,因为这样做是在构建自己的 DI 库.这有很多缺点,例如您失去了编译时支持,同时无法恢复现有 DI 库可为您带来的任何好处.

Please don't be tempted to simplify development by implementing some mechanism that allows creating types using reflection, because in doing so you are building your own DI library. There are many downsides to this, e.g. you lose compile time support while not getting back any of the benefits that an existing DI library can give you.

当您的 Composition Root 开始变得难以维护时,您应该考虑从 Pure DI 切换到 DI 库.

When your Composition Root is starting to get hard to maintain, that is the moment you should consider switching from Pure DI to a DI library.

请注意,在我的示例组合根中,所有 应用程序组件(控制器除外)都定义为单件.单例意味着应用程序将只有每个组件的一个实例.这种设计需要你的组件是无状态的(因此是线程安全的),任何有状态的东西(比如 BooksContext)不应通过构造函数注入.在示例中,我使用了 Func 作为 BooksContext 的提供者,该提供者按请求存储.

Do note that in my example Composition Root, all application components (except for the controllers) are defined as singleton. Singleton means that the application will only have one instance of each component. This design needs your components to be stateless (and thus thread-safe), anything that has state (such as the BooksContext) should not be injected through the constructor. In the example I used a Func<T> as the provider of the BooksContext which is stored per request.

使您的对象图成为单例 有许多有趣的优点.例如,它可以防止您犯常见的配置错误,例如 Captive Dependencies 并且它迫使您进入更可靠的设计.此外,一些 DI 库非常慢,并且在稍后切换到 DI 库时,将所有内容都设为单例可能会防止出现性能问题.另一方面,这种设计的缺点是团队中的每个人都应该明白所有组件都必须是无状态的.在组件中存储状态会导致不必要的悲伤和恶化.我的经验是有状态组件比大多数 DI 配置错误更容易检测.我还注意到,对于大多数开发人员来说,拥有单例组件是一件很自然的事情,尤其是那些没有使用 DI 经验的开发人员.有关可供选择的两种组合模型及其优缺点的详细讨论,请查看 这一系列的博文.

Making your object graphs singletons has many interesting advantages. For instance, it prevents you from making common configuration errors such as Captive Dependencies and it forces you into a more SOLID design. And besides, some DI libraries are very slow, and making everything a singleton might prevent performance problems when switching to a DI library later on. On the other hand, the downside of this design is that everybody on the team should understand that all components must be stateless. Storing state in components will cause needless grief and aggravation. My experience is that stateful components are much easier to detect than most DI configuration errors. I have also noticed that having singleton components is something that feels natural to most developers, especially those who aren't experienced with DI. For a detailed discussion on the two composition models to choose from and their downsides and advantages, take a look at this serie of blog posts.

请注意,在示例中,我为 BooksContext 手动实现了每个请求的生活方式.尽管所有 DI 库都对范围生活方式(例如按请求生活方式)提供开箱即用的支持,但我反对使用这些范围生活方式(除非库保证抛出异常而不是静默失败).当您解析活动范围上下文之外的范围实例时,大多数库不会警告您(例如解析后台线程上的每个请求实例).有些容器会返回一个单例实例,而另一些容器会在您每次询问时返回一个新实例.这真的很麻烦,因为它隐藏了错误,并可能导致您花费数小时来尝试调试您的应用程序(我在这里说的是经验).

Note that in the example I manually implemented a per-request lifestyle for the BooksContext. Although all DI libraries have out-of-the-box support for scoped lifestyles such as per-request lifestyles, I would argue against using those scoped lifestyles (except perhaps when the library guarantees to throw an exception instead of failing silently). Most libraries do not warn you when you resolve a scoped instance outside the context of an active scope (for instance resolving a per-request instance on a background thread). Some containers will return you a singleton instance, others return you a new instance each time you ask. This is really troublesome because it hides bugs and can cause you many hours trying to debug your application (I speak from experience here).

这篇关于在没有任何 DI 库的情况下使用依赖注入的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆