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

所有类型都在App_Start中注册:

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

然后,当我尝试在 Controller 中解决该问题时,它会失败并引发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中返回,从而导致无限循环和堆栈溢出。

编辑2-对此进行了进一步的跟踪,它似乎是由SomeService同时具有构造函数和属性注入(inject)引起的:
public class SomeService : ISomeService
{
    public IAnotherService AnotherService { get; set; }
    public SomeService(IAnotherService anotherService)
    {
        AnotherService = anotherService;
    }
}

通过构造函数和属性注入(inject)了依赖项IAnotherService AnotherService,但无意使用属性注入(inject)器。

编辑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()方法,以确保仅使用构造函数注入(inject)。它仍然会引发stackoverflow。

要测试解析是否有效,我只是在HomeController.Index()方法中执行以下操作:
public ActionResult Index()
{
    var a = ServiceLocator.Current.GetInstance<ISomeService>();
}

我向ServiceController.GetInstance方法添加了一些控制台日志记录,以便可以看到导致栈溢出的方法调用流。这是日志,结果有点出乎意料。您可以看到,当它为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

当我注释掉服务的构造函数时,解析工作完成,所有依赖项都通过属性注入(inject)来解析。如果包含构造函数,则将引发stackoverflow异常。

而且,尽管解析ISomeService仅在使用属性时有效,但如果该服务在其依赖项中包含ISomeService,则无法解析IAnotherService。我希望上面的日志可以使您对这个问题有所了解。到目前为止,LightInject的性能已经大大优于Unity。

最佳答案

作为LightInject的作者,我将尽力回答最好的问题。

首先,LightInject绝不应该引发StackOverflowException,因此需要根据您在此处描述的内容进行检查。

而且我清楚地知道,此处并不是您打算使用属性注入(inject),因为两次注入(inject)服务(构造函数和属性)几乎没有意义。

实际上,我们可以做一些事情来避免将依赖项注入(inject)到属性中。

  • 通过删除公共(public) setter ,使属性为只读。
  • 使用文档称为“显式”服务注册的方式注册服务。
    container.Register<ISomeService>(f => new SomeService(f.GetInstance<IAnotherService>()));
    

    这将导致公共(public)属性被忽略,因为我们现在已经明确说明了如何解决SomeService类的依赖关系。
  • 使用LightInject.Annotation

    这将确保仅标记有InjectAttribute的属性将注入(inject)其依赖项。在您的特定情况下,这意味着只保留该属性而没有该属性。
    在组合的根部,我们可以通过进行简单的配置来启用它。
    container.EnableAnnotatedPropertyInjection();
    

  • 如前所述,我将进一步调查StackOverflowException并针对这种情况进行适当的调整。我的第一个想法是,如果容器找到了既可以作为构造函数依赖又可以作为属性依赖注入(inject)的依赖,则抛出异常。

    希望这可以帮助。

    此致

    伯恩哈德·里希特

    编辑

    尽管您的情况似乎可以解决问题,但我仍然希望能够在测试中重现此问题。出现异常的原因可能不像具有相同类型的构造函数依赖项和属性依赖项的服务那样简单。

    考虑这项服务
    public class FooWithConstructorAndPropertyDependency : IFoo
    {
        public FooWithConstructorAndPropertyDependency(IBar bar)
        {
            Bar = bar;
        }
    
        public IBar Bar { get; set; }
    }
    

    LightInject将愉快地注入(inject)依赖项。
    container.Register<IBar, Bar>();
    container.Register<IFoo, FooWithConstructorAndPropertyDependency>();
    
    container.GetInstance<IFoo>();
    

    因此,肯定还有其他导致异常的原因。如果您能拿出最小的例子来重现异常,以便能够解决该问题,我将不胜感激。如果没有任何失败,对我来说做进一步的调查会有些困难:)如果您选择进行此操作,可以在此处发布或将其发送给[email protected]

    10-06 12:05