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



我的问题基于微软的InventorySampleApp。

ServiceLocator包含注册Services和ViewModels的方法Configure()。用方法GetService<T>()可以得到它,例如ProductView.cs:

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

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

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

我无法理解ViewModel用来将此类接口放入其构造函数的magiс。所以没有这样的线路:

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

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

服务定位器

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就是该注册表。

现在,每当组件A需要注册表中的实例时,都有两个不同的选项:

  1. 直接向容器请求一个实例,例如ServiceLocator.Current.GetService<ProductDetailsViewModel>()。这被称为服务定位器模式(我建议立即忘记这一点(
  2. 与其直接询问容器,不如通过A(例如public A(ProductDetailsViewModel viewModel)(的构造函数来请求依赖关系

第二种方法可以越来越向上推,直到到达应用程序层次结构的顶部,即所谓的composition root

无论如何,在这两种方式中,容器都使用反射机制。这是一种检索类、方法、属性、构造函数等元数据的方法。每当容器被要求某个类型(例如ProductDetailsViewModel(时,他都会使用反射来获取有关其构造函数的信息
一旦构造函数被解析,它的依赖关系也是已知的(IProductServiceIFilePickerServiceICommonServices(。由于这些依赖关系是在容器中注册的(记住serviceCollection(,因此可以创建实例
这种情况一直持续下去,直到不再有依赖项,容器可以开始实例化和组合所有对象。最后,给出了ProductDetailsViewModel的一个实例。如果构建链中有一个容器未知的依赖项,则实例化失败。

因此,基本上,实例化过程从代码转移到DI容器中。

请注意,GetService方法调用ServiceProvider。GetService。这是一种库方法,可以为您处理事务。在封面之下,它使用反射来检查您请求的类型的构造函数。

例如,当您请求ProductDetailsViewModel时,ServiceLocator可以看到它需要类型为IProductServiceIFilePickerServiceICommonServices的对象。

然后,它查看其服务注册表。例如,线路

serviceCollection.AddSingleton<IProductService, ProductService>();

针对接口IProductService注册具体类型ProductService,因此每当ServiceLocator需要创建IProductService对象时,它都会使用ProductService对象。

这个过程被称为自动布线,在Steven van Deursen的第12章和我的关于依赖注入的书中有更详细的描述。

我无法理解ViewModel用于将此类接口放入其构造函数的magiс

事实上,帮自己一个忙,学习Pure DI,而不是依赖于你觉得不舒服的不透明库。

我以前从未见过这个示例代码库,但从这里发布的示例来看,它看起来充满了代码气味和反模式。

相关内容

  • 没有找到相关文章

最新更新