【问题标题】:Can Prism use .NET Core's built in Dependency Injection?Prism 可以使用 .NET Core 的内置依赖注入吗?
【发布时间】:2021-01-01 15:21:38
【问题描述】:

我想使用 .NET Core 3.1 启动一个 WPF 应用程序
Prism 可以使用 .Net Core 的内置 DI (IServiceCollection) 还是我必须使用 Unity 之类的东西?
如果 Prism 不能使用内置 DI,它们可以并存吗?

【问题讨论】:

    标签: .net-core prism


    【解决方案1】:

    Prism 能否利用 .Net Core 的内置 DI

    据我所知,您不能真正用 ASP.NET Core 内置的替代 Prism 的 DryIot。主要是 DryIot 比 ServiceCollection API 功能更丰富。有这个开源package 我发现它有一个使用ServiceCollectionIContainerExtension 实现,但根据开发人员自己的话,这更像是一个概念证明而不是可靠的解决方案。

    如果 Prism 不能使用内置 DI,它们可以并排存在吗?

    是的,他们可以。需要注意的是 - 您不能简单地在 ServiceCollection 中注册服务并期望能够直接在您的应用程序、模块和视图模型中注入该服务。这将失败,因为这些文件由 Prism 框架管理,因此注入仅适用于您使用 IContainerRegistry 接口注册的服务。

    好处

    你为什么要这样做?作为内置物联网容器,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>()
    }
    

    所以现在我们需要将它注入到桌面项目中。我建议两种方法:

    1. 简单
    2. 看似无懈可击

    简单

    这种方法需要非常简单的启动配置。需要注意的是,您将无法直接在构造函数中注入服务,而是通过IServiceProvider 接口。

    1. 参考Microsoft.Extensions.DependencyInjection

    2. 在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);
       }
      
    3. 在需要 IUserService 的地方注入 IServiceProvider。在本例中,我将使用 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;
           }
       }
      

    就是这样,您现在可以在任何可以通过 Prism 注入访问 IServiceProvider 的地方使用您的 ServiceCollection 注册依赖项。这是我推荐的方法,因为我们只是将 .Net 容器包装在 Prism 中。

    无形

    这是它变得更有趣的地方。完全免责声明 - 您可能会在使用这种方法时遇到问题。除了最基本的用例之外,我还没有测试过这个。此方法提供的唯一优点是您将能够直接在构造函数中注入服务,而不必通过IServiceProvider

    本质上,此方法只是循环通过ServiceCollection 并直接在 Prism 容器中注册所有服务。如果我们看一下ServiceCollection 的实现,它只是一个ServiceDescriptors 的列表。经过进一步检查,我们发现ServiceDescriptior 包含一堆构造函数。我们将忽略这些并专注于属性:

    1. ServiceType - 注入时将使用的类型
    2. ImplementationType - 要注入的实现类型
    3. ImplementationInstance - 实现类型的实例
    4. ImplementationFactory - 返回实现类型实例的工厂委托
    5. LifeTime - Singleton、Scoped 或 Transient 类型

    现在让我们检查IContainerRegistry 接口。我们会看到有很多Register 的重载接受类型、对象和委托。

    利用这些知识,我们现在可以创建一个从 ServiceDescriptor 到注册 IContainerRegistry 的适配器。下面的实现将只关注瞬态服务,但服务生命周期之间的区别只是我们调用的注册表方法 - Register 用于瞬态,RegisterSingleton 用于单例。

    1. 使用接受IContainerRegistryServiceDescriptor 参数的静态方法创建和适配器类:

       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 提供HttpAuthorizationService 实现而不是DesktopAuthorizationService

    本质上,我们使用 DryIot 兼容工厂(接受 DryIot 容器的实例)包装原始工厂方法,该工厂可以为原始工厂提供 IServiceProvider 实例。

    1. 参考Microsoft.Extensions.DependencyInjection

    2. 在App中调用你的服务注册方法:

       protected override void RegisterTypes(IContainerRegistry container)
       {
           var services = new ServiceCollection().AddServices()
      
           foreach (var service in services)
           {
               Adapter.Register(container, service);
           }
       }
      
    3. 直接在模块构造函数中注入IUserService

       public class Module : IModule
       {
           private readonly IUserService userService;
      
           public Module(IUserService userService)
           {
               this.userService = userService;
           }
       }
      

    最后的想法

    再次,我推荐简单的方法。简单意味着更低的学习曲线和更少的出错空间。相比之下,不便之处很小。

    另一个公平的警告 - 这不是生产就绪代码。尤其是无缝的方法。我还没有对这个实现进行“实战测试”,但它可能会为你指明正确的方向。

    如果有人有反馈/意见,我很乐意阅读:)

    【讨论】:

      【解决方案2】:

      Prism 能否利用 .Net Core 的内置 DI

      简答,

      这是@brianlagunas(棱镜的创造者)的a comment

      正如我所提到的,我们不能像在 netstandard 1.0 中那样使用 IServiceProvider。 ServiceProvider 和 IServiceCollection 在 netstandard 2.0 中。此外,Prism 需要在 IServiceCollection 实现中限制的许多功能。例如命名实例和注册,以及可变容器。

      这里是@dansiegel 的a comment

      我花了很多时间讨论这个问题,最终我们不能直接依赖 IServiceProvider 和 IServiceCollection,原因有很多,超出了它们是否可用。

      这里也是another comment @brianlagunas

      【讨论】:

      • 这并不完全正确。您无法替换内置容器,但您可以通过 IServiceCollection 自行配置,或者您可以在 Prism 的容器中注册 IServiceProvider 并并排使用它们。
      【解决方案3】:

      我必须使用 Unity 之类的东西吗?

      ServiceCollection “有点像 Unity”。而且,是的,您可以将它与 prism 一起使用:

      1. 创建一个重定向到ServiceCollectionIContainerExtension 实现
      2. PrismApplicationBase 派生并从CreateContainerExtension 返回您的容器扩展

      【讨论】:

        猜你喜欢
        • 2020-12-20
        • 2016-05-22
        • 1970-01-01
        • 2022-01-24
        • 2021-10-23
        • 1970-01-01
        • 2021-02-02
        • 1970-01-01
        • 2019-07-08
        相关资源
        最近更新 更多