我在ViewModel
上有以下代码片段,我想摆脱new
关键字,并将创建的责任交给DI容器。但是,我有一些困难能够将IDataFileReader
注入我的ViewModel
,因为给定的参数progress
与ViewModel
ProgressBarValue
属性相关联。
基本上,我的文件阅读器需要将进度作为参数,因此我可以在我的UI上显示进度。
所以问题是,如何在
IDataFileReader
上使用AutoFac模块注册ViewModelLocator
?
ViewModel.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();
}
}
另一种选择是注入一个可以创建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);
}
基本上,你不能以很好的方式做到这一点:)你应该首先问自己,为什么DataFileReader
关心进步。也许,其他事情应该观察进展并向全世界报告?
我还建议避免使用ServiceLocator模式。类应该只包含通过构造函数显式注入的明确定义的依赖项。在我看来,属性注入也应该被视为反模式。
你想要的是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;
}
}