2010-10-18 34 views
2

我正在构建一个框架,我不想将它耦合到特定的IOC容器,因此在Ninject/structuremap之上创建了一个图层等。当有多个方法绑定类型时,Ninject绑定到错误的匿名方法

我有一个绑定类,它接受一个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被请求新建立一个MVC控制器这需要在IRepository和显示iCalendar,收到一个类型转换错误,指出一个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' 失败: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根,IParameter []参数) NinjectIocContainer.cs(40,0):在Ioc.Ninject.NinjectIocContainer。GetTInstance IocTestBase.cs(149,0):在Ioc.Test.IocTestBase.Expect_That_Multiple_Method_Bound_Types_Can_Exist_Within_The_Same_Module()

回答

0

在片段:

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

你提领该Method位。你想要做的是binding.Method()=>binding.Method()(根据C#类型推断规则,前者可能无法明确推理)。

你提到这是从你的真实代码严重剥离。因此,这可能不是真正的问题。尽管如此,我仍然在赌注closure confusion(请参阅部分比较捕捉策略:复杂性与电力在此CSID excerpt为一个很好的演练)。

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

+0

我没有我面前的代码,在我头上打字并在.ToMethod(binding.Method())中输入错字。由于这是一个Ninject绑定,它实际上应该包含一个上下文; (c => binding.ToMethod())。我已经更新了原来的问题来反映这一点。再一次,在这个范围上是正确的,因为这是在NinjectModule中。我在链接'封闭混乱'文章中注意到类似问题,其中重复了forloop的最后一个值。然而,我不完全确定如何将其应用于我的案例。 – 2010-10-19 06:42:00

+0

对于基元来说,它的值会被复制,但是如何返回我认为会由ref返回的对象时,它是如何应用的。 – 2010-10-19 07:04:12

+0

@Joshua Hayes:不幸的是,没有你的代码库和VS在我面前,我不觉得我可以猜测任何更有用的方向。仍然不明白你为什么要存储一个范围,然后使用.InRequestScope()。最后一点,我没有做 - 我想知道为什么你觉得你需要抽象容器,而不去CSL路线。我个人通常使用ctor注入,并使代码容器保持中立 - 让这一层变得混乱 - 但我相信您的情况与我目前正在使用的代码库类型不同...... – 2010-10-19 10:00:27