【问题标题】:How to Inject properly an IDBContextFactory into a controller's inject IDomainFactory using Ninject MVC3?如何使用 Ninject MVC3 将 IDBContextFactory 正确注入控制器的注入 IDomainFactory?
【发布时间】:2012-02-22 13:40:26
【问题描述】:

预赛

  1. 我正在使用 Ninject.MVC3 2.2.2.0 Nuget 包向我的控制器注入一个 IDomain 接口的实现,该接口使用工厂方法分离我的业务逻辑 (BL)。
  2. 我正在使用以下方式在预配置的 NinjectMVC3.cs 中注册我的 Ninject 模块:

    private static void RegisterServices(IKernel kernel)
    {
        var modules = new INinjectModule[]
        {
            new DomainBLModule(),
            new ADOModule()
        };
        kernel.Load(modules);
    }
    
  3. 我正在努力避免邪恶的 Service Locator 反模式的致命诅咒。

域类使用了一个 DBContext,我也试图通过 IDBContext 注入接口实现,具有以下场景:

IDomainBLFactory

public interface IDomainBLFactory
{
    DomainBL CreateNew();
}

DomainBLFactory

public class DomainBLFactory : IDomainBLFactory
{
    public DomainBL CreateNew()
    {            
        return new DomainBL();
    }
}

在控制器的命名空间中:

public class DomainBLModule : NinjectModule
{
    public override void Load()
    {
        Bind<IDomainBLFactory>().To<DomainBLFactory>().InRequestScope();
    }
}

此时我可以使用 Ninject Constructor Injection 将 IDomainBLFactory 实现注入到我的控制器中,没有任何问题:

public class MyController : Controller
{
    private readonly IDomainBLFactory DomainBLFactory;
    // Default Injected Constructor
    public MyController(IDomainBLFactory DomainBLFactory)
    {
        this.DomainBLFactory = DomainBLFactory;
    }
    ... (use the Domain for performing tasks/commands with the Database Context)
}

现在是我的中心问题。

在 DomainBL 实现中,我会将依赖项注入到特定的 DBContext,在这种情况下,来自 Entity Framework 的 ADO DBContext,再次使用 IDBContextFactory:

IDbDataContextFactory

public interface IDbDataContextFactory
{
    myADOEntities CreateNew();
}

DbDataContextFactory

public class DbDataContextFactory : IDbDataContextFactory
{
    public myADOEntities CreateNew()        
    {
        return new myADOEntities ();
    }
}

ADOModule

public class ADOModule : NinjectModule
{
    public override void Load()
    {
        Bind<IDbDataContextFactory>().To<DbDataContextFactory>().InRequestScope();
    }
}

现在在 DomainBL 实现中,我遇到了为 DBContext 对象工厂注入必要接口的问题:

public class DomainBL
{
  private readonly IDbDataContextFactory contextFactory;
  **** OPS, i tried to understand about 10+ * articles ***
  ...
}

我尝试了什么?

  1. 使用构造函数注入。但我不知道在 IDBContextFactory 中的 Factory CreateNew() 调用中注入什么。为了清楚:

    public class DomainBLFactory: IDomainBLFactory        
    {
        // Here the constructor requires one argument for passing the factory impl.
        public DomainBL CreateNew()
        {
            return new DomainBL(?????)  // I need a IDBContextFactory impl to resolve.
            //It's not like in the MVC Controller where injection takes place internally
            //for the controller constructor. I'm outside a controller
        }
    }
    

    Useful Post 中,我们独一无二的真正朋友 Remo Gloor 在评论中为我描述了一个可能的解决方案,并引用:“创建一个具有 CreateSomething 方法的接口,该方法可以获取你所需要的一切需要创建实例并让它返回实例。然后在您的配置中实现此接口并将 IResolutionRoot 添加到其构造函数并使用此实例获取所需的对象。”

    问题:如何使用 Ninject.MVC3 和我适度的域类方法以正确的方式实现这一点?如何解决 IResolutionRoot 而不会因在 Service Locator 反模式中的中继而受到惩罚?

  2. 为 IDBContexFactory 使用属性注入。在学习和阅读所有相互矛盾的观点以及关于它的理论解释的过程中,我可以推断这不是为我的 DBContexFactory 类代码进行注入的正确方法。没关系。反正也没用。

    public class DomainBL
    {
    
      [Inject]
      public IDbDataContextFactory contextFactory
      {
          get;
          set;
      }
      //Doesn't works, contextFactory is null with or without parameterless constructor
      .... (methods that uses contextFactory.CreateNew()....
    
    }
    

    问题:我错过了什么?即使这种方法是错误的,属性也不会注入。

  3. 被诅咒。使用 DependencyResolver 并忍受耻辱。这行得通,我将继续采用这种方法,直到为我找到合适的解决方案。这真的很令人沮丧,因为在我过去 10 天的努力中缺乏知识,试图理解和做正确的事情。

    public class DomainBL
    {
      private readonly IDbDataContextFactory contextFactory;
      this.contextFactory = DependencyResolver.Current.GetService<IDbDataContextFactory>();
      //So sweet, it works.. but i'm a sinner.
    
    }
    

    问题:我对注入接口实现的工厂方法和分解业务逻辑的using a Domain Driven Approach 的理解是否存在很大错误?如果我错了,我应该自信地实施哪些模式堆栈?

    我之前看到大量文章和博客并没有以公开、清晰的方式提出这个重要问题。


Remo Gloor 在 www.planetgeek.ch/2011/12/31/ninject-extensions-factory-introduction 中介绍了 Ninject 3.0.0 RC 的 Ninject.Extensions.Factory。

问题:此扩展是否可以与 Ninject.MVC3 一起用于一般的海豚?在这种情况下,这应该是我对不久的将来的希望。

提前感谢大家的指导,请记住,我们感谢您的帮助。我想很多人也会发现这个场景很有用。

【问题讨论】:

    标签: dependency-injection ninject ninject-2 ninject.web.mvc ninject-extensions


    【解决方案1】:

    我真的不明白你们工厂的目的。通常,一个请求只有一个 ObjectContext 实例。这意味着您不需要工厂,只需在请求范围内绑定 myADOEntities 并将其注入您的 DomainBL 而无需添加工厂:

    Bind<myADOEntities>().ToSelf().InRequestScope();
    Bind<DomainBL>().ToSelf().InRequestScope();
    

    是的,工厂和 mvc 扩展一起工作。

    【讨论】:

    • 非常感谢您的快速回复雷莫。好的,我明白了。现在,我将按照您所说的 Bind ToSelf() 我的 impl 来避免工厂。剩下的问题是如何将 myADOEntities 注入不是控制器的类(DomainBL 类)中?在实例化 DomainBL 构造函数时,我提供什么作为构造函数参数?我尝试了属性注入(为了避免构造函数)和构造函数注入(在我的模块中绑定 ToSelf(),同样的问题)不知道要传递什么,除非执行 DependencyResolver。这就是为什么我要求为 IResolutionRoot 提供适当的 impl。
    • 您使用构造函数注入将 myADoEntities 注入 DomainBL 并使用构造函数注入将 DomainBL 注入控制器(或任何需要它的类)。你永远不会调用 new DomainBL()
    【解决方案2】:

    这是一个通用 IFactory 的实现,用于解决问题,而无需求助于 ServiceLocator 反模式。

    首先你定义一个很好的通用工厂接口

      public interface IFactory<T>
      {
        T CreateNew();
      }
    

    并定义使用ninject内核创建请求对象的实现

      class NinjectFactory<T> : IFactory<T>
      {
        private IKernel Kernel;
    
        public NinjectFactory( IKernel Kernel )
        {
          this.Kernel = Kernel;
        }
    
        public T CreateNew()
        {
          return Kernel.Get<T>();
        }
      }
    

    使用以下方法绑定到您的工厂

      private static void RegisterServices(IKernel kernel)
      {
        kernel.Bind<myADOEntities>().ToSelf();
        kernel.Bind<DomainBL>().ToSelf();
        kernel.Bind(typeof(IFactory<>)).To(typeof(NinjectFactory<>));
      }        
    

    您现在可以在控制器中执行以下操作。

      public class MyController : Controller
      {
        private readonly IFactory<DomainBL> DomainBLFactory;
    
        public MyController( IFactory<DomainBL> DomainBLFactory )
        {
          this.DomainBLFactory = DomainBLFactory;
        }
    
        // ... (use the Domain for performing tasks/commands with the Database Context)
      }
    

    在你的 DomainBL 中

      public class DomainBL
      {
        IFactory<myADOEntities> EntitiesFactory;
    
        public DomainBL( IFactory<myADOEntities> EntitiesFactory )
        {
          this.EntitiesFactory = EntitiesFactory;
        }
    
        // ... (use the Entities factory whenever you need to create a Domain Context)
      }
    

    【讨论】: