2010-10-18 13 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