3

I need some help to understand how to instantiate ViewModels without having all of them as parameters in the MainViewModel Class constructor.

Could any of you help me to get my head around and get rid of so many parameters in the constructor. I've read about FactoryPatterns but I don't understand how to implement it or maybe that is not the solution?. Anyway here's the code.

Thank you. Pls help me this is driving me nuts!

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

}

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

    
}
Laycoonz
  • 163
  • 1
  • 4
  • 18
  • 1
    Note that a class having only three dependencies is nothing to worry about. Especially when using composition this is very common. You can easily get 10+ dependencies depending on your design and complexity. That is what you primarily use a DI framework for; to automatically create instances with complex construction in a centralized manner. – BionicCode Jun 25 '20 at 06:53
  • 2
    Using the Abstract Factory pattern wouldn't neccessarily reduce the parameter count of your constructors as you'd still have to inject the factories into the depending class. And creating the dependencies inside the class using e.g., a single a factory (or even worse the service container) is considered bad practice or even anti-pattern as it hides dependencies or introduces tight coupling. You typically use factories to abstract away dynamic class instantiation. – BionicCode Jun 25 '20 at 06:58
  • 1
    You can explicitly resolve from a di container in code. I would prefer that to property injection. If there is some complexity to instantiation you could use lazy singleton instantiation as a way to wrap a factory method. Factory methods should be way down on your list of candidates if you're using di though. – Andy Jun 25 '20 at 12:46
  • Thanks that clarifies some doubts about DI. I will try what Andy has suggested but it maybe complicated for me as I'm in the learning path of design patterns and all you've said seems way out of my current knowledge – Laycoonz Jun 25 '20 at 14:38

2 Answers2

1

You're using constructor injection, which is only one of the ways to do DI. Another method is property injection, where you typically do something like this:

public class ClassToBeInjected
{
    [Inject]
    public SomeDataType PropertyToBeInjected {get; set;}

   ... etc ...
}

So long as ClassToBeInjected is being created via the DI framework, any properties tagged with [Inject] will also be injected automatically. Furthermore, any properties of SomeDataType that are tagged with the [Inject] attribute will also be injected, and so on down the heirarchy.

The exact mechanism of how this is achieved will depend on your DI framework. I've used the [Inject] attribute here, which is what Ninject uses, but each framework will have its own way of doing it.

Mark Feldman
  • 15,731
  • 3
  • 31
  • 58
  • Thank you Sir for your suggestion, by all accounts it looks like a good idea – Laycoonz Jun 25 '20 at 14:33
  • 3
    Do note that OP is using .NET Core's default DI Container (MS.DI) and this container does not support property injection, which means that something like an `[Inject]` attribute can't (easily) be added to it. Also note that Property Injection comes with a [myriad of downsides](https://stackoverflow.com/a/39853478/264697), which should not be ignored. – Steven Jun 25 '20 at 16:18
  • @Laycoonz Steven is eminently more qualified to talk on this subject than I, it looks like we both have some reading to do. – Mark Feldman Jun 29 '20 at 05:45
1

You don't need to pass all your dependencies to MainViewModel, now it's easy because you only have three, what if they were 20? I think the best thing to do here is to inject the dependency container and get all your view models from there.

public class MainViewModel : ViewModelBase
{
    private ServiceProvider _serviceProvider;

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

    private void SomeMethod()
    {
        CustomerViewModel viewModel = _serviceProvider.GetRequiredService<CustomerViewModel>();
    }
}
Dimitris Maragkos
  • 8,932
  • 2
  • 8
  • 26
Enmanuel G
  • 156
  • 3
  • 13