使用 Ninject、MVC 3 和服务定位器模式进行依赖注入

发布于 2024-10-10 16:12:30 字数 1927 浏览 3 评论 0原文

自从我读到另一个 stackoverflow 问题的答案(现在我无法理解确切的答案)以来,有件事一直困扰着我,其中一位用户说“如果你调用服务定位器,那么你就做错了。< /strong>”

这是一个享有很高声誉的人(我认为有数十万),所以我倾向于认为这个人可能知道他们在说什么。自从我第一次开始了解 DI 以及它与单元测试的关系以及其他方面的关系后,我就一直在我的项目中使用 DI。我现在对此相当满意,并且我认为我知道自己在做什么。

但是,我在很多地方都使用服务定位器来解决项目中的依赖关系。一个主要的例子来自我的 ModelBinder 实现。

典型模型活页夹的示例。

public class FileModelBinder : IModelBinder {
    public object BindModel(ControllerContext controllerContext,
                            ModelBindingContext bindingContext) {
        ValueProviderResult value = bindingContext.ValueProvider.GetValue("id");

        IDataContext db = Services.Current.GetService<IDataContext>();
        return db.Files.SingleOrDefault(i => i.Id == id.AttemptedValue);
    }
}

不是一个真正的实现 - 只是一个简单的示例

由于当第一次请求 Binder 时,ModelBinder 实现需要一个新实例,因此不可能在构造函数上为此特定使用依赖注入执行。

我的很多课都是这样。另一个示例是缓存过期进程,每当我的网站中的缓存对象过期时,该进程就会运行一个方法。我运行了一堆数据库调用等等。我也使用服务定位器来获取所需的依赖项。

我最近遇到的另一个问题(我在这里发布了一个问题)是我的所有控制器都需要一个 IDataContext 实例,我使用 DI 来实现它 - 但一个操作方法需要不同的 IDataContext 实例。幸运的是,Ninject 通过命名依赖来救援。然而,这感觉像是一个拼凑,而不是一个真正的解决方案。

我认为我至少很好地理解了关注点分离的概念,但我对依赖注入和服务定位器模式的理解似乎存在根本性的错误 - 而且我不知道那是什么。

我目前的理解方式(这也可能是错误的)是,至少在 MVC 中,ControllerFactory 查找控制器的构造函数并调用服务定位器本身来获取所需的依赖项,然后将它们传入。但是,我可以理解,并非所有类和类都没有工厂来创建它们。所以在我看来,某些服务定位器模式是可以接受的......但是......

  1. 什么时候不可接受?
  2. 当我应该重新考虑如何使用服务定位器模式时,我应该留意什么样的模式?
  3. 我的 ModelBinder 实现是否错误?如果是这样,我需要学习什么来解决它?
  4. 在另一个问题中,一位用户 Mark Seemann 推荐了一个抽象工厂 - 这有什么关系?

我想就是这样 - 我真的想不出任何其他问题来帮助我的理解,但非常感谢任何额外的信息。

我知道 DI 可能不是所有问题的答案,而且我在实现它的方式上可能会太过分,但是,它似乎按照我期望的方式进行单元测试和其他工作。

我不是在寻找代码来修复我的示例实现 - 我正在寻找学习,寻找解释来修复我有缺陷的理解。

我希望 stackoverflow.com 能够保存草稿问题。我也希望回答这个问题的人能够因回答这个问题而获得适当的声誉,因为我认为我要求很多。谢谢,提前。

Something that has been bugging me since I read an answer on another stackoverflow question (the precise one eludes me now) where a user stated something like "If you're calling the Service Locator, you're doing it wrong."

It was someone with a high reputation (in the hundred thousands, I think) so I tend to think this person might know what they're talking about. I've been using DI for my projects since I first started learning about it and how well it relates to Unit Testing and what not. It's something I'm fairly comfortable with now and I think I know what I'm doing.

However, there are a lot of places where I've been using the Service Locator to resolve dependencies in my project. Once prime example comes from my ModelBinder implementations.

Example of a typical model binder.

public class FileModelBinder : IModelBinder {
    public object BindModel(ControllerContext controllerContext,
                            ModelBindingContext bindingContext) {
        ValueProviderResult value = bindingContext.ValueProvider.GetValue("id");

        IDataContext db = Services.Current.GetService<IDataContext>();
        return db.Files.SingleOrDefault(i => i.Id == id.AttemptedValue);
    }
}

not a real implementation - just a quick example

Since the ModelBinder implementation requires a new instance when a Binder is first requested, it's impossible to use Dependency Injection on the constructor for this particular implementation.

It's this way in a lot of my classes. Another example is that of a Cache Expiration process that runs a method whenever a cache object expires in my website. I run a bunch of database calls and what not. There too I'm using a Service Locator to get the required dependency.

Another issue I had recently (that I posted a question on here about) was that all my controllers required an instance of IDataContext which I used DI for - but one action method required a different instance of IDataContext. Luckily Ninject came to the rescue with a named dependency. However, this felt like a kludge and not a real solution.

I thought I, at least, understood the concept of Separation of Concerns reasonably well but there seems to be something fundamentally wrong with how I understand Dependency Injection and the Service Locator Pattern - and I don't know what that is.

The way I currently understand it - and this could be wrong as well - is that, at least in MVC, the ControllerFactory looks for a Constructor for a Controller and calls the Service Locator itself to get the required dependencies and then passes them in. However, I can understand that not all classes and what not have a Factory to create them. So it seems to me that some Service Locator pattern is acceptable...but...

  1. When is it not acceptable?
  2. What sort of pattern should I be on the look out for when I should rethink how I'm using the Service Locator Pattern?
  3. Is my ModelBinder implementation wrong? If so, what do I need to learn to fix it?
  4. In another question along the lines of this one user Mark Seemann recommended an Abstract Factory - How does this relate?

I guess that's it - I can't really think of any other question to help my understanding but any extra information is greatly appreciated.

I understand that DI might not be the answer to everything and I might be going overboard in how I implement it, however, it seems to work the way I expect it to with Unit Testing and what not.

I'm not looking for code to fix my example implementation - I'm looking to learn, looking for an explanation to fix my flawed understanding.

I wish stackoverflow.com had the ability to save draft questions. I also hope whoever answers this question gets the appropriate amount of reputation for answering this question as I think I'm asking for a lot. Thanks, in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

迷离° 2024-10-17 16:12:30

请考虑以下事项:

public class MyClass
{
  IMyInterface _myInterface;
  IMyOtherInterface _myOtherInterface;

  public MyClass(IMyInterface myInterface, IMyOtherInterface myOtherInterface)
  {
    // Foo

    _myInterface = myInterface;
    _myOtherInterface = myOtherInterface;
  }
}

通过此设计,我能够表达我的类型的依赖性要求。类型本身不负责了解如何实例化任何依赖项,它们是通过使用的任何解析机制(通常是 IoC 容器)赋予(注入)的。然而:

public class MyClass
{
  IMyInterface _myInterface;
  IMyOtherInterface _myOtherInterface;

  public MyClass()
  {
    // Bar

    _myInterface = ServiceLocator.Resolve<IMyInterface>();
    _myOtherInterface = ServiceLocator.Resolve<IMyOtherInterface>();
  }
}

我们的类现在依赖于创建特定实例,但是通过委托给服务定位器。从这个意义上说,服务位置可以被视为一种反模式,因为您没有公开依赖关系,但您允许通过编译捕获的问题冒泡到运行时。 (此处值得一读)。你隐藏了复杂性。

两者之间的选择实际上取决于您的建筑物所基于的内容及其提供的服务。通常,如果您从头开始构建应用程序,我会一直选择 DI。它提高了可维护性,促进了模块化并使测试类型变得更加容易。但是,以 ASP.NET MVC3 为例,您可以轻松地在设计中实现 SL。

您始终可以选择组合设计,将 IoC/DI 与 SL 结合使用,就像使用公共服务定位器。您的组件可以通过 DI 连接,但通过 SL 暴露。您甚至可以将组合放入其中并使用诸如托管可扩展性框架之类的东西(它本身支持 DI,但也可以连接到其他 IoC 容器或服务定位器)。这是一个重大的设计选择,通常我的建议是尽可能使用 IoC/DI。

我不会说你的具体设计是错误的。在这种情况下,您的代码不负责创建模型绑定器本身的实例,这取决于框架,因此您无法控制它但是您对服务定位器的使用可能很容易改变访问 IoC 容器。 但是在 IoC 容器上调用解析的操作...您不会考虑该服务位置吗?

使用抽象工厂模式,工厂专门用于创建特定类型。您不需要注册类型来进行解析,您本质上是注册一个抽象工厂并构建您可能需要的任何类型。通过服务定位器,它旨在定位服务并返回这些实例。从约定的角度来看类似,但行为却截然不同。

Consider the following:

public class MyClass
{
  IMyInterface _myInterface;
  IMyOtherInterface _myOtherInterface;

  public MyClass(IMyInterface myInterface, IMyOtherInterface myOtherInterface)
  {
    // Foo

    _myInterface = myInterface;
    _myOtherInterface = myOtherInterface;
  }
}

With this design I am able to express the dependency requirements for my type. The type itself isn't responsible for knowing how to instantiate any of the dependencies, they are given to it (injected) by whatever resolving mechanism is used [typically an IoC container]. Whereas:

public class MyClass
{
  IMyInterface _myInterface;
  IMyOtherInterface _myOtherInterface;

  public MyClass()
  {
    // Bar

    _myInterface = ServiceLocator.Resolve<IMyInterface>();
    _myOtherInterface = ServiceLocator.Resolve<IMyOtherInterface>();
  }
}

Our class is now dependent on creating the specfic instances, but via delegation to a service locator. In this sense, Service Location can be considered an anti-pattern because you're not exposing dependencies, but you are allowing problems which can be caught through compilation to bubble up into runtime. (A good read is here). You hiding complexities.

The choice between one or the other really depends on what your building on top of and the services it provides. Typically if you are building an application from scratch, I would choose DI all the time. It improves maintainability, promotes modularity and makes testing types a whole lot easier. But, taking ASP.NET MVC3 as an example, you could easily implement SL as its baked into the design.

You can always go for a composite design where you could use IoC/DI with SL, much like using the Common Services Locator. You component parts could be wired up through DI, but exposed through SL. You could even throw composition into the mix and use something like the Managed Extensibility Framework (which itself supports DI, but can also be wired to other IoC containers or service locators). It's a big design choice to make, generally my recommendation would be for IoC/DI where possible.

Your specific design I wouldn't say is wrong. In this instance, your code is not responsible for creating an instance of the model binder itself, that's up to the framework so you have no control over that but your use of the service locator could probably be easily changed to access an IoC container. But the action of calling resolve on the IoC container...would you not consider that service location?

With an abstract factory pattern the factory is specialised at creating specific types. You don't register types for resolution, you essentially register an abstract factory and that builds any types that you may require. With a Service Locator it is designed to locate services and return those instances. Similar from an convention point of view, but very different in behaviour.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文