如何使用 DI 容器(Autofac)注册带参数的服务

How to use DI-container (Autofac) to register service with parameters

我的 ViewModel 上有以下代码片段,我想去掉 new 关键字并将创建的责任交给 DI 容器。但是,我在将 IDataFileReader 注入我的 ViewModel 时遇到了一些困难,因为给定的参数 progress 绑定到 ViewModel ProgressBarValue 属性.

基本上,我的文件 reader 需要进度作为参数,因此我可以在我的 UI 上显示进度。

So the question is, how to register IDataFileReader with AutoFac modules on ViewModelLocator?

VieModel.cs

  ProgressBarIsIndetermined = true;
  var progress = new Progress<int>(status => { ProgressBarValue = status; });

  await Task.Run(() =>
  {
    IDataFileReader fileImporter = new DataFileReader(progress);
    DataSet = new ObservableCollection<MeasurementPoint>(fileImporter.DataSet);
  });

我在 WPF 中使用 Mvvm Light viewmodelLocator 和 MVVM。对于不需要任何参数的简单服务,我可以通过构造函数注入轻松实现。

ViewModelLocator.cs

static ViewModelLocator()
{
  var builder = new ContainerBuilder();
  builder.RegisterModule<AutofacModule>();
  var container = builder.Build();
  ServiceLocator.SetLocatorProvider(() => new AutofacServiceLocator(container));
}

public SettingsViewModel SettingsViewModel => ServiceLocator.Current.GetInstance<SettingsViewModel>();

AutoFacModule.cs

以下模块只是一个草稿,适用于不带参数的简单构造函数注入。

  public class AutofacModule : Module
  {
    protected override void Load(ContainerBuilder builder)
    {
      builder.RegisterType<DataFileReader>().As<IDataFileReader>();   
      builder.RegisterType<SettingsViewModel>().AsSelf().SingleInstance();

    }
  }

基本上,您不能很好地做到这一点 :) 您应该首先问问自己,为什么 DataFileReader 关心进度。也许,应该有其他东西来观察进展并将其报告给世界?

我还建议避免使用 ServiceLocator 模式。 类 应该只包含通过构造函数显式注入的明确定义的依赖项。在我看来,属性注入也应该被视为反模式。

您想要的是 DataFileReader 更新 ViewModel 的 ProgressBarValue 属性。最简单的方法是在 DataFileReader

上添加 OnUpdate 方法
reader.OnUpdate(status => this.ProgressBarValue = status.PercentProgress); 

这样做会给您的 IDataFileReader 界面增加一个新的责任,它可能不合适并破坏 Single Responsibility Principle

在这种情况下,通常会引入一个只关注一件事的新组件。

public interface IProgressObserver
{
    void OnUpdate(Action<Int32> updater);
    void Update(Int32 percent);
}

您的 DataFileReader 可以依赖此组件并在需要时调用 Update 方法。您的 ViewModel 将具有 IProgressObserver 依赖项和 IDataFileReader

IProgressObserver 的一种可能实现可以像

一样简单
public class ProgressObserver : IProgressObserver
{
    private Action<Int32> _updater = _ => { };

    public void Update(Int32 percent)
    {
        this._updater(percent);
    }
    public void Register(Action<Int32> updater)
    {
        this._updater = updater;
    }
}

另一种选择是注入一个可以创建 IDataFileReader 的委托,而不是注入一个已经实例化的委托。这将允许您将 Progress 对象传递给它。

A​​utofac 支持 delegate factories。这可能会导致类似以下内容(未经测试):

public class DataFileReader : IDataFileReader
{
  public delegate DataFileReader Factory(Progress progress);

  public Shareholding(Progress progress)
  {
    Progress = progress;
  }
}

public class ViewModel
{
  private readonly DataFileReader.Factory factory;
  public ViewModel(DataFileReader.Factory dataFileReaderFactory)
  {
    factory = dataFileReaderFactory;
  }

  ...
  IDataFileReader fileImporter = factory(progress);
}