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

JK.*_*JK. 5 c# ioc-container inversion-of-control service-locator light-inject

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

所有类型都在App_Start中注册:

container.RegisterAssembly("MyApp*.dll");
Run Code Online (Sandbox Code Playgroud)

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

    public SomeController(ISomeService someService)
    {
         _someService = someService;
    }
Run Code Online (Sandbox Code Playgroud)

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

ServiceLocator.Current.GetInstance<ISomeService>();
Run Code Online (Sandbox Code Playgroud)

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

public object GetInstance(Type serviceType)
{
    return GetDefaultDelegate(serviceType, true)(constants.Items);
}
Run Code Online (Sandbox Code Playgroud)

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

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

public class SomeService : ISomeService 
{
    public IAnotherService AnotherService { get; set; }
    public SomeService(IAnotherService anotherService)
    {
        AnotherService = anotherService;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

编辑3

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

MyApp.IoC.LightInject
MyApp.Repositories
MyApp.Services
MyApp.Web.UI
Run Code Online (Sandbox Code Playgroud)

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

var serviceLocator = new LightInjectServiceLocator(container);
ServiceLocator.SetLocatorProvider(() => serviceLocator);
Run Code Online (Sandbox Code Playgroud)

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

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

public ActionResult Index()
{
    var a = ServiceLocator.Current.GetInstance<ISomeService>();
}
Run Code Online (Sandbox Code Playgroud)

我在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
Run Code Online (Sandbox Code Playgroud)

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

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

see*_*per 6

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

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

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

实际上我们可以做几件事来避免将依赖注入到属性中.

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

    container.Register<ISomeService>(f => new SomeService(f.GetInstance<IAnotherService>())); 
    
    Run Code Online (Sandbox Code Playgroud)

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

  3. 使用LightInject.Annotation

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

    container.EnableAnnotatedPropertyInjection(); 
    
    Run Code Online (Sandbox Code Playgroud)

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

希望这可以帮助.

最好的祝福

伯恩哈德里希特

编辑

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

考虑这项服务

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

    public IBar Bar { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

LightInject将很乐意注入依赖项.

container.Register<IBar, Bar>();
container.Register<IFoo, FooWithConstructorAndPropertyDependency>();

container.GetInstance<IFoo>();
Run Code Online (Sandbox Code Playgroud)

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