Search code examples
c#wpfautofacmvvm-light

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

    }
  }

Solution

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