Unity:如果我试图获得在多个映射中注册的单例,将会得到StackOverflowException

本文关键字:注册 单例 StackOverflowException 映射 如果 Unity | 更新日期: 2023-09-27 17:50:55

给定以下两个接口和一个类:

interface IRepository<T> {}
interface IXRepository : IRepository<MyType> {}
class XRepository : IXRepository {}

我想要一个类型为XRepository的单例,它可以通过IRepository<MyType>IXRepository来解析。

我以以下代码开始:

public void Register<TFrom, TTo>(params Type[] typesToRegister) where TTo : TFrom
{
    parentContainer.RegisterType<TFrom, TTo>(new HierarchicalLifetimeManager());
    foreach (Type type in typesToRegister)
    {
        parentContainer.RegisterType(type, typeof(TTo), new HierarchicalLifetimeManager(), new InjectionFactory(c => c.Resolve<TFrom>()));
    }
}

下面是客户端方法调用:

service.Register<IRepository<MyType>, XRepository>(typeof(IXRepository));

执行此语句后的childContainer的注册(标题:RegisteredType, MappedToType, Name和LifetimeManagerType):

  • typeof(IRepository<MyType>), typeof(XRepository>), null, typeof(HierarchicalLifetimeManager)
  • typeof(IXRepository), typeof(XRepository), null, typeof(HierarchicalLifetimeManager)

但是,如果我试图通过IRepository<MyType>(或IXRepository)的方式解决,StackOverflowException将被抛出。这是因为new InjectionFactory(c => c.Resolve<TFrom>())中的lambda表达式被一次又一次地执行。有人知道这是怎么回事吗?在请求IRepository<MyType>时执行lambda表达式的原因是什么?

我发现,如果你在注册时提供一个名字("gaga"在这个例子中),不会抛出StackOverflowException:

  • typeof(IRepository<MyType>) typeof(XRepository>), "gaga" , typeof(HierarchicalLifetimeManager)
  • typeof(IXRepository), typeof(XRepository), null, typeof(HierarchicalLifetimeManager)

谢谢,罗杰

Unity:如果我试图获得在多个映射中注册的单例,将会得到StackOverflowException

您可以将具体类型XRepository注册为单例,然后注册要用该类型解析的两个接口:

container.RegisterType<XRepository>(new HierarchicalLifetimeManager());
container.RegisterType<IRepository<String>, XRepository>();
container.RegisterType(typeof(IXRepository), typeof(XRepository));

如果你运行下面的代码,你会看到单例行为被应用,并且子容器被考虑在内:

var isSingleton = container.Resolve<IRepository<String>>() == container.Resolve<IXRepository>();
var outerInstance = container.Resolve<IXRepository>();
using(var childContainer = container.CreateChildContainer())
{
    isSingleton = childContainer.Resolve<IRepository<String>>() == childContainer.Resolve<IXRepository>();
    var isHierarchichalSingleton = outerInstance  != childContainer.Resolve<IXRepository>();
}

作为旁注,您的问题可以用更简单的方式复制。这可能是Unity中的一个bug 这是根据Unity的设计(检查@Lukazoid评论和回答),例如:

interface IFoo { }
interface IBar { }
class Foo : IFoo, IBar  { }
container.RegisterType<IFoo, Foo>();
container.RegisterType<IBar, Foo>(new InjectionFactory(c => c.Resolve<IFoo>()));
var foo = container.Resolve<IFoo>();

在Unity中使用LifetimeManagers时,只使用为目标类型指定的最后一个LifetimeManager。到相同目标类型的多个接口映射都将使用相同的LifetimeManager,例如

container.RegisterType<IRepository<MyType>, XRepository>(new HierarchicalLifetimeManager());
container.RegisterType<IXRepository, XRepository>();
var first = container.Resolve<IRepository<MyType>>();
var second = container.Resolve<IXRepository>();
Console.WriteLine("Same instance: {0}", ReferenceEquals(first, second));

将输出

相同实例:True

基于这个事实,你的寄存器方法可以像这样改变:

public void Register<TFrom, TTo>(params Type[] typesToRegister) where TTo : TFrom
{
    parentContainer.RegisterType<TFrom, TTo>(new HierarchicalLifetimeManager());
    foreach (Type type in typesToRegister)
    {
        parentContainer.RegisterType(type, typeof(TTo));
    }
}
下面是演示解决方案的LINQPad示例:http://share.linqpad.net/dbd9hx.linq