c# - ViewModel 构造函数如何获取所需的接口?
问题描述
我的问题基于Microsoft 的InventorySampleApp。
ServiceLocator
包含Configure()
注册服务和视图模型的方法。通过方法GetService<T>()
我们可以得到它。例如ProductView.cs
:
ViewModel = ServiceLocator.Current.GetService<ProductDetailsViewModel>();
每个都*ViewModel
包含带有接口的构造函数,例如:
public ProductDetailsViewModel(IProductService productService, IFilePickerService filePickerService, ICommonServices commonServices)
我无法理解ViewModel 用于将此类接口放入其构造函数的魔法。所以没有这样的行:
... = 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
需要来自注册表的实例时,都有两种不同的选择:
- 直接向容器询问实例,例如
ServiceLocator.Current.GetService<ProductDetailsViewModel>()
. 这被称为服务定位器模式(我建议立即忘记这一点)。 - 与其直接询问容器,不如通过
A
(例如public A(ProductDetailsViewModel viewModel)
)的构造函数来请求依赖。
第二种方法可以越来越多地向上推,直到到达应用程序层次结构的顶部 - 即所谓的composition root
.
无论如何,在这两种方式中,容器都使用了Reflection机制。它是一种检索类、方法、属性、构造函数等元数据的方法。每当容器被要求提供某种类型(例如ProductDetailsViewModel
)时,他使用反射来获取有关其构造函数的信息。
一旦构造函数被解析,它的依赖关系也是已知的(IProductService
, IFilePickerService
, ICommonServices
)。由于这些依赖项是在容器中注册的(记住serviceCollection
),因此可以创建实例。
这种情况一直持续下去,直到不再有依赖关系,并且容器可以开始实例化和组合所有对象。最后,有一个实例ProductDetailsViewModel
. 如果构造链中存在容器未知的依赖项,则实例化失败。
所以基本上,实例化过程从你的代码移到了 DI 容器中。
推荐阅读
- azure-devops - VS2022 因错误 TFS30063 失去 DevOps 授权 - 但仅在一段时间后
- python - 为什么我没有定义名称“请求”?
- python - 如果可调整大小,如何在框架上添加按钮和标签
- javascript - 使用 javascript 过滤方法从列表中获取所有项目
- flask - SQLalchemy:许多请求快速接替
- reactjs - 我如何在没有专门导入的情况下加载图像
- .net - 无法安装 Visual Studio Community Edition (IDE) 并且总是遇到 dll not found 异常
- reactjs - 调用多个函数时反应对象状态不持久
- curl - Curl 无法识别网址
- xcode - 删除在 Xcode 中创建的本地应用程序 M1 Mac