Prism可以利用.Net Core内置的DI吗
据我所知,您无法真正用 ASP.NET Core 内置工具替换 Prism 的 DryIot。主要是 DryIot 比 ServiceCollection API 功能更齐全。有这个开源的package https://github.com/dansiegel/Prism.Container.Extensions/tree/master/src/Prism.Microsoft.DependencyInjection.Extensions我发现有一个IContainerExtension
实施使用ServiceCollection
,但根据开发人员自己的说法,这更多的是概念验证而不是可靠的解决方案。
如果Prism不能使用内置DI,它们可以并存吗?
是的他们可以。需要注意的是 - 您不能简单地在ServiceCollection
并期望能够将该服务直接注入您的应用程序、模块和视图模型中。这将失败,因为这些文件由 Prism 框架管理,因此注入仅适用于您使用IContainerRegistry
界面。
Benefits
你为什么要这么做?作为内置的物联网容器ServiceCollection
API 是众所周知的,因此对于 .Net 开发人员来说会更简单。此外,您可以构建非 WPF 项目以使用默认容器注册服务,从而使它们与您的桌面项目完全解耦。这对于领域驱动设计等更复杂的架构非常有用。
让我们考虑以下项目结构:
solution
-- Desktop // Prism WPF application, containing only views and models
-- Application // Class library, containing operational logic.
假设作为应用程序项目的一部分,您需要一个IUserService
它保存有关当前用户的信息,当用户在桌面应用程序中进行身份验证时,必须将这些信息填充到内存中。注册方法如下所示:
public IServiceCollection AddServices(this IServiceCollection services)
{
services.AddSingleton<IUserService, UserService>()
}
所以现在我们需要将它注入到桌面项目中。我建议两种方法:
- Simple
- Seemless
Simple
这种方法需要非常简单的启动配置。需要注意的是,您将无法直接在构造函数中注入服务,而是通过IServiceProvider
界面。
-
参考Microsoft.Extensions.DependencyInjection
-
在App中调用您的服务注册方法:
protected override void RegisterTypes(IContainerRegistry container)
{
// Build service provider using the ServiceCollection API
var provider = new ServiceCollection()
.AddServices()
.BuildServiceProvider();
// Register IServiceProvider within DryIot and provide
// a factory method to retrieve the service instance
container.Register<IServiceProvider>(() => provider);
}
-
Inject IServiceProvider
在你需要的地方IUserService
。在本例中,我将使用 Prism 模块:
public class Module : IModule
{
private readonly IUserService userService;
public Module(IServiceProvider serviceProvider)
{
this.userService = serviceProvider.GetService<IUserService>();
}
...
private void Authenticate()
{
this.userService.IsAuthenticated = true;
}
}
就是这样,您现在可以在任何可以访问的地方使用 ServiceCollection 注册的依赖项IServiceProvider
通过棱镜注入。这是我推荐的方法,因为我们只是将 .Net 容器包装在 Prism 中。
Seemless
这就是事情变得更有趣的地方。完整免责声明 - 使用此方法可能会遇到问题。除了最基本的用例之外,我还没有对此进行测试。此方法提供的唯一优点是您将能够直接在构造函数中注入服务,而不必通过IServiceProvider
.
从本质上讲,该方法只是循环ServiceCollection
并直接在 Prism 容器中注册所有服务。如果我们看一下实施ServiceCollection
- 这只是一个列表ServiceDescriptors
。经过进一步检查,我们发现ServiceDescriptior
包含一堆构造函数。我们将忽略这些并专注于属性:
-
ServiceType
- 注入时将使用的类型
-
ImplementationType
- 要注入的实现类型
-
ImplementationInstance
- 实现类型的实例
-
ImplementationFactory
- 返回实现类型实例的工厂委托
-
LifeTime
- Singleton、Scoped 或 Transient 类型
现在让我们检查一下IContainerRegistry
界面。我们会看到有很多重载Register
接受类型、对象和委托。
利用这些知识,我们现在可以创建一个适配器ServiceDescriptor
到注册IContainerRegistry
。下面的实现仅关注瞬态服务,但服务生命周期之间的区别只是我们调用哪种注册表方法 -Register
对于瞬态和RegisterSingleton
对于单身人士来说。
-
使用接受的静态方法创建适配器类IContainerRegistry
and ServiceDescriptor
论点:
public static void Register(IContainerRegistry container, ServiceDescriptor service)
{
// In case an implementation instance is provided we simply need to register it
if (service.ImplementationInstance != null)
{
containter.Register(service.ServiceType, service.ImplementationInstance);
}
// In case a factory is provided we have a bit more work.
// We need to modify it in order for it to be usable by the DryIot container
else if (service.ImlementationFactory != null)
{
var factory = service.ImplementationFactory;
var dryIotFactory = dryIotProvider =>
{
var provider = dryIotProvider.Resolve<IServiceProvider>();
return factory(provider);
};
container.Register(service.ServiceType, dryIotFactory);
}
// If no implementation or factory is provided then we simply register the
// types and let the container create the implementation instance on its own.
else
{
container.Register(service.ServiceType, service.ImplementationType);
}
}
这里最棘手的部分是工厂。为了更好地理解服务注册中的工厂,有时您可能需要访问其他服务才能提供正确的实现实例。例如如果IHttpClient
已注册您需要提供IAuthorizationSerice
with HttpAuthorizationService
实施而不是DesktopAuthorizationService
.
本质上,我们用一个与 DryIot 兼容的工厂(接受 DryIot 容器的实例)包装原始工厂方法,该工厂可以为原始工厂提供IServiceProvider
实例。
-
参考Microsoft.Extensions.DependencyInjection
-
在App中调用您的服务注册方法:
protected override void RegisterTypes(IContainerRegistry container)
{
var services = new ServiceCollection().AddServices()
foreach (var service in services)
{
Adapter.Register(container, service);
}
}
-
Inject IUserService
直接在模块构造函数中:
public class Module : IModule
{
private readonly IUserService userService;
public Module(IUserService userService)
{
this.userService = userService;
}
}
最后的想法
我再次推荐简单的方法。简单意味着较低的学习曲线和较小的错误空间。相比之下,带来的不便就很小了。
另一个公平的警告 - 这不是生产就绪的代码。尤其是无痕法。我还没有对这个实现进行“战斗测试”,但它可能会为您指明正确的方向。
如果有人有反馈/意见,我很乐意阅读:)