当存在多个方法绑定类型时,Ninject绑定到错误的匿名方法

时间:2010-10-18 12:03:33

标签: dependency-injection ioc-container ninject

我正在构建一个我不想耦合到特定IOC容器的框架,因此在Ninject / structuremap等之上创建了一个层。

我有一个接受Func的绑定类,允许绑定到方法。

例如

public class Binding
{
 public Type Source { get; set; }
 public Func<object> Method {get; set; }
 public Scope { get; set; }
}

如果我有像...那样的约束力。

var binding = new Binding() { 
Source = typeof(IRepository),
Method = () => new Repository(new LinqToSqlRepository(connectionString)),
Scope = Scope.HttpRequest
};

包装Ninject的框架为我的通用绑定创建了Ninject绑定,如此

Module :NinjectModule
{
 IList<Binding> _Bindings;

 public Module(IList<Binding> bindings)
 {
    _Bindings = bindings;
 }

 public override void Load() { 
    foreach (var binding in _Bindings) {
      switch(binding.Scope) {
        case IocScope.HttpRequest:
          Bind(binding.Source).ToMethod(c => binding.Method()).InRequestScope();
          break;
        // ... omitted for brevity
      }
    }
  }
}

当只有一个绑定绑定到方法时,这可以正常工作。当多个绑定在同一模块中绑定到方法时,会返回不正确的类型。从调试开始,看起来好像总是使用最后一个绑定。

这样一个例子的问题;

var binding1 = new Binding() { 
 Source = typeof(IRepository),
 Method = () => new Repository(new LinqToSqlRepository(connectionString)),
 Scope = Scope.HttpRequest
};

var binding2 = new Binding() { 
 Source = typeof(ICalendar),
 Method = () => new MvcCalendar( ..... )
 Scope = Scope.HttpRequest
};

在运行时,当Ninject被要求新建一个接收IRepository和ICalendar的MVC控制器时,我收到一个类型转换错误,指出MvcCalendar无法转换为IRepository。我发现由于某种原因,始终为第一个请求的类型返回最后一个绑定。

这是一个高度简化的版本,它正在尝试突出实际问题,当有多个方法绑定时,错误的方法被绑定到请求的类型。我希望这仍然可以解释这个问题。

这似乎与某种封闭范围问题有关。我也想知道Ninject是否会被Func而不是Func使用感到困惑。

单元测试示例

这是我加载到自定义IOC容器中的测试模块。这不依赖于任何特定的IOC框架。当我实例化NinjectIocContainer来处理DI时,Ninject中的内部绑定作为示例进一步发生(参见NinjectModule)

public class MultipleMethodBoundTypesModule : IocModule
{
    public override void Load()
    {
        Bind<IPerson>().To(() => new Person()).In(IocScope.Transient);
        Bind<IRobot>().To(() => new Robot(new Person())).In(IocScope.Transient);
    }
}

这是一个尝试检索每种类型的简单测试。

    [Test]
    public void   Expect_That_Multiple_Method_Bound_Types_Can_Exist_Within_The_Same_Module()
    {
        // arrange
        var container = Get_Container_With_Module(new MultipleMethodBoundTypesModule());

        // act
        var person = container.Get<IPerson>();
        var robot = container.Get<IRobot>();

        // assert
        Assert.IsNotNull(person);
        Assert.IsNotNull(robot);
    }

正如所解释的那样,它会抛出一个类型转换,其中最后一个闭包(对于机器人)被绑定到一个人。

  

TestCase'Ioc.Test.NinjectContainerTest.Expect_That_Multiple_Method_Bound_Types_Can_Exist_Within_The_Same_Module'   failed:System.InvalidCastException:无法将类型为“Ioc.Test.Robot”的对象强制转换为“Ioc.Test.IPerson”。       在System.Linq.Enumerable.d__b1 1.MoveNext() at System.Linq.Enumerable.Single[TSource](IEnumerable 1来源)       在Ninject.ResolutionExtensions.Get [T](IResolutionRoot root,IParameter []参数)       NinjectIocContainer.cs(40,0):at Ioc.Ninject.NinjectIocContainer.GetTInstance       IocTestBase.cs(149,0):at Ioc.Test.IocTestBase.Expect_That_Multiple_Method_Bound_Types_Can_Exist_Within_The_Same_Module()

1 个答案:

答案 0 :(得分:0)

在摘录中:

      Bind(binding.Source).ToMethod(binding.Method()).InRequestScope();

您取消引用Method位。您希望将其作为binding.Method()=>binding.Method()(前者可能无法根据C#类型推断规则明确推断)。

你提到过你的实际代码很少被剥夺。因此,这可能不是实际问题。我仍然会押注某种形式的closure confusion(参见比较捕获策略:复杂性与功效一节中的CSID excerpt以获得精彩的演练)。

你也可能想要使用.InScope(binding.Scope)而不是.InRequestScope()