ViewModel构造函数如何获取所需的接口? [英] How does the ViewModel constructor get the required interfaces?

查看:81
本文介绍了ViewModel构造函数如何获取所需的接口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的问题基于Microsoft的 InventorySampleApp

My question based on InventorySampleApp by Microsoft.

ServiceLocator 包含注册服务和ViewModel的方法 Configure()。使用方法 GetService< T>(),我们可以获得它。例如, ProductView.cs

The ServiceLocator contains method Configure() that register Services and ViewModels. With method GetService<T>() we can get it. For example, ProductView.cs:

ViewModel = ServiceLocator.Current.GetService<ProductDetailsViewModel>();

每个 * ViewModel 包含带接口的构造函数,例如:

Each *ViewModel contains constructor with interface, for example:

public ProductDetailsViewModel(IProductService productService, IFilePickerService filePickerService, ICommonServices commonServices)

我不明白ViewModel用于将此类接口引入其构造函数的magiс。因此没有这样的行:

I can't understand the magiс that ViewModel uses to get such interfaces into its constructor. So there are no lines like this:

... = new ProductDetailsViewModel(productService, filePickerService, commonServices)

ViewModel构造函数如何获取所需的接口?

How does the ViewModel constructor get the required interfaces?

ServiceLocator

public class ServiceLocator : IDisposable
{
    static private readonly ConcurrentDictionary<int, ServiceLocator> _serviceLocators = new ConcurrentDictionary<int, ServiceLocator>();

    static private ServiceProvider _rootServiceProvider = null;

    static public void Configure(IServiceCollection serviceCollection)
    {
        serviceCollection.AddSingleton<ISettingsService, SettingsService>();
        serviceCollection.AddSingleton<IDataServiceFactory, DataServiceFactory>();
        serviceCollection.AddSingleton<ILookupTables, LookupTables>();
        serviceCollection.AddSingleton<ICustomerService, CustomerService>();
        serviceCollection.AddSingleton<IOrderService, OrderService>();
        serviceCollection.AddSingleton<IOrderItemService, OrderItemService>();
        serviceCollection.AddSingleton<IProductService, ProductService>();

        serviceCollection.AddSingleton<IMessageService, MessageService>();
        serviceCollection.AddSingleton<ILogService, LogService>();
        serviceCollection.AddSingleton<IDialogService, DialogService>();
        serviceCollection.AddSingleton<IFilePickerService, FilePickerService>();
        serviceCollection.AddSingleton<ILoginService, LoginService>();

        serviceCollection.AddScoped<IContextService, ContextService>();
        serviceCollection.AddScoped<INavigationService, NavigationService>();
        serviceCollection.AddScoped<ICommonServices, CommonServices>();

        serviceCollection.AddTransient<LoginViewModel>();

        serviceCollection.AddTransient<ShellViewModel>();
        serviceCollection.AddTransient<MainShellViewModel>();

        serviceCollection.AddTransient<DashboardViewModel>();

        serviceCollection.AddTransient<CustomersViewModel>();
        serviceCollection.AddTransient<CustomerDetailsViewModel>();

        serviceCollection.AddTransient<OrdersViewModel>();
        serviceCollection.AddTransient<OrderDetailsViewModel>();
        serviceCollection.AddTransient<OrderDetailsWithItemsViewModel>();

        serviceCollection.AddTransient<OrderItemsViewModel>();
        serviceCollection.AddTransient<OrderItemDetailsViewModel>();

        serviceCollection.AddTransient<ProductsViewModel>();
        serviceCollection.AddTransient<ProductDetailsViewModel>();

        serviceCollection.AddTransient<AppLogsViewModel>();

        serviceCollection.AddTransient<SettingsViewModel>();
        serviceCollection.AddTransient<ValidateConnectionViewModel>();
        serviceCollection.AddTransient<CreateDatabaseViewModel>();

        _rootServiceProvider = serviceCollection.BuildServiceProvider();
    }

    static public ServiceLocator Current
    {
        get
        {
            int currentViewId = ApplicationView.GetForCurrentView().Id;
            return _serviceLocators.GetOrAdd(currentViewId, key => new ServiceLocator());
        }
    }

    static public void DisposeCurrent()
    {
        int currentViewId = ApplicationView.GetForCurrentView().Id;
        if (_serviceLocators.TryRemove(currentViewId, out ServiceLocator current))
        {
            current.Dispose();
        }
    }

    private IServiceScope _serviceScope = null;

    private ServiceLocator()
    {
        _serviceScope = _rootServiceProvider.CreateScope();
    }

    public T GetService<T>()
    {
        return GetService<T>(true);
    }

    public T GetService<T>(bool isRequired)
    {
        if (isRequired)
        {
            return _serviceScope.ServiceProvider.GetRequiredService<T>();
        }
        return _serviceScope.ServiceProvider.GetService<T>();
    }

    #region Dispose
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_serviceScope != null)
            {
                _serviceScope.Dispose();
            }
        }
    }
    #endregion


推荐答案

使用依赖注入时,对象的实例被移动到名为依赖注入(DI)容器控制反转(IoC)容器。该组件具有某种注册表,其中包含可以实例化的所有已知服务。在您的示例中, serviceCollection 是该注册表。

When using dependency injection, the instantiation of objects is moved to a component called Dependency Injection (DI) Container or Inverse of Control (IoC) Container. This component has some kind of registry that contains all known services that can be instantiated. In your example, the serviceCollection is that registry.

现在,只要组件 A 需要注册表中的一个实例,有两种不同的选择:

Now, whenever a component A needs an instance from the registry, there are two different options:


  1. 直接向容器询问实例, e。 G。 ServiceLocator.Current.GetService< ProductDetailsViewModel>()。这被称为服务定位器模式(我建议您立即将其忘记)。

  2. 与其直接询问容器,不如直接通过容器的构造函数请求依赖项 A (例如 public A(ProductDetailsViewModel viewModel))。

  1. Directly ask the container for an instance, e. g. ServiceLocator.Current.GetService<ProductDetailsViewModel>(). This is known as the Service Locator Pattern (I'd recommend to forget this immediately).
  2. Rather than asking the container directly, request the dependency via constructor of A (e. g. public A(ProductDetailsViewModel viewModel)).

第二种方法可以越来越多地向上推动,直到达到应用程序层次结构的顶部为止-所谓的组合根

The second approach can be pushed more and more upwards until the top of the application hierarchy is reached - the so called composition root.

无论如何,容器都以两种方式使用反射。这是一种检索类,方法,属性,构造函数等元数据的方法。每当要求容器提供某种类型(例如 ProductDetailsViewModel )时,他都会使用反射来获取有关其构造函数的信息。

构造函数解析后,其依赖关系也众所周知( IProductService IFilePickerService ICommonServices )。由于这些依赖项已在容器中注册(请记住 serviceCollection ),因此可以创建实例。

一直进行下去,直到没有其他依赖项为止并且容器可以开始实例化和组成所有对象。最后,有一个 ProductDetailsViewModel 的实例。
如果构造链中有一个容器不知道的依赖关系,则实例化将失败。

Anyways, in both ways, the container uses the mechanism of Reflection. It is a way of retrieving metadata of classes, methods, properties, constructors, etc. Whenever the container is asked for a certain type (e. g. ProductDetailsViewModel), he uses reflection to get information about its constructor.
Once the constructor is resolved, its dependencies are known as well (IProductService, IFilePickerService, ICommonServices). Since these dependencies are registered within the container (remember the serviceCollection), instances can be created.
This goes on and on until there are no more dependencies and the container can start instantiating and composing all the objects. Finally, there is an instance of ProductDetailsViewModel. If there is one dependency within the construction chain that is unknown to the container, the instantiation fails.

因此,基本上,实例化的过程从您的代码放入DI容器中。

So basically, the process of instantiation is moved away from your code into the DI container.

这篇关于ViewModel构造函数如何获取所需的接口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆