解决主线程与异步任务中的依赖关系

时间:2017-05-12 02:49:45

标签: c# dependency-injection unity-container

我正在使用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;
   }
}

FacadeOneFacadeTwo都依赖于FirstService

在我的作品根目录中,我创建了一个MainController的实例。当我创建该实例时,我希望发生以下事件(here is a diagram which hopefully makes it clearer):

  • facadeOne的{​​{1}}和facadeTwo参数应使用MainController的相同实例。
  • 解析后传递到ServiceOne参数的facadeOnefacadeTwo参数应与taskSchedulerfacadeOne参数不同传递给facadeTwo的构造函数。
  • 解析MainController参数后,其taskSchedulerfacadeOne参数应使用相同的facadeTwo实例,但它应该是一个不同的实例FirstService的{​​{1}}和MainController参数使用的那个。
  • 解析facadeOne参数后,其facadeTwotaskScheduler参数应使用facadeOnefacadeTwo的不同实例(以及SecondService }如上所述)比ThirdService的{​​{1}}和FirstService参数使用的实例。

有没有一种简单的方法可以在Unity中使用生命周期管理器来完成此操作,而不是创建一个错综复杂且容易出错的MainController s,facadeOne s和临时变量序列?

2 个答案:

答案 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显示对象哈希码。