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

时间:2013-09-21 11:20:53

标签: c# inversion-of-control ioc-container service-locator light-inject

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

所有类型都在App_Start中注册:

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

然后当我尝试在控制器中解析它时它会失败并引发stackoverflow异常:

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

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

ServiceLocator.Current.GetInstance<ISomeService>();

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

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

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

EDIT 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()方法,以确保只使用构造函数注入。它仍在抛出堆栈溢出。

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

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

我在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

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

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

1 个答案:

答案 0 :(得分:6)

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

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

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

实际上我们可以采取一些措施来避免将依赖注入到属性中。

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

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

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

  3. 使用LightInject.Annotation

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

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

    希望这有帮助。

    祝你好运

    Bernhard Richter

    修改

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

    考虑这项服务

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

    LightInject将很乐意注入依赖项。

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

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