C# 从合成根目录解析交互请求的视图模型?

C# 从合成根目录解析交互请求的视图模型?,c#,wpf,mvvm,dependency-injection,C#,Wpf,Mvvm,Dependency Injection,我最近启动了一个遵循MVVM模式的WPF项目。我正在尝试一次性解析视图模型的对象图(在组合根目录中,即应用程序的启动事件处理程序中),以避免任何其他类依赖于我的IoC容器: public partial class App : Application { private void OnStartup(object sender, StartupEventArgs e) { // Composition Root var container = new

我最近启动了一个遵循MVVM模式的WPF项目。我正在尝试一次性解析视图模型的对象图(在组合根目录中,即应用程序的启动事件处理程序中),以避免任何其他类依赖于我的IoC容器:

public partial class App : Application
{
    private void OnStartup(object sender, StartupEventArgs e)
    {
       // Composition Root

       var container = new UnityContainer();

       container.RegisterType<IResidentListViewModel, ResidentListViewModel>();
       container.RegisterType<IMainViewModel, MainViewModel>();

       container.RegisterType<IDataService, DataService>();

       var mainWindow = new MainWindow(container.Resolve<IMainViewModel>());

       Current.MainWindow = mainWindow;
       mainWindow.Show();
    }
}
这不是问题,因为容器将很好地解决该依赖关系

但是,ResidentListviewModel有一个访问IDataService的居民属性:

private readonly IDataService dataService;

public ResidentListViewModel(IDataService dataService)
{
   if (dataService == null)
      throw new ArgumentNullException("dataService");

   this.dataService = dataService;
}
private readonly ObservableCollection<IResidentViewModel> residents = new ObservableCollection<IResidentViewModel>(); 

public ObservableCollection<IResidentViewModel> Residents
{
   get
   {
      if (this.residents == null)
         LoadResidents();

      return this.residents;
   }
}
我知道,我知道,反复添加到一个可观的集合是不可能的,但请在这里容忍我。看到那个闪亮的“新”关键词了吗?这才是真正的罪魁祸首。我不知道如何在不放弃服务定位器(“实例工厂”)的情况下摆脱它,根据我最喜欢的DI书籍作者Mark Seemann的说法,这反过来是一种反模式(是的,我有这本书,并会向任何C#开发者推荐)

当然,我可以注入vm列表本身,但这会将数据的检索移到合成根(听起来是个坏主意),我也可以很容易地提出一个场景,其中用户选择一个条目,代码必须根据该选择检索数据,这让我回到原点

所以问题是:有没有办法用来自composition root的同一个调用来处理这个问题?

您已经接近了。不是实例工厂反模式,而是模式:


residentViewModelFactory
当然是通过构造函数注入作为接口提供的依赖项,这使得一切都很好并且可以测试,当然也可以从组合根(将工厂注册为组件之一)进行设置。

使用容器和构造函数注入注册工厂,而不是静态的构造函数注入(阅读:全局)服务定位器适用于我:)+1,尽管AFAICT,
residentViewModelFactory
不是一个工厂方法,而是一个抽象工厂。@jimmy_keen如果你在中查找工厂方法的原始描述,你会看到它是专门根据模板方法模式描述的。这两种模式都依赖继承来实现。相反,如果您通过组合提供某种工厂接口,这是一种策略,在本例中,更具体地说是一种抽象工厂。接口上的
CreateInstance
方法不是一种模式——它只是一种方法;所以你不能将这种方法称为工厂方法,并期望人们理解你的意思。@Jaans:在这种情况下,我认为工厂将是新的实例。“如果它不得不使用容器来获取实例,我认为这种工厂的存在没有多大意义,因为它所做的只是委托给容器。”jimmy_keen说,“工厂新闻”对此表示反对。容器的范围仅限于启动时的
on
(即合成根),并且仅用于创建对象图。当它超出范围时,没有办法更新对象。
private async Task LoadResidents()
{
   if (!IsLoading)
   {
      IsLoading = true;

      var models = await this.dataService
         .ListResidents();

      var viewModels = models
         .OrderBy(m => m.Name)
         .ThenBy(m => m.Vorname)
         .Select(m => new ResidentViewModel(m.Z_PF, string.Format("{0}, {1}", m.Name, m.Vorname)));

      residents.Clear();

      foreach (var viewModel in viewModels)
         residents.Add(viewModel);

      IsLoading = false;
   }
}
var viewModels = models
    .OrderBy(m => m.Name)
    .ThenBy(m => m.Vorname)
    .Select(m => residentViewModelFactory.CreateInstance(m));