LightInject IoC容器在解析类型时抛出stackoverflow

在尝试LightInject IoC容器http://www.lightinject.net/时,它会在解析ISomeService类型时抛出stackoverflowexception:

所有类型都在App_Start中注册:

container.RegisterAssembly("MyApp*.dll"); 

然后,当我尝试在控制器中解决它时,它会失败并抛出stackoverflowexception:

  public SomeController(ISomeService someService) { _someService = someService; } 

使用ServiceLocator时也会出现相同的错误:

 ServiceLocator.Current.GetInstance(); 

我已经跟踪了它,我可以看到它在LightInject ServiceContainer类中失败了,但是我不明白为什么它失败了。

 public object GetInstance(Type serviceType) { return GetDefaultDelegate(serviceType, true)(constants.Items); } 

在调用GetDefaultDelegate之后,执行路径再次在GetInstance中结束,导致无限循环和堆栈溢出。

编辑2 – 已进一步跟踪此问题,它似乎是由SomeService同时具有构造函数和属性注入引起的:

 public class SomeService : ISomeService { public IAnotherService AnotherService { get; set; } public SomeService(IAnotherService anotherService) { AnotherService = anotherService; } } 

依赖IAnotherService AnotherService是通过构造函数和属性注入的,但是无意使用属性注入器。

编辑3

应用程序中有几个层都使用ServiceLocator,所以我最初使用NuGet将LI添加到自己的项目中,所以我有:

 MyApp.IoC.LightInject MyApp.Repositories MyApp.Services MyApp.Web.UI 

但实际上并不需要将其添加到自己的项目中,因为可以在UI层中设置服务定位器提供程序:

 var serviceLocator = new LightInjectServiceLocator(container); ServiceLocator.SetLocatorProvider(() => serviceLocator); 

所以我刚刚删除了额外的项目,并将NuGet包放入UI层。 我还安装了LightInject.Annotation并故意不调用container.EnableAnnotatedPropertyInjection()方法,以确保只使用构造函数注入。 它仍在抛出stackoverflow。

要测试解析是否正常,我只是在HomeController.Index()方法中执行此操作:

 public ActionResult Index() { var a = ServiceLocator.Current.GetInstance(); } 

我在ServiceController.GetInstance方法中添加了一些控制台日志记录,以便我可以看到导致stackoverflow的方法调用流程。 这是日志,结果有点出乎意料。 您可以看到,当它为ISomeService调用CreateDelegate()时,它最终会尝试首先获取HomeController的实例 – 为什么会这样?

 DoGetInstance: ISomeService. Key = '' GetInstance: ISomeService TryGetValue: ISomeService not found TryAddValue: trying to add ISomeService now CreateDynamicMethodDelegate: ISomeService CreateDynamicMethodDelegate: calling CreateDelegate() for ISomeService DoGetInstance: HomeController. Key = '' GetInstance: HomeController TryGetValue: HomeController not found TryAddValue: trying to add HomeController now CreateDynamicMethodDelegate: HomeController CreateDynamicMethodDelegate: calling CreateDelegate() for HomeController DoGetInstance: ISomeService. Key = '' GetInstance: ISomeService TryGetValue: ISomeService not found TryAddValue: trying to add ISomeService now CreateDynamicMethodDelegate: ISomeService CreateDynamicMethodDelegate: calling CreateDelegate() for ISomeService DoGetInstance: HomeController. Key = '' GetInstance: HomeController TryGetValue: HomeController not found TryAddValue: trying to add HomeController now CreateDynamicMethodDelegate: HomeController CreateDynamicMethodDelegate: calling CreateDelegate() for HomeController DoGetInstance: ISomeService. Key = '' GetInstance: ISomeService TryGetValue: ISomeService not found TryAddValue: trying to add ISomeService now CreateDynamicMethodDelegate: ISomeService CreateDynamicMethodDelegate: calling CreateDelegate() for ISomeService DoGetInstance: HomeController. Key = '' GetInstance: HomeController TryGetValue: HomeController not found TryAddValue: trying to add HomeController now CreateDynamicMethodDelegate: HomeController CreateDynamicMethodDelegate: calling CreateDelegate() for HomeController DoGetInstance: ISomeService. Key = '' GetInstance: ISomeService TryGetValue: ISomeService not found TryAddValue: trying to add ISomeService now CreateDynamicMethodDelegate: ISomeService CreateDynamicMethodDelegate: calling CreateDelegate() for ISomeService 

当我注释掉服务的构造函数时,解析工作和所有依赖项都通过属性注入来解决。 如果包含构造函数,则它会抛出stackoverflowexception。

虽然解析ISomeService仅在使用属性时起作用,但如果该服务在其依赖项中包含ISomeService,则无法解析IAnotherService。 我希望上面的日志能够解释这个问题。 到目前为止,LightInject的性能明显优于Unity

作为LightInject的作者,我会尽力回答。

首先,LightInject应该永远不会抛出StackOverflowException,因此需要根据您在此处描述的内容进行调查。

我清楚地知道,属性注入不是你的意图,因为注入服务两次没有意义(构造函数和属性)。

实际上我们可以做几件事来避免将dependency injection到属性中。

  1. 通过删除公共setter使属性为只读。
  2. 使用文档所称的“显式”服务注册来注册服务。

     container.Register(f => new SomeService(f.GetInstance())); 

    这将导致公共属性被忽略,因为我们现在已经明确了解如何解决SomeService类的依赖关系。

  3. 使用LightInject.Annotation

    这将确保只有标记有InjectAttribute的属性才会注入其依赖项。 在您的具体情况下,这意味着只需将属性保留为没有属性的属性。 在组合根目录下,我们可以通过这个简单的配置来实现这一点。

     container.EnableAnnotatedPropertyInjection(); 

我将如前所述,进一步调查StackOverflowException并对此案例进行适当的调整。 我的第一个想法是,如果容器找到一个符合注入条件的依赖项作为构造函数依赖项和属性依赖项,则抛出exception。

希望这可以帮助。

最好的祝福

伯恩哈德里希特

编辑

虽然问题似乎已经解决了,但我仍然希望能够在测试中重现这个问题。 获得exception的原因可能并不像具有构造函数依赖性和相同类型的属性依赖性的服务那么简单。

考虑这项服务

 public class FooWithConstructorAndPropertyDependency : IFoo { public FooWithConstructorAndPropertyDependency(IBar bar) { Bar = bar; } public IBar Bar { get; set; } } 

LightInject将很乐意注入依赖项。

 container.Register(); container.Register(); container.GetInstance(); 

所以必须有其他东西导致exception。 如果您能够提出再现exception的最小示例以便可以处理,我将不胜感激。 没有失败的东西我进行进一步的调查有点困难:)如果你选择做这个努力你可以在这里发布或发邮件给bernhard.richter@gmail.com。