简体   繁体   English

无法从单例yyy中使用作用域服务xxx

[英]Cannot consume scoped service xxx from singleton yyy

I followed a blog post for 'Building Your First Web API with ASP.NET Core and Visual Studio Code'. 我关注了一篇博客文章“使用ASP.NET Core和Visual Studio Code构建您的第一个Web API”。

http://www.codingflow.net/building-your-first-web-api-with-asp-net-core-and-visual-studio-code/ http://www.codingflow.net/building-your-first-web-api-with-asp-net-core-and-visual-studio-code/

In this scenario, data are not saved in database but rather in memory like this: 在这种情况下,数据不会保存在数据库中,而是这样保存:

services.AddDbContext<TodoContext>(options => options.UseInMemoryDatabase());
services.AddSingleton<ITodoRepository, TodoRepository>();

You will notice: 您会注意到:

(1) UseInMemoryDatabase on DbContext (1) UseInMemoryDatabase上的DbContext

(2) AddSingleton on TodoRepository (2)在AddSingleton上添加TodoRepository

This works pretty well. 效果很好。 Now I updated the code to save data inside a real database . 现在,我更新了代码以将数据保存在真实数据库中 So main changes are: 所以主要的变化是:

services.AddDbContext<TodoContext> (options => options.UseSqlite("Data Source=blogging.db"));            
services.AddSingleton<ITodoRepository, TodoRepository>();

I would like to notify that I had to migrate AspNetCore from 1.0 to 2.2. 我想通知我,我不得不将AspNetCore从1.0迁移到2.2。

Now at runtime, when targetting the controller, I got the error: Cannot consume scoped service 'Models.TodoContext' from singleton 'Models.ITodoRepository'. 现在在运行时,以控制器为目标时,出现错误: 无法使用单例“ Models.ITodoRepository”中的作用域服务“ Models.TodoContext”。

I understand that in this situation: 我了解在这种情况下:

  • My TodoContext is a Scoped object: the same within a request, but different across different requests. 我的TodoContext是一个Scoped对象:请求中的对象相同,但不同请求中的对象不同。

  • My TodoRepository is a Singleton object: the same for every object and every request. 我的TodoRepository是一个Singleton对象:每个对象和每个请求都相同。

So I finally changed AddSingleton to AddScoped which works pretty fine: 因此,我终于将AddSingleton更改为AddScoped ,它的效果很好:

services.AddDbContext<TodoContext> (options => options.UseSqlite("Data Source=blogging.db"));            
services.AddScoped<ITodoRepository, TodoRepository>();

My question is : to know whether of not this is an acceptable approach ? 我的问题是 :要知道这是否是可以接受的方法?

PS: I know there are other questions on this issue on SO but I didn't read clear response. PS:我知道关于SO的这个问题还有其他问题,但是我没有得到明确的答复。

For your question, yes it is the right way to do the things. 对于您的问题,是的,这是正确的做法。

I wanted to summarize the answer in a better way, but then I did some research on this topic and I found an article that deals with questions like you asked. 我想以更好的方式总结答案,但是后来我对该主题进行了一些研究,发现了一篇涉及您所问问题的文章

I would suggest having a look at the article since it has "Best Practices" sections on how to approach certain problems. 我建议您看一下这篇文章,因为该文章包含有关如何解决某些问题的“最佳实践”部分。

ASP.NET core built in dependency injection container is protecting you against a dependency injection anti pattern called "captive dependencies" (you can read more about it and dependency injection in general here ). 内置于依赖项注入容器中的ASP.NET核心可保护您免受称为“专属依赖项”的依赖项注入反模式的影响(您可以在此处阅读有关它和一般性的依赖项的更多信息)。

The basic idea is that a class having a certain lifetime can only depend on objects having a lifetime equal to or longer than its own lifetime. 基本思想是,具有一定寿命的类只能依赖具有等于或大于其自身寿命的寿命的对象。 This is because when you do dependency injection you provide a class with all its dependencies (usually via constructor injection) and that class save a reference to the dependency, so that it will be able to use it later when it needs. 这是因为当您进行依赖注入时,您为类提供了其所有依赖(通常通过构造函数注入),并且该类保存了对依赖的引用,以便以后可以在需要时使用它。

Because the class you are designing saves a reference to the injected object, then the injected object is kept alive (at least) for as long as your class' instance it's alive. 由于您要设计的类保存对注入对象的引用,因此,只要您的类实例处于活动状态,注入对象就至少(至少)保持活动状态。 Maybe an example could help you to understand. 也许一个例子可以帮助您理解。

public interface IFoo {}

public class Foo: IFoo {}

public class Bar 
{
  private readonly IFoo foo;

  public Bar(IFoo foo)
  {
    this.foo = foo ?? throw new ArgumentNullException(nameof(foo));
  }
}

var foo = new Foo();
var bar = new Bar(foo); // the object referenced by foo variable is alive as long as the Bar instance is alive, because a reference to it is saved inside the private field of Bar instance

This kind of scenario could put you in trouble if the intended lifetime of Foo instances is shorter than the intended lifetime of Bar instances. 如果Foo实例的预期生存期短于Bar实例的预期生存期,这种情况可能会给您带来麻烦。

For instance, imagine of being in the context of a web application and suppose that the class Foo is not thread safe, so that accessing an instance of it concurrently from different threads could lead to the corruption of its private state. 例如,想象一下在Web应用程序的上下文中,并假设类Foo不是线程安全的,因此从不同的线程同时访问它的实例可能会导致其私有状态的破坏。 In this scenario you can decide to register the Foo class as a scoped dependency so that each time the application receives an HTTP request a new instance of Foo is created and that instance will be reused for the entire lifetime of the HTTP request. 在这种情况下,您可以决定将Foo类注册为作用域依赖项,以便每次应用程序收到HTTP请求时,都会创建一个新的Foo实例,并且该实例将在HTTP请求的整个生命周期内被重用。 Doing so is fine, even if handling the HTTP request implies the use of some async operations. 这样做很好,即使处理HTTP请求意味着使用了一些异步操作。 Supposing that you await on each involved async operation there can only be at most one thread accessing your Foo instance concurrently and the internal state of the instance is preserved against corruption. 假设您在等待每个涉及的异步操作,则最多只能有一个线程同时访问Foo实例,并且实例的内部状态得以保留,以防损坏。

Given this scenario, if you register the Bar class as a singleton then there could be only one instance of Bar for the entire lifetime of the application and so different threads will access the Bar instance concurrently (remember that a web application is able to serve multiple requests concurrently by using a pool of threads). 在这种情况下,如果将Bar类注册为一个单例,则在应用程序的整个生命周期中可能只有一个Bar实例,因此不同的线程将同时访问Bar实例(请记住,一个Web应用程序可以服务多个使用线程池并发请求)。 Your singleton instance of Bar has a reference to an instance of Foo which can potentially be used concurrently from multiple threads and this will lead to the corruption of its internal state and to unpredictable results. 您的Bar的单例实例引用了一个Foo实例,该实例可能会在多个线程中同时使用,这将导致其内部状态损坏并导致无法预测的结果。 You have a captive dependency because you have a singleton (the Bar class) which depends on a class having a shorter lifetime (the scoped Foo class). 您有一个圈养依赖性,因为您有一个单例(Bar类),该类依赖于寿命较短的类(范围为Foo的类)。

Going back to you question your solution is fine: you can't register your repository as a singleton, because it must use a scoped dependency, so registering it as a scoped service is the fine approach in my opinion. 回到您的问题,您的解决方案是否很好:您不能将存储库注册为单例,因为它必须使用范围内的依赖项,因此我认为将其注册为范围内的服务是一种很好的方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 无法使用单例的作用域服务 - Cannot consume scoped service from singleton 无法使用来自单例 Windows 主机服务的范围服务 - Cannot consume scoped service from singleton Windows Host Service 不能从singleton中使用作用域服务MyDbContext - InvalidOperationException - Cannot consume scoped service MyDbContext from singleton - InvalidOperationException 无法使用来自单例“IMyCustomThing”的范围服务“IHttpContextAccessor” - Cannot consume scoped service 'IHttpContextAccessor' from singleton 'IMyCustomThing' .NET 核心 DI:无法使用来自 singleton 的范围服务 - .NET Core DI: Cannot consume scoped service from singleton MultiTenant自定义ILogger - 不能从单例中使用作用域服务 - MultiTenant custom ILogger - cannot consume scoped service from singleton 无法在测试中重现异常无法从 Mvc 框架外的单例消费范围服务 - Cannot reproduce in tests the exception Cannot consume scoped service from singleton outside Mvc framework 无法使用来自单例“Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor”的范围服务“MyDbContext” - Cannot consume scoped service 'MyDbContext' from singleton 'Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor' 环境特定错误:无法使用来自单例“Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor”的范围服务 - Environment specific error: Cannot consume scoped service from singleton 'Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor' InvalidOperationException:无法从 Blazor 中的单例“...IAuthentication”使用范围服务“Microsoft.JSInterop.IJSRuntime” - InvalidOperationException: Cannot consume scoped service 'Microsoft.JSInterop.IJSRuntime' from singleton '...IAuthentication' in Blazor
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM