visual tutorial studio net mvc asp c# asp.net-core

c# - tutorial - No se puede consumir el servicio con ámbito IMongoDbContext desde singleton IActiveUsersService después de actualizar a ASP.NET Core 2.0



asp.net core web api (4)

Hay otra forma de abordar este problema, y ​​es mediante la adición de MongoDbContext a la DI como AddTransient esta manera:

services.AddSingleton<IActiveUsersService, ActiveUsersService>(); services.AddTransient<IMongoDbContext, MongoDbContext>();

El significado de usar este enfoque es que terminarás con una instancia de MongoDbContext para cada clase de Singleton estés usando. Por ejemplo, si tiene 10 clases Singleton usando MongoDbContext , tendrá 10 instancias de eso, pero en lugar de crear una instancia para cada solicitud.

Vea esto como referencia: No se puede consumir el servicio de ámbito de Singleton - Una lección en ASP.net Core DI Scopes

Actualicé un proyecto a ASP.NET Core 2 hoy y recibo el siguiente error:

No se puede consumir el servicio con ámbito IMongoDbContext desde singleton IActiveUsersService

Tengo el siguiente registro:

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) { ... } }

¿Por qué DI no puede consumir el servicio? Todo funciona bien para ASP.NET Core 1.1.


No se puede utilizar un servicio con una vida útil menor. Los servicios con ámbito solo existen por solicitud, mientras que los servicios de singleton se crean una vez y la instancia se comparte.

Ahora solo existe una instancia de IActiveUsersService en la aplicación. Pero quiere depender de MongoDbContext , que es Scoped, y se crea por solicitud.

Tendrá que:

  1. Hacer MongoDbContext un Singleton, o
  2. Hacer IActiveUsersService Scoped, o
  3. Pase MongoDbContext al servicio de usuario como un argumento de función

También puedes añadir

.UseDefaultServiceProvider(options => options.ValidateScopes = false)

antes de .Build() en el archivo Program.cs para deshabilitar la validación.

Intente esto solo para las pruebas de desarrollo, ActiveUsersService es único y tiene una vida útil más larga que MongoDbContext, que tiene un ámbito y no se eliminará.


Hay diferencias importantes entre los servicios de Scoped y Singleton. La advertencia está ahí para sacar esto a la luz, y apagarlo o cambiar las vidas de manera indiscriminada para que desaparezca no resolverá el problema.

Los servicios de ámbito se crean desde un IServiceScope . Uno de sus propósitos más importantes es garantizar que todos los servicios IDisposable que se crean en ese ámbito se eliminen adecuadamente cuando el alcance en sí mismo.

En ASP.NET Core, un alcance de servicio se crea automáticamente para usted en cada solicitud entrante, por lo que normalmente no necesita preocuparse por esto. Sin embargo, también puede crear su propio ámbito de servicio; solo necesitas deshacerte de ti mismo.

Una forma de hacer esto es:

  • IDisposable su servicio de singleton IDisposable ,
  • inyectar IServiceProvider ,
  • cree y almacene un ámbito IServiceScope utilizando el método de extensión IServiceProvider.CreateScope() ,
  • use ese ámbito para crear el servicio de ámbito que necesita,
  • Dispose el alcance del servicio en el método 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(); } }

Dependiendo de cómo utilice estos y los servicios de alcance que consume, podría hacer uno de los siguientes:

  • cree una única instancia del servicio con ámbito y úsela durante la vida útil del singleton; o
  • almacene una referencia al IServiceProvider raíz (inyectado), utilícelo para crear un nuevo IServiceScope dentro de un bloque de using cada vez que necesite un servicio de ámbito, y deje que el alcance se elimine cuando el bloque salga.

Solo tenga en cuenta que cualquier servicio IDisposable creado a partir de un IServiceScope se eliminará automáticamente cuando el alcance sí lo haga.

En resumen, no se limite a cambiar la duración de sus servicios para "hacer que funcione"; Todavía debe pensar en ellos y asegurarse de que se eliminen adecuadamente. ASP.NET Core maneja los casos más comunes automáticamente; Para otros, solo necesitas trabajar un poco más.

Desde C # 1.0 hemos tenido que using() bloques para garantizar que los recursos se eliminen correctamente. Pero el using() bloques using() no funciona cuando otra cosa (el servicio DI) está creando esos recursos para usted. Ahí es donde entran los servicios de Scoped, y su uso incorrecto conducirá a fugas de recursos en su programa.