94 votes

Impossible de consommer le service défini IMongoDbContext de singleton IActiveUsersService après la mise à niveau vers ASP.NET Core 2.0

J'ai mis à jour un projet vers ASP.NET Core 2 aujourd'hui et j'obtiens le message d'erreur suivant:

Impossible de consommer le service défini IMongoDbContext de singleton IActiveUsersService

J'ai l'enregistrement suivant:

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

Pourquoi DI ne peut pas consommer le service? Tout fonctionne bien pour ASP.NET Core 1.1.

139voto

juunas Points 19242

Vous ne pouvez pas utiliser un service avec une plus petite durée de vie. L'étendue des services n'existent que par la demande, tout singleton services sont créés une fois et l'instance est partagé.

Désormais, une seule instance de IActiveUsersService existe dans l'application. Mais il veut dépendent MongoDbContext, ce qui est étendu, et est créé par demande.

Il vous faudra:

  1. Faire MongoDbContext un Singleton, ou
  2. Faire IActiveUsersService d'Étendue, ou
  3. Pass MongoDbContext dans le service à l'usager comme un argument de fonction

47voto

Toby J Points 942

Il existe des différences importantes entre l'Étendue et Singleton services. L'avertissement est là pour apporter ce à la lumière, et de l'éteindre ou de commutation autour de la durée de vie sans distinction à faire disparaître, ne résoudra pas le problème.

Services spécifiques sont créés à partir d'un IServiceScope. L'un de ses objectifs majeurs est d'assurer que tout IDisposable services qui sont créés dans le champ d'application soient éliminés correctement quand le champ est lui-même.

Dans ASP.NET de Base, un service de champ est automatiquement créé pour vous sur chaque demande entrante, de sorte que d'ordinaire, vous n'avez pas besoin de vous inquiéter à ce sujet. Cependant, vous pouvez également créer votre propre champ d'application du service, vous avez juste besoin de disposer de vous-même.

Une façon de le faire est:

  • faites votre singleton service IDisposable,
  • injecter IServiceProvider,
  • créer et enregistrer un IServiceScope de la portée à l'aide de l' IServiceProvider.CreateScope() méthode d'extension,
  • utilisez ce champ pour créer la portée du service dont vous avez besoin,
  • disposer le champ d'application du service de l' Dispose méthode.
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();
   }
}

Selon la façon dont vous utilisez ces et l'étendue des services que vous consommez, vous pouvez effectuer l'une des opérations suivantes:

  • créer une instance unique de l'étendue du service et de l'utiliser pour la vie de l'singleton; ou
  • stocker une référence à l' (injectée) racine IServiceProvider, l'utiliser pour créer un nouveau IServiceScope à l'intérieur d'un using bloc à chaque fois que vous besoin d'une étendue de services, et de laisser le champ d'application sont disposées lorsque le bloc de sorties.

Il suffit de garder à l'esprit que tout IDisposable services créés à partir d'un IServiceScope sera automatiquement éliminé lorsque l'étendue elle-même n'.

En bref, ne vous contentez pas de changement autour de la durée de vie de vos services à "faire fonctionner"; vous devez toujours penser à propos de ceux-ci et être sûr qu'ils éliminés correctement. ASP.NET Noyau gère les cas les plus courants automatiquement; pour les autres, vous avez juste besoin de faire un peu plus de travail.

Depuis C# 1.0, nous avons eu using() blocs pour s'assurer que les ressources sont éliminés correctement. Mais using() blocs ne fonctionne pas lorsque quelque chose d'autre (la DI de service) est de la création de ces ressources pour vous. C'est là où l'Étendue des services et de les utiliser de manière incorrecte entraînera les ressources des fuites dans votre programme.

10voto

Robin Thomas Points 39

Vous pouvez aussi ajouter

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

avant .Build() en Program.cs fichier pour désactiver la validation.

Essayez ceci uniquement pour les tests de développement, ActiveUsersService est singleton et a une durée de vie supérieure à celle de MongoDbContext, qui est étendue et ne sera pas éliminée.

6voto

Liran Friedman Points 1036

Il est une autre façon d'aborder cette question, et c'est par l'ajout de l' MongoDbContext à la DI comme AddTransient comme ceci:

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

Le sens de l'utilisation de cette approche est que vous vous retrouverez avec une instance de MongoDbContext pour chaque Singleton classe que vous avez de l'utiliser. Par exemple, si vous avez 10 Singleton classes à l'aide d' MongoDbContext vous aurez 10 instances de celui-ci, mais c'est plutôt de la création d'une instance pour chaque demande.

Voir cette référence: Ne peut pas Consommer de l'Étendue du Service De Singleton – Une Leçon De ASP.net Core DI Étendues

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X