依赖注入 WPF MVVM 导航 .NET Core

3

我需要一些帮助来理解如何实例化ViewModels而不将它们全部作为MainViewModel类构造函数的参数。

你们中是否有人能够帮助我理清思路并摆脱在构造函数中添加许多参数的情况。我已经阅读了关于工厂模式的文章,但我不知道如何实现它,或者也许那不是解决方案?无论如何,以下是代码。

谢谢。请帮帮我,这让我发疯了!

app.xaml.cs

    private readonly ServiceProvider _serviceProvider;
    public App()
    {
        ServiceCollection services = new ServiceCollection();
        ConfigureServices(services);
        _serviceProvider = services.BuildServiceProvider();
    }

    private void ConfigureServices(ServiceCollection services)
    {
        services.AddSingleton<MainWindow>();
        // Services
        services.AddSingleton<ICustomerService, CustomerService>();
        // ViewModels
        services.AddScoped<MainViewModel>();
        services.AddScoped<CustomerViewModel>();
        services.AddScoped<CustomerAddViewModel>();
        services.AddScoped<CustomerEditViewModel>();
        services.AddScoped<ServiceViewModel>();
    }

    private void OnStartup(object sender, StartupEventArgs e)
    {
        var mainWindow = _serviceProvider.GetService<MainWindow>();
        mainWindow.DataContext = _serviceProvider.GetService<MainViewModel>();
        mainWindow.Show();
    }

MainViewMode.cs

public class MainViewModel : ViewModelBase
{
    private CustomerViewModel _customerViewModel;
    private CustomerAddViewModel _customerAddViewModel;
    private CustomerEditViewModel _customerEditViewModel;

    private ViewModelBase _selectedViewModel;
    public ViewModelBase SelectedViewModel
    {
        get => _selectedViewModel;
        set
        {
            _selectedViewModel = value;
            NotifyPropertyChanged();
        }
    }

    public RelayCommand CustCommand { get; set; }
    public RelayCommand ServCommand { get; set; }

    **public MainViewModel(
        CustomerViewModel customerViewModel,
        CustomerAddViewModel customerAddViewModel,
        CustomerEditViewModel customerEditViewModel)
    {
        _customerViewModel = customerViewModel;
        _customerAddViewModel = customerAddViewModel;
        _customerEditViewModel = customerEditViewModel;
        CustCommand = new RelayCommand(OpenCustomer);
    }**

    private void OpenCustomer()
    {
        SelectedViewModel = _customerViewModel;
    }

}

客户视图模型

    public class CustomerViewModel : ViewModelBase
    {
    private ICustomerService _repo;
    private ObservableCollection<Customer> _customers;
    public ObservableCollection<Customer> Customers
    {
        get => _customers;
        set
        {
            _customers = value;
            NotifyPropertyChanged();
        }
    }

    public CustomerViewModel(ICustomerService repo)
    {
        _repo = repo;
    }

    public async void LoadCustomers()
    {
        List<Customer> customers = await _repo.GetCustomers();
        Customers = new ObservableCollection<Customer>(customers);
    }

    
}

1
请注意,一个只有三个依赖项的类并不需要担心。特别是在使用组合时,这是非常常见的。根据您的设计和复杂性,您可以轻松获得10多个依赖项。这就是您主要使用DI框架的原因;以集中的方式自动创建具有复杂构造的实例。 - BionicCode
2
使用抽象工厂模式并不一定会减少构造函数的参数数量,因为您仍然需要将工厂注入到依赖类中。在类内部使用单个工厂(甚至更糟的是服务容器)创建依赖关系被认为是不良实践或反模式,因为它隐藏了依赖关系或引入了紧密耦合。通常使用工厂来抽象动态类实例化。 - BionicCode
1
你可以在代码中明确地从 DI 容器中解析依赖项。我更喜欢这种方式而不是属性注入。如果实例化存在一些复杂性,你可以使用延迟单例实例化作为包装工厂方法的一种方式。但是,如果你正在使用 DI,则工厂方法应该是候选列表中的最后选择。 - Andy
谢谢,这解决了我对 DI 的一些疑惑。我会尝试 Andy 建议的方法,但对我来说可能有些复杂,因为我正在学习设计模式,而你所说的一切似乎超出了我的当前知识水平。 - Laycoonz
2个回答

1
你不需要将所有的依赖项传递到MainViewModel中,现在很容易,因为你只有三个,如果有20个呢?我认为最好的做法是注入依赖容器并从那里获取所有的视图模型。
public class MainViewModel : ViewModelBase
{
    private ServiceProvider _serviceProvider;

    public MainViewModel(IServiceProvider provider)
    {
        _serviceProvider = provider;
    }

    private void SomeMethod()
    {
        CustomerViewModel viewModel = _serviceProvider.GetRequiredService<CustomerViewModel>();
    }
}

1
您正在使用构造函数注入,这只是进行依赖注入的方式之一。另一种方法是属性注入,在其中通常会执行以下操作:
public class ClassToBeInjected
{
    [Inject]
    public SomeDataType PropertyToBeInjected {get; set;}

   ... etc ...
}

只要ClassToBeInjected是通过DI框架创建的,任何标记了[Inject]的属性也将自动注入。此外,任何标记有[Inject]属性的SomeDataType属性也将被注入,以此类推到下一级。
具体实现机制取决于您使用的DI框架。我在这里使用了[Inject]属性,这是Ninject使用的方式,但每个框架都有自己的方法。

感谢您的建议,从各方面来看,这似乎是一个好主意。 - Laycoonz
3
请注意,OP正在使用.NET Core的默认DI容器(MS.DI),而该容器不支持属性注入,这意味着像[Inject]这样的属性不能(轻松地)添加到其中。另请注意,属性注入带有很多缺点,不应被忽视。 - Steven
@Laycoonz,Steven在这个主题上比我更有资格发言,看起来我们都需要阅读一些资料。 - Mark Feldman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接