简体   繁体   中英

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

I have the following code snippet on my ViewModel and I would like to get rid of the new keyword and give the responsibility of creation to a DI-container. However, I am having some difficulties to be able to inject IDataFileReader into my ViewModel because the given parameter progress is tied to a ViewModel ProgressBarValue property.

Basically, my file reader requires the progress as a parameter so I can display the progress on my 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);
  });

I am using Mvvm Light viewmodelLocator and MVVM with WPF. For simple services which do not require any parameters, I can easily achieve this by constructor injection.

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

The following module is just a draft and would work for a simple constructor injection without parameters.

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

    }
  }

Basically, you can't do this in nice way :) You should first ask yourself, why does DataFileReader cares about progress. Maybe, something else should observe progress and report it to the world?

I'd also recommend avoiding ServiceLocator pattern. Classes should contain only well defined dependencies injected explicitly via constructor. Properties injection should also be considered as anti pattern in my opinion.

What you want is that DataFileReader update the ProgressBarValue property of your ViewModel. The easiest way to do that would be to add a OnUpdate method on the DataFileReader

reader.OnUpdate(status => this.ProgressBarValue = status.PercentProgress); 

By doing so you will add a new responsibility to your IDataFileReader interface which may not be suitable and break Single Responsibility Principle .

In this case it is common to introduce a new component that will focus on only one thing.

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

Your DataFileReader can rely on this component and call the Update method when needed. Your ViewModel will have a IProgressObserver dependency and a IDataFileReader

One possible implementation for IProgressObserver can be as easy as

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;
    }
}

An alternative option is to inject a delegate that can create the IDataFileReader , rather than an already instantiated one. This will allow you to pass the Progress object to it.

Autofac has support for delegate factories . This could result in something like the following (untested):

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM