我正在使用Unity开发MVC-ish(可能更像MVP)桌面应用程序,Unity具有主UI线程并定期在后台运行异步任务。主窗口的UI事件由MainController
处理。当用户打开一个新窗口时,MainController
将创建一个新的子控制器并传入它需要的任何依赖项。当主窗口中的计时器滴答时,它会调用MainController
上的方法来启动异步任务。 MainController
中有任务同步,所以我一次没有运行多个aysnc任务。
我正在向MainController
注入两个服务外观和异步任务调度程序。这两个服务门面有一个服务,他们各自依赖。
外观服务和任务调度程序服务如下所示:
public class FacadeOne : IFacadeOne
{
private readonly IFirstService firstService;
private readonly ISecondService secondService;
public FacadeOne(IFirstService firstService, ISecondService secondService)
{
this.firstService = firstService;
this.secondService = secondService;
}
}
public class FacadeTwo : IFacadeTwo
{
private readonly IFirstService firstService;
private readonly IThirdService thirdService;
public FacadeTwo(IFirstService firstService, IThirdService thirdService)
{
this.firstService = firstService;
this.thirdService = thirdService;
}
}
public class TaskScheduler : ITaskScheduler
{
private readonly IFacadeOne facadeOne;
private readonly IFacadeTwo facadeTwo;
public TaskScheduler(IFacadeOne facadeOne, IFacadeTwo facadeTwo)
{
this.facadeOne = facadeOne;
this.facadeTwo = facadeTwo;
}
}
MainController
看起来像这样
public class MainController
{
private readonly IFacadeOne facadeOne;
private readonly IFacadeTwo facadeTwo;
private readonly ITaskScheduler taskScheduler;
public MainController(IFacadeOne facadeOne, IFacadeTwo facadeTwo, ITaskScheduler taskScheduler)
{
this.facadeOne = facadeOne;
this.facadeTwo = facadeTwo;
this.taskScheduler = taskScheduler;
}
}
FacadeOne
和FacadeTwo
都依赖于FirstService
。
在我的作品根目录中,我创建了一个MainController
的实例。当我创建该实例时,我希望发生以下事件(here is a diagram which hopefully makes it clearer):
facadeOne
的{{1}}和facadeTwo
参数应使用MainController
的相同实例。ServiceOne
参数的facadeOne
和facadeTwo
参数应与taskScheduler
和facadeOne
参数不同传递给facadeTwo
的构造函数。MainController
参数后,其taskScheduler
和facadeOne
参数应使用相同的facadeTwo
实例,但它应该是一个不同的实例FirstService
的{{1}}和MainController
参数使用的那个。facadeOne
参数后,其facadeTwo
和taskScheduler
参数应使用facadeOne
和facadeTwo
的不同实例(以及SecondService
}如上所述)比ThirdService
的{{1}}和FirstService
参数使用的实例。有没有一种简单的方法可以在Unity中使用生命周期管理器来完成此操作,而不是创建一个错综复杂且容易出错的MainController
s,facadeOne
s和临时变量序列?
答案 0 :(得分:1)
我看到两个一种做法,第二种要避免!
使用ContainerControlledLifetimeManager命名注册
如果您不介意将某些注册与注册注册加倍,您可以这样做:
container.RegisterType<IFirstService, FirstService>("MainThread", new ContainerControlledLifetimeManager());
container.RegisterType<ISecondService, SecondService>("MainThread", new ContainerControlledLifetimeManager());
container.RegisterType<IThirdService, ThirdService>("MainThread", new ContainerControlledLifetimeManager());
container.RegisterType<IFirstService, FirstService>("TaskScheduler", new ContainerControlledLifetimeManager());
container.RegisterType<ISecondService, SecondService>("TaskScheduler", new ContainerControlledLifetimeManager());
container.RegisterType<IThirdService, ThirdService>("TaskScheduler", new ContainerControlledLifetimeManager());
container.RegisterType<IFacadeOne, FacadeOne>("MainThread", new ContainerControlledLifetimeManager(),
new InjectionConstructor(
new ResolvedParameter<IFirstService>("MainThread"),
new ResolvedParameter<ISecondService>("MainThread")));
container.RegisterType<IFacadeTwo, FacadeTwo>("MainThread", new ContainerControlledLifetimeManager(),
new InjectionConstructor(
new ResolvedParameter<IFirstService>("MainThread"),
new ResolvedParameter<IThirdService>("MainThread")));
container.RegisterType<IFacadeOne, FacadeOne>("TaskScheduler", new ContainerControlledLifetimeManager(),
new InjectionConstructor(
new ResolvedParameter<IFirstService>("TaskScheduler"),
new ResolvedParameter<ISecondService>("TaskScheduler")));
container.RegisterType<IFacadeTwo, FacadeTwo>("TaskScheduler", new ContainerControlledLifetimeManager(),
new InjectionConstructor(
new ResolvedParameter<IFirstService>("TaskScheduler"),
new ResolvedParameter<IThirdService>("TaskScheduler")));
container.RegisterType<ITaskScheduler, TaskScheduler>("TaskScheduler", new ContainerControlledLifetimeManager(),
new InjectionConstructor(
new ResolvedParameter<IFacadeOne>("TaskScheduler"),
new ResolvedParameter<IFacadeTwo>("TaskScheduler")));
container.RegisterType<MainController>(new ContainerControlledLifetimeManager(),
new InjectionConstructor(
new ResolvedParameter<IFacadeOne>("MainThread"),
new ResolvedParameter<IFacadeTwo>("MainThread"),
new ResolvedParameter<ITaskScheduler>("TaskScheduler")));
MainController imWhatYouWanted = container.Resolve<MainController>();
注意1:您可以删除所有“MainThread”名称(不是注册,只是将其设为 not-named - ,它的工作方式相同。与{{1}的注册相同本身,你可能根本就没有命名,但你仍然需要使用ITaskScheduler
中的名字。
注意2:您可以使用ResolvedParameters
代替HierarchicalLifetimeManager
。如果不使用子容器,它们的行为相同。
黑客我不建议
除非你绝对不能使用命名注册,否则你不能推荐的另一种方法是:使用ContainerControlledLifetimeManager
代替HierarchicalLifetimeManager
,并结合子容器:
ContainerControlledLifetimeManager
使用第二个解决方案,通过使用container.RegisterType<IFirstService, FirstService>(new HierarchicalLifetimeManager());
container.RegisterType<ISecondService, SecondService>(new HierarchicalLifetimeManager());
container.RegisterType<IThirdService, ThirdService>(new HierarchicalLifetimeManager());
container.RegisterType<IFacadeOne, FacadeOne>(new HierarchicalLifetimeManager(),
new InjectionConstructor(
new ResolvedParameter<IFirstService>(),
new ResolvedParameter<ISecondService>()));
container.RegisterType<IFacadeTwo, FacadeTwo>(new HierarchicalLifetimeManager(),
new InjectionConstructor(
new ResolvedParameter<IFirstService>(),
new ResolvedParameter<IThirdService>()));
IUnityContainer childContainer = container.CreateChildContainer();
childContainer.RegisterType<ITaskScheduler, TaskScheduler>(new HierarchicalLifetimeManager(),
new InjectionConstructor(
new ResolvedParameter<IFacadeOne>(),
new ResolvedParameter<IFacadeTwo>()));
// Resolve at registration time == Bad.
// You could do a work around too, but that's another lesson!
ITaskScheduler taskScheduler = childContainer.Resolve<ITaskScheduler>();
container.RegisterInstance<ITaskScheduler>(taskScheduler);
container.RegisterType<MainController>(new HierarchicalLifetimeManager(),
new InjectionConstructor(
new ResolvedParameter<IFacadeOne>(),
new ResolvedParameter<IFacadeTwo>(),
new ResolvedParameter<ITaskScheduler>()));
MainController imWhatYouShouldntWant = container.Resolve<MainController>();
并在子容器上解析它, Unity 在您解析父级时将不会考虑任何事情(使用该LifetimeManager)。
注3:提供图像的好工作,让你很容易理解你想要的东西!
答案 1 :(得分:1)
根据您的场景和图表,清楚您真正想要的是创建一个新的子容器,从中解析任务调度程序。您只需使用ITaskScheduler
的代理类即可将此调用转发给从子容器中解析的TaskScheduler
。
首先设置父容器。每当我们在子容器中解析时,使用HierarchicalLifetimeManager
将为我们提供一个新实例:
var container = new UnityContainer();
container.RegisterType<IFirstService, FirstService>(new HierarchicalLifetimeManager());
container.RegisterType<ISecondService, SecondService>(new HierarchicalLifetimeManager());
container.RegisterType<IThirdService, ThirdService>(new HierarchicalLifetimeManager());
container.RegisterType<IFacadeOne, FacadeOne>(new HierarchicalLifetimeManager());
container.RegisterType<IFacadeTwo, FacadeTwo>(new HierarchicalLifetimeManager());
// Note that we don't map this against ITaskScheduler
container.RegisterType<TaskScheduler>(new HierarchicalLifetimeManager());
container.RegisterType<MainController>(new ContainerControlledLifetimeManager());
然后我们添加代理TaskScheduler的注册:
container.RegisterInstance<ITaskScheduler>(new UnityChildScopedTaskScheduler(container), new ContainerControlledLifetimeManager());
UnityChildScopedTaskScheduler
声明为:
public class UnityChildScopedTaskScheduler : ITaskScheduler, IDisposable
{
private IUnityContainer childContainer;
private ITaskScheduler realTaskScheduler;
private ITaskScheduler taskScheduler
{
get
{
if(realTaskScheduler == null)
{
realTaskScheduler = childContainer.Resolve<TaskScheduler>();
}
return realTaskScheduler;
}
}
public UnityChildScopedTaskScheduler(IUnityContainer container)
{
childContainer = container.CreateChildContainer();
}
// Implement ITaskScheduler methods, passing the calls to taskScheduler
public void Dispose()
{
childContainer.Dispose();
}
}
大多数代码应该是不言自明的。只需传入父容器,并在首次使用时解析实际任务调度程序。你显然可以使这个泛型并适用于其他类。我认为你也可以自己注射容器,但我更喜欢直接注射容器,所以它更明显地发生了什么!
最后像往常一样从主容器中解析你的控制器,你可以去:
var controller = container.Resolve<MainController>();
Here's a dotnet fiddle显示对象哈希码。