升级到ASP.NET Core 2.0后,无法从singleton IActiveUsersService使用作用域服务IMongoDbContext

use*_*173 47 c# asp.net-core

我今天更新了一个项目到ASP.NET Core 2,我收到以下错误:

不能从singleton IActiveUsersService使用作用域服务IMongoDbContext

我有以下注册:

services.AddSingleton<IActiveUsersService, ActiveUsersService>();
services.AddScoped<IMongoDbContext, MongoDbContext>();
services.AddSingleton(option =>
{
   var client = new MongoClient(MongoConnectionString.Settings);
   return client.GetDatabase(MongoConnectionString.Database);
})



public class MongoDbContext : IMongoDbContext
{
   private readonly IMongoDatabase _database;

   public MongoDbContext(IMongoDatabase database)
   {
      _database = database;
   }

   public IMongoCollection<T> GetCollection<T>() where T : Entity, new()
   {
      return _database.GetCollection<T>(new T().CollectionName);
   }
}

public class IActiveUsersService: ActiveUsersService
{

   public IActiveUsersService(IMongoDbContext mongoDbContext)
   {
      ...
   }
}
Run Code Online (Sandbox Code Playgroud)

为什么DI无法使用该服务?一切都适用于ASP.NET Core 1.1.

juu*_*nas 57

您不能使用寿命较短的服务.仅按请求存在范围服务,而单一服务创建一次并且实例共享.

现在IActiveUsersService,应用程序中只存在一个实例.但它希望依赖于MongoDbContextScoped,并且是按请求创建的.

你必须要么:

  1. MongoDbContext一个单身人士,或
  2. 制作IActiveUsersServiceScoped,或
  3. MongoDbContext作为函数参数传递给用户服务

  • 有道理,但是,如果我有另一个类,例如`MyService`,它是有范围的,我想为这个服务定义`MongoDbContext` 的范围。 (2认同)

Tob*_*s J 15

Scoped和Singleton服务之间存在重要差异.警告是为了使其发光,关闭它或不加选择地切换寿命以使其消失将无法解决问题.

范围内的服务是通过创建的IServiceScope.其最重要的目的之一是确保IDisposable在范围本身时正确处理在该范围内创建的任何服务.

在ASP.NET Core中,会在每个传入请求中自动为您创建服务范围,因此您通常不必担心这一点.但是,您也可以创建自己的服务范围; 你只需要自己处理它.

一种方法是:

  • 做你的单身人士服务IDisposable,
  • 注入IServiceProvider,
  • IServiceScope使用IServiceProvider.CreateScope()扩展方法创建和存储范围,
  • 使用该范围来创建所需的范围服务,
  • Dispose方法中配置服务范围.
services.AddSingleton<IActiveUsersService, ActiveUsersService>();
services.AddScoped<IMongoDbContext, MongoDbContext>();
services.AddSingleton(option =>
{
   var client = new MongoClient(MongoConnectionString.Settings);
   return client.GetDatabase(MongoConnectionString.Database);
})

public class MongoDbContext : IMongoDbContext
{
   private readonly IMongoDatabase _database;

   public MongoDbContext(IMongoDatabase database)
   {
      _database = database;
   }

   public IMongoCollection<T> GetCollection<T>() where T : Entity, new()
   {
      return _database.GetCollection<T>(new T().CollectionName);
   }
}

public class ActiveUsersService: IActiveUsersService, IDisposable
{
   private readonly IServiceScope _scope;

   public ActiveUsersService(IServiceProvider services)
   {
      _scope = services.CreateScope(); // CreateScope is in Microsoft.Extensions.DependencyInjection
   }

   public IEnumerable<Foo> GetFooData()
   {
       using (var context = _scope.ServiceProvider.GetRequiredService<IMongoDbContext>())
       {
           return context.GetCollection<Foo>();
       }
   }

   public void Dispose()
   {
       _scope?.Dispose();
   }
}
Run Code Online (Sandbox Code Playgroud)

根据您使用这些以及您正在使用的作用域服务的方式,您可以改为执行以下操作之一:

  • 创建作用域服务的单个实例,并将其用于单例的生命周期; 要么
  • 存储对(注入)根的引用,每次需要范围服务时IServiceProvider使用它IServiceScopeusing块内部创建一个新的,并在块退出时让范围被释放.

请记住,当范围本身发生时,IDisposable从一个IServiceScope意志创建的任何服务都会自动处理.

简而言之,不要只是改变服务的生命周期来"让它工作"; 你还需要考虑那些并确保它们得到妥善处理.ASP.NET Core自动处理最常见的情况; 对于其他人来说,你只需要做更多的工作.

自从C#1.0以来,我们已经有了using()块来确保正确处理资源.但是using()当其他东西(DI服务)为您创建这些资源时,块不起作用.这就是Scoped服务的用武之地,错误地使用它们会导致程序中的资源泄漏.


Lir*_*man 7

还有另一种方法可以解决这个问题,它将添加MongoDbContext到 DI 中,AddTransient如下所示:

\n\n
services.AddSingleton<IActiveUsersService, ActiveUsersService>();\nservices.AddTransient<IMongoDbContext, MongoDbContext>();\n
Run Code Online (Sandbox Code Playgroud)\n\n

使用这种方法的意义在于,对于使用它的MongoDbContext每个类,您最终都会得到一个实例。\n例如,如果您有 10 个使用它的 Singleton 类,那么您将拥有它的 10 个实例,但它\而不是为每个请求创建一个实例。SingletonMongoDbContext

\n\n

请参阅此参考:Cannot Consume Scoped Service From Singleton \xe2\x80\x93 A Lesson In ASP.net Core DI Scopes

\n


Rob*_*mas 5

您也可以添加

.UseDefaultServiceProvider(options =>
                    options.ValidateScopes = false)
Run Code Online (Sandbox Code Playgroud)

.Build()Program.cs文件之前禁用验证。

仅在进行开发测试时尝试使用此方法,ActiveUsersService是单例的,并且其生存期大于MongoDbContext的生存期,该MongoDbContext是有作用域的,不会被废弃。