gpt4 book ai didi

c# - 无法使用单例 yyy 的作用域服务 xxx

转载 作者:太空宇宙 更新时间:2023-11-03 12:05:24 26 4
gpt4 key购买 nike

我关注了一篇博文“使用 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/

在这种情况下,数据不会保存在数据库中,而是像这样保存在内存中:

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

你会注意到:

(1) UseInMemoryDatabaseDbContext

(2) TodoRepository 上的AddSingleton

这很好用。现在,我更新了代码以将数据保存在真实数据库 中。所以主要的变化是:

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

我想通知我必须将 AspNetCore 从 1.0 迁移到 2.2。

现在在运行时,当以 Controller 为目标时,我收到错误:无法使用来自单例“Models.ITodoRepository”的作用域服务“Models.TodoContext”。

我理解在这种情况下:

  • 我的 TodoContext 是一个 Scoped 对象:在一个请求中相同,但在不同请求之间不同。

  • 我的 TodoRepository 是一个单例对象:每个对象和每个请求都相同。

所以我最终将 AddSingleton 更改为 AddScoped,效果非常好:

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

我的问题是:要知道这是否是一种可接受的方法?

PS:我知道在 SO 上还有关于此问题的其他问题,但我没有阅读明确的回复。

最佳答案

ASP.NET Core 内置的依赖注入(inject)容器保护您免受称为“俘虏依赖”的依赖注入(inject)反模式(您可以阅读更多关于它和一般依赖注入(inject)的信息 here )。

基本思想是,具有特定生命周期的类只能依赖于生命周期等于或长于自身生命周期的对象。这是因为当你进行依赖注入(inject)时,你提供了一个类及其所有依赖项(通常通过构造函数注入(inject)),并且该类保存了对依赖项的引用,以便以后需要时可以使用它。

因为您正在设计的类保存了对注入(inject)对象的引用,所以只要您的类的实例存在,注入(inject)对象就会(至少)保持事件状态。也许一个例子可以帮助你理解。

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

如果 Foo 实例的预期生命周期短于 Bar 实例的预期生命周期,这种情况可能会给您带来麻烦。

例如,假设在 Web 应用程序的上下文中,假设类 Foo 不是线程安全的,因此从不同线程同时访问它的实例可能会导致其私有(private)状态损坏。在这种情况下,您可以决定将 Foo 类注册为作用域依赖项,这样每次应用程序收到 HTTP 请求时,都会创建一个新的 Foo 实例,并且该实例将在 HTTP 请求的整个生命周期内重复使用。这样做很好,即使处理 HTTP 请求意味着使用一些异步操作。假设您在每个涉及的异步操作上等待,最多只能有一个线程同时访问您的 Foo 实例,并且实例的内部状态被保存以防止损坏。

在这种情况下,如果您将 Bar 类注册为单例,那么在应用程序的整个生命周期中可能只有一个 Bar 实例,因此不同的线程将同时访问 Bar 实例(请记住,Web 应用程序能够通过使用线程池同时处理多个请求)。您的 Bar 单例实例引用了 Foo 的实例,该实例可能会被多个线程并发使用,这将导致其内部状态损坏并导致不可预测的结果。你有一个俘虏依赖,因为你有一个单例( Bar 类),它依赖于一个生命周期较短的类(作用域 Foo 类)。

回到你的问题,你的解决方案很好:你不能将你的存储库注册为单例,因为它必须使用作用域依赖项,所以在我看来将它注册为作用域服务是很好的方法。

关于c# - 无法使用单例 yyy 的作用域服务 xxx,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55325828/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com