如何将数据传递给注入的服务

How to pass data to injected services

我想编写一个小型 WinForms 或 WPF 程序来控制一次只有一个 person/computer 通过网络共享使用特定的共享文件。这个文件需要在工作时复制到本地,工作完成后复制回网络共享。

我想创建一个只有一个按钮的小型 WPF 应用程序来将文件标记为已锁定,将其复制到本地并打开与该文件扩展名关联的应用程序。当这个应用程序关闭时,文件被复制回网络共享并释放锁。每台电脑都会使用这个应用程序来访问文件,所以绝不能是两台电脑同时编辑同一个文件。

应用程序必须有某种配置文件,其中包含本地和远程文件夹的路径、文件名和应用程序打开该文件的路径。为了简化应用程序的设置,它将通过 SettingsWindow 完成。

我正在尝试使用 IoC 和某种轻量级 DI 容器(即 SimpleInjector)来做到这一点,但我对如何正确地做到这一点有一些疑问:

Program.cs

static class Program
{
    [STAThread]
    static void Main()
    {
        var container = Bootstrap();

        // Any additional other configuration, e.g. of your desired MVVM toolkit.

        RunApplication(container);
    }

    private static Container Bootstrap()
    {
        // Create the container as usual.
        var container = new Container();

        // Register your types, for instance:
        container.Register<ILauncher, Launcher>();
        container.Register<IFileSyncService, FileSyncronizer>();
        container.Register<IAppRunnerService, AppRunnerService>();
        container.Register<ILockService, LockService>();

        // Register your windows and view models:
        container.Register<MainWindow>();
        container.Register<ConfigurationWindow>();

        container.Verify();

        return container;
    }

    private static void RunApplication(Container container)
    {
        try
        {
            var app = new App();
            var mainWindow = container.GetInstance<MainWindow>();
            app.Run(mainWindow);
        }
        catch (Exception ex)
        {
            //Log the exception and exit
        }
    }
}

主窗口

我修改了构造函数以接收将由 DI 容器解析的接口 ILauncher。

    public partial class MainWindow : Window
    {
        public MainWindow(ILauncher launcher)
        {
            InitializeComponent();
        }
…
}

ILauncher界面

public interface ILauncher
{
    void Run();
}

启动器实现

启动器实现将负责协调启动应用程序和编辑文件所需的每项任务。这包括:检查并获取锁、同步文件、执行并监视应用程序是否已关闭。为了遵循单一职责原则 (SRP),这是通过注入的一些服务完成的:

public class Launcher : ILauncher
{
    public Launcher(
        ILockService lockService,
        IAppRunnerService appRunnerService,
        IFileSyncService fileSyncService
        )
    {
        LockService = lockService;
        AppRunnerService = appRunnerService;
        FileSyncService = fileSyncService;
    }

    public ILockService LockService { get; }
    public IAppRunnerService AppRunnerService { get; }
    public IFileSyncService FileSyncService { get; }

    public void Run()
    {
        //TODO check lock + adquire lock
        //TODO Sinchronize file
        //TODO Subscribe to the IAppRunnerService.OnStop event 
        //TODO Start app through IAppRunnerService.Run method
    }
}

我的问题:

  1. 使用“new”关键字或手动调用 类 或 windows 中的容器创建新实例是一种不好的做法,因此桌面的入口点应用程序(通常是某种主要 window)应该(通过构造函数)询问所有内容。当应用程序增长时,这似乎不是一个合适的方法。我对吗?解决方法是什么?

  2. 在我的应用程序中,每个服务都需要某种运行时数据(可执行文件路径、本地或远程目录……)。容器在应用程序执行的早期实例化它们,甚至在这些数据已知之前。服务如何接收这些数据? (请注意,稍后可以通过 SettingsWindow 修改数据)。

很抱歉延长这个 post 但我想把我的问题和上下文说清楚。

  1. 正如您所说,您必须在入口点请求一些服务,但什么是请求一切?您的入口点应该只将它直接使用的服务指定为 ctor 参数。如果您的应用程序增长并且您突然有十几个服务直接由您的入口点调用 - 这可能表明一些服务可以合并到一个更大的数据包中。不过,这是基于意见的领域,只要您认为它是可维护的就可以了。

  2. 这里有许多可能的解决方案,一个是 ISettingsManager 允许实时访问将其作为依赖项的任何服务的设置。