首页 > 解决方案 > 如何将数据传递给注入的服务

问题描述

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

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

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

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

程序.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”关键字或手动调用类或窗口中的容器来创建新实例是一种不好的做法,因此桌面应用程序的入口点(通常是某种主窗口)应该(通过构造函数)询问一切。当应用程序增长时,这似乎不是一个正确的方法。我对吗?解决办法是什么?

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

很抱歉这篇文章的扩展,但我想让我的问题和上下文清楚。

标签: c#dependency-injectioninversion-of-control

解决方案


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

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


推荐阅读