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

时间:2019-03-08 10:48:07

标签: c# wpf autofac mvvm-light

我的ViewModel上有以下代码段,我想摆脱new关键字,将创建责任交给DI容器。但是,由于给定的参数IDataFileReaderViewModel progress属性有关,因此我难以将ViewModel注入我的ProgressBarValue中。

基本上,我的文件阅读器需要进度作为参数,以便我可以在UI上显示进度。

  

所以问题是,如何在IDataFileReader上向AutoFac模块注册   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);
  });

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

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();

    }
  }

3 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

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

上添加DataFileReader方法
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;
    }
}

答案 2 :(得分:1)

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

Autofac支持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);
}