构造对象时的依赖注入

发布于 2025-01-05 06:02:57 字数 2502 浏览 1 评论 0原文

我正在为我的控制器创建一个自定义 ActionResult ,因为我注意到很多可以重复使用的重复代码。它看起来像这样:

 public ExtendedViewResult<T> : ActionResult
 {
      protected T Model { get; set; }
      protected IModelExtender<T> Extender { get; set; }

      public ExtendedActionResult(T model, IModelExtender<T> extender)
      {
          this.Model = model;
          this.Extender = extender;
      }
 }

 public class BaseController : Controller
 {
     public ExtendedViewResult<T> ExtendedView<T>(T model)
     {
         // I need to create the result here, but how?
         var result = new ExtendedViewResult<T>(model, ???????);

         return result;
     }
 }

我遇到的问题是我不确定如何构造我的 ExtendedViewResult 对象。由于接口是通用的,我想使用依赖注入来获取正确的对象,但我不确定如何做到这一点,因为我自己构造对象。

我正在使用 Ninject 和 Ninject.MVC3,默认的 Nuget 包为我创建了一个 Bootstrapper 类,当我访问 Bootstrapper.Kernel 属性时,我收到以下警告:

Ninject.Web.Mvc.Bootstrapper.Kernel 已过时。不要使用 Ninject 作为服务定位器。

如果我不应该直接访问内核,那么我如何更改我的代码以便获得适当的具体类?

编辑
这是 ninject 引导程序代码。我添加的唯一方法是 GetInstance()

public static class NinjectMVC3 
{
    private static readonly Bootstrapper bootstrapper = new Bootstrapper();

    /// <summary>
    /// Starts the application
    /// </summary>
    public static void Start() 
    {
        DynamicModuleUtility.RegisterModule(typeof(OnePerRequestModule));
        DynamicModuleUtility.RegisterModule(typeof(HttpApplicationInitializationModule));
        bootstrapper.Initialize(CreateKernel);
    }

    /// <summary>
    /// Stops the application.
    /// </summary>
    public static void Stop()
    {
        bootstrapper.ShutDown();
    }

    // I ADDED THIS CODE, EVERYTHING ELSE IS AUTO-GENERATED
    // BY THE NUGET PACKAGE
    public static T GetInstance<T>()
    {
        return bootstrapper.Kernel.Get<T>();
    }

    /// <summary>
    /// Creates the kernel that will manage your application.
    /// </summary>
    /// <returns>The created kernel.</returns>
    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        RegisterServices(kernel);
        return kernel;
    }

    /// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
    }        
}

I am creating a custom ActionResult for my controllers because I've noticed a lot of repeated code that could be made reusable. It looks something like this:

 public ExtendedViewResult<T> : ActionResult
 {
      protected T Model { get; set; }
      protected IModelExtender<T> Extender { get; set; }

      public ExtendedActionResult(T model, IModelExtender<T> extender)
      {
          this.Model = model;
          this.Extender = extender;
      }
 }

 public class BaseController : Controller
 {
     public ExtendedViewResult<T> ExtendedView<T>(T model)
     {
         // I need to create the result here, but how?
         var result = new ExtendedViewResult<T>(model, ???????);

         return result;
     }
 }

The problem I am having is that I'm not sure how to construct my ExtendedViewResult object. Since the interface is generic I want to use Dependency Injection to get the proper object, but I'm not sure how to do that since I'm constructing the object myself.

I am using Ninject and Ninject.MVC3 and the default Nuget package creates a Bootstrapper class for me, and when I access the Bootstrapper.Kernel property I get the following warning:

Ninject.Web.Mvc.Bootstrapper.Kernel is obsolete. Do not use Ninject as Service Locator.

If I'm not supposed to access the kernel directly, then how can I change my code so that I can get the appropriate concrete class?

EDIT
Here is the ninject bootstrapper code. The only method I added is the GetInstance()

public static class NinjectMVC3 
{
    private static readonly Bootstrapper bootstrapper = new Bootstrapper();

    /// <summary>
    /// Starts the application
    /// </summary>
    public static void Start() 
    {
        DynamicModuleUtility.RegisterModule(typeof(OnePerRequestModule));
        DynamicModuleUtility.RegisterModule(typeof(HttpApplicationInitializationModule));
        bootstrapper.Initialize(CreateKernel);
    }

    /// <summary>
    /// Stops the application.
    /// </summary>
    public static void Stop()
    {
        bootstrapper.ShutDown();
    }

    // I ADDED THIS CODE, EVERYTHING ELSE IS AUTO-GENERATED
    // BY THE NUGET PACKAGE
    public static T GetInstance<T>()
    {
        return bootstrapper.Kernel.Get<T>();
    }

    /// <summary>
    /// Creates the kernel that will manage your application.
    /// </summary>
    /// <returns>The created kernel.</returns>
    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        RegisterServices(kernel);
        return kernel;
    }

    /// <summary>
    /// Load your modules or register your services here!
    /// </summary>
    /// <param name="kernel">The kernel.</param>
    private static void RegisterServices(IKernel kernel)
    {
    }        
}

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

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

发布评论

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

评论(2

掌心的温暖 2025-01-12 06:02:57

抱歉,这在实际代码中会很枯燥 - 我没有太多使用 ninject,但其他 DI 容器有这个解决方案,并且我确信 ninject 也会有这个构造。

问题是您正在构造对象本身。当您真正使用 DI 容器并遵循 IoC 时,任何时候您看到关键字 new 都应该是一个危险信号,而使用容器作为服务定位器则是一个黄色信号。

既然您需要一个新对象,那么我们如何摆脱“新”呢?答案是让您的 BaseController 依赖于可以创建 ExtendedViewResult 的工厂。在 Autofac(我选择的容器)中,这就像注入 Func 一样简单。如果 Ninject 没有同样的功能,我会感到惊讶。事实上,看起来确实如此 - 这个 ninject维基页面指向此博客发布在 Ninject.Extensions.Factory 上。

因此,这意味着您的控制器代码可能如下所示:

public class ConcreteController : BaseController
 {
     private Func<Foo,ExtendedViewResult<Foo>> _factory;
     public BaseController(Func<Foo,ExtendedViewResult<Foo>> factory)
     { 
        _factory = factory;
     }

     public ExtendedViewResult<Foo> Method(Foo model)
     {            
         var result = _factory(model);    
         return result;
     }
 }

如果您确实希望有一个通用方法在您的基类中进行创建,那么您可能需要从上面链接的博客文章中转到显式工厂接口路线。不过,使用这种样式代码,在大多数情况下您不需要它,并且您的控制器显式声明它们需要的依赖项。

Sorry this will be dry on actual code - I've not used ninject much, but other DI containers have this solution, and I'm sure ninject will have this construct as well.

The issue is that you are constructing the object itself. When you're really using a DI container and following IoC, anytime you see the keyword new should be a red flag - and using the container as a service locator is a yellow one.

So how do we get rid of the 'new', since you need a new object? The answer is to have your BaseController take a dependency on a factory that can create an ExtendedViewResult. In Autofac (my container of choice), that would be as simple as having a Func<ExtendedViewResult> injected. I would be surprised if Ninject doesn't have the same. In fact, looks like it does - this ninject wiki page points to this blog post on Ninject.Extensions.Factory.

So that means your code for the Controller could look like this:

public class ConcreteController : BaseController
 {
     private Func<Foo,ExtendedViewResult<Foo>> _factory;
     public BaseController(Func<Foo,ExtendedViewResult<Foo>> factory)
     { 
        _factory = factory;
     }

     public ExtendedViewResult<Foo> Method(Foo model)
     {            
         var result = _factory(model);    
         return result;
     }
 }

If you REALLY want to have a generic method do the creation in your base class, then you will probably need to go the explicit factory interface route from the blog post linked above. With this style code though, you would not need it in most cases, and your controllers explicitly declare the dependencies they need.

寂寞清仓 2025-01-12 06:02:57

每当您的对象想要创建其他对象时,您都应该使用抽象工厂。抽象工厂本身可以注入。我在这里问了类似的问题:使用依赖注入框架时的抽象工厂

虽然你的工厂是通用的,但实际上应该没有太大区别。

You should be using an abstract factory anytime your objects want to create other objects. The abstract factory itself can injected. I've asked a similar question here: Abstract factories when using dependency injection frameworks

It really shouldn't make much of a difference that your factory is generic though.

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