如何在C#/ ASP中的单例中使用范围依赖 [英] how to use scoped dependency in a singleton in C# / ASP

查看:83
本文介绍了如何在C#/ ASP中的单例中使用范围依赖的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是来自Java世界的C#/ ASP新手。我已经阅读了这篇文章: https://docs.asp.net/zh_CN/latest/fundamentals/dependency-injection.html#service-lifetimes-and-registration-options 会明智地警告与较小的依赖项注入有关的危险范围。不幸的是,它没有解释如何在C#/ ASP中解决此问题。
在Java中,提供程序的概念

I'm new to C#/ASP coming from a Java world. I've read this article: https://docs.asp.net/en/latest/fundamentals/dependency-injection.html#service-lifetimes-and-registration-options which wisely warns about the dangers associated with injecting a dependency with a smaller scope. Unfortunately it does not explain how to solve this issue in C#/ASP. In Java there's a concept of Provider

interface Provider<T> { T get(); }

这尤其有助于解决范围问题:
某些类型为T的寄存器可以注入Provider 的自动生成的实例而不是T,然后在需要时获取T的实例:自动生成的Provider确保我们获得适合当前范围的实例(无论如何)此范围是:HTTP请求,HTTP会话或其他自定义范围)。内置在ASP.NET核心中的标准DI框架没有这样的东西,但是我认为在C#中应该很容易实现,因为C#泛型不像Java的那样糟糕( https://docs.oracle.com/javase/tutorial/java/generics/erasure.html ) 。因此,我创建了以下类:

which, among other things helps to solve the scoping issue: whenever a binding for some type T is register we can inject an automatically generated instance of Provider<T> instead of T and then get an instance of T whenever it is needed: an automatically generated Provider makes sure that we get an instance appropriate for the current scope (whatever this scope is: HTTP request, HTTP session or other custom scopes). The standard DI framework built into ASP.NET core does not have anything like this, but I thought in C# it should be very easy to implement as C# generics don't suck like java's do (https://docs.oracle.com/javase/tutorial/java/generics/erasure.html). So I've created the following class:

public class Provider<T>: IProvider<T> {
  private readonly IServiceProvider serviceProvider;
  public Provider(IServiceProvider serviceProvider) {
    this.serviceProvider = serviceProvider;
  }
  public T IProvider<T>.Get() {
    return serviceProvider.GetService<T>();
  }
}

,我打算按以下方式使用它:

and I attemtped to use it the following way:

public class SingletonService : ISingletonService {
  private readonly IProvider<IScopedService> scopedServiceProvider;

  public SingletonService(IProvider<IScopedService> scopedServiceProvider) {
    this.scopedServiceProvider = scopedServiceProvider;
  }

  public string PerformMyTask() {
    var scopedDependency = scopedServiceProvider.Get();
    // do something with scopedDependency to verify we get instances
    // appropriate for the current scope
  }
}

并在我的Startup类中:

and in my Startup class:

public void ConfigureServices(IServiceCollection services) {
  services.AddSingleton<ISingletonService, SingletonService>();
  services.AddScoped<IScopedService, ScopedService>();
  services.AddTransient<IProvider<IScopedService>, Provider<IScopedService>>();
  // other bindings here
}

不幸的是,这种方式不起作用我打算因为IServiceProvider实例似乎也适用于当前的HTTP请求,并且在处理不同的请求期间,我从提供者那里获得了ScopedDependency的完全相同的实例:(

Unfortunately this does not work the way I intended as IServiceProvider instance seems to be also scoped to the current HTTP request and I get exactly the same instance of ScopedDependency from my provider during processing of different requests :(

Any提示如何解决此问题?
是否可能有比ServiceProvider更高的对象,大致绑定到创建请求范围对象(或ServiceProvider本身)实例的应用程序生命周期(而不是当前请求)我可以注入我的Provider对象而不是ServiceProvider吗?例如,在Java中,如果我使用google Guice作为DI框架,则有一个Injector对象,通常在应用程序启动时创建,该对象包含所有类型绑定并具有方法

Any hints how can I solve this problem? Is there any "higher level" object than ServiceProvider maybe, bound roughly to application lifecycle (not to the current request) that creates instances of request scoped objects (or of ServiceProvider itself) that I can inject into my Provider objects instead of ServiceProvider? For example in Java if I use google Guice as a DI framework there is an Injector object, usually created at the startup of an application which holds all the type bindings and has a method

<T> T getInstance(Class<T> type);

检查当前范围是什么并返回相应的实例。

which checks what is the current scope and returns a corresponding instance.

编辑:

我认为一个可能做到这一点的方法将是每次在Proivder< Get> .Get()方法中获取对ServiceProvider实例的新引用,而不是注入构造函数并将其存储为实例var。这样,我的组件仍不会受到对框架特定的IServiceProvider的引用的污染,因为它们将在它们通过抽象IProvider 接口访问的Provider 的实现中对它们隐藏。但是,我在网上找不到是否可以从我的Provider类中获得这样的参考以及如何做到这一点。对此方向的任何指点将不胜感激:)

I think that one possible way to do it would be to get a new reference to instance of ServiceProvider each time in the Proivder<T>.Get() method instead of injecting in the constructor and storing as an instance var. This way my components would still not be polluted with a reference to the framework specific IServiceProvider as it would be hidden from them in the implementation of Provider<T> that they access via the abstract IProvider<T> interface. I can't however find on the web if it's possible to get such a reference from my Provider class and how to do this. Any pointers in this direction would be appreciated :)

谢谢!

推荐答案

确定,找到它:

public class Provider<T> : IProvider<T> {
    IHttpContextAccessor contextAccessor;
    public Provider(IHttpContextAccessor contextAccessor) {
        this.contextAccessor = contextAccessor;
    }
    T IProvider<T>.Get() {
        return contextAccessor.HttpContext.RequestServices.GetService<T>();
    }
}

在启动中:

    public void ConfigureServices(IServiceCollection services) {
        services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        services.AddSingleton<ISingletonService, SingletonService>();
        services.AddScoped<IScopedService, ScopedService>();
        services.AddTransient<IProvider<IScopedService>, Provider<IScopedService>>();
        // other bindings
    }

:)

请参见 https://github.com/aspnet/Hosting/ issue / 793 有关使用和注册HttpContextAccessor的更多详细信息

see https://github.com/aspnet/Hosting/issues/793 for more details about using and registering HttpContextAccessor

这篇关于如何在C#/ ASP中的单例中使用范围依赖的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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