Dependencyinjection: Autoriser les services à se décorer en empêchant la résolution récursive

Créé le 18 déc. 2015  ·  31Commentaires  ·  Source: aspnet/DependencyInjection

User Story / Scénario de cas d'utilisation

En tant que développeur, je souhaite étendre un service précédemment enregistré en l'enveloppant avec un autre service qui expose la même interface.

Voir : https://github.com/aspnet/Mvc/issues/3739

Exemple d'interface et d'implémentations

public interface IMyService
{
    void DoSomething();
}

public partial class DefaultMyService
{
    public partial void DoSomething();
}

public partial class DecoratorForMyService
{
    private IMyService decorated;

    public DecoratorForMyService(IMyService decorated)
    {
        this.decorated = decorated;
    }

    public partial void DoSomething();
}

Exemples de façons de configurer la collection de services

Utilisation de la résolution du constructeur

var collection = new ServiceCollection();
collection.AddTransient<IMyService, DefaultMyService>();
collection.AddTransient<IMyService, DecoratorForMyService>();
var provider = collection.BuildServiceProvider();
var service = collection.GetService<IMyService>(); // Returns the constructed DecoratorForMyService

Utilisation des fabriques d'implémentation

var collection = new ServiceCollection();
collection.AddTransient<IMyService, DefaultMyService>();
collection.AddTransient<IMyService>(_provider =>
{
    // Returns the previously registered IMyService.
    // Calling _provider.GetService<IEnumerable<IMyService>>() here
    // would return a collection containing all registered IMyServices
    // except for this one.
    var decorated = _provider.GetService<IMyService>(); 
    return new DecoratorForMyService(decorated);
});
var provider = collection.BuildServiceProvider();
var service = collection.GetService<IMyService>(); // Returns the constructed DecoratorForMyService

Considérations comportementales

Actuellement, les deux approches ci-dessus entraînent respectivement une exception "Circular Dependency" et un StackOverflowException . Une implémentation qui autorise les scénarios ci-dessus empêcherait efficacement le StackOverflowException entièrement et ne lèverait l'exception 'Circular Dependency' que lorsqu'il n'y a pas d'autres services précédemment enregistrés pour le type en cours de résolution, ce qui entraînerait la levée de scénarios de dépendance circulaire le type d'exception "Impossible de résoudre le service...", qui devrait en soi suffisamment traduire la circularité pour ces cas.

Commentaire le plus utile

Salut les gens !

Je viens de peaufiner et d'inclure les API Decorate<TService> de mon essentiel ci-dessus dans Scrutor (v1.9).

Voici un exemple sur la façon de l'utiliser (du README.md):

var collection = new ServiceCollection();

// First, add our service to the collection.
collection.AddSingleton<IDecoratedService, Decorated>();

// Then, decorate Decorated with the Decorator type.
collection.Decorate<IDecoratedService, Decorator>();

// Finally, decorate Decorator with the OtherDecorator type.
// As you can see, OtherDecorator requires a separate service, IService. We can get that from the provider argument.
collection.Decorate<IDecoratedService>((inner, provider) => new OtherDecorator(inner, provider.GetRequiredService<IService>()));

var serviceProvider = collection.BuildServiceProvider();

// When we resolve the IDecoratedService service, we'll get the following structure:
// OtherDecorator -> Decorator -> Decorated
var instance = serviceProvider.GetRequiredService<IDecoratedService>();

Tous les 31 commentaires

Nous n'allons pas soutenir quelque chose comme ça. Le problème est que c'est une fonctionnalité très spécifique et nous aurions besoin d'autres systèmes DI pour fonctionner de cette façon. Ou nous pourrions simplement abandonner et vous forcer à utiliser les nôtres

Pour la recherche, pouvez-vous jeter un œil à d'autres systèmes DI et voir comment ils résolvent ces problèmes. S'ils le permettent même

  • NInject permet de spécifier une implémentation particulière à utiliser lors de l'injection dans une autre implémentation particulière. Pas sûr de la situation «usine».
  • StructureMap permet des spécifications similaires, ainsi que la possibilité de spécifier un type avec lequel envelopper d'autres implémentations enregistrées à l'avance.
  • Spring.NET dont je ne suis pas sûr; ma recherche initiale ne donne pas d'exemple.
  • Windsor semble vous permettre d'enregistrer simplement chacun des types, bien qu'il semble que cela puisse prendre une approche dans l'ordre inverse
  • Autofac adopte une approche similaire à NInject mais il semblerait qu'une spécification supplémentaire soit requise en utilisant les clés de chaîne 'name'
  • Unity semble avoir fait la même chose qu'Autofac , en utilisant des enregistrements nommés pour gérer la décoration, mais il existe également une extension de décorateur disponible qui fonctionne de manière similaire à Windsor.

Ce sont toutes les bibliothèques DI "principales" pour .NET dont je me souviens. J'ai vu des noms d'autres personnes qui ne sont pas aussi populaires et je ne les ai donc pas approfondis.

Ce que j'ai proposé est alors très similaire à Windsor et Unity.

@davidfowl :

Je sais que vous avez dit que vous n'êtes pas ici pour discuter de philosophie, mais l'implémentation d'aspnet/DependencyInjection aborde déjà ce problème de manière incohérente en détectant et en retirant la récursivité pour un scénario mais pas pour l'autre. Cette incohérence pourrait être corrigée de plusieurs manières :

  • Ajoutez la détection au scénario d'usine pour empêcher qu'un StackOverflowException ne se produise
  • Supprimez la détection du scénario du constructeur et autorisez un StackOverflowException à se produire
  • Supprimer la détection du scénario du constructeur mais empêcher qu'un StackOverflowException ne se produise jamais en s'assurant qu'un service donné n'est pas disponible pour la résolution alors qu'il est déjà en cours de résolution

Chacune de ces voies résoudrait l'incohérence, mais une seule apporterait quelque chose d'utile à l'assiette.

En plus de cela, nous avons la question des "portées", qui est un détail d'implémentation non garanti par l'interface IServiceProvider . Et tandis que d'autres bibliothèques DI qui implémentent l'interface IServiceProvider une méthode peuvent implémenter une certaine forme de portée, d'autres non. Veuillez me corriger si je me trompe à ce sujet, mais compte tenu de cela, d'où vient la résistance à avoir des détails de mise en œuvre «spéciaux»? Voici une liste complète des éléments implémentés par l'implémentation d'aspnet/DependencyInjection qui sortent de l'interface extrêmement indescriptible GetService(Type) exposée par IServiceProvider :

  • Enregistrement automatique du IServiceProvider en tant que service
  • Mise en œuvre et enregistrement automatique du IServiceScopeFactory
  • Implémentation et enregistrement automatique du service ouvertement générique IEnumerable<> qui peut être utilisé pour récupérer chaque service enregistré par un certain type

Maintenant, en particulier à propos de ces autres bibliothèques de conteneurs, telles que NInject, StructureMap et Autofac, la façon dont elles permettent la spécification des décorateurs est très facilement accomplie avec la méthode lambda d'usine disponible pour l'implémentation d'aspnet/DependencyInjection, si seulement cette implémentation pouvait empêcher la récursivité à l'avant. :clin d'œil:

Honnêtement, je n'ai pas vu grand-chose de ce que les autres bibliothèques DI offrent qui n'est pas possible avec l'implémentation aspnet/DependencyInjection. Je pense que toute la question de "laisser les autres conteneurs faire ceci ou cela" n'est vraiment pas un problème.

Avoir la possibilité de créer un motif de décorateur dans le système DI est très utile et précieux.

Je l'ai fait fonctionner dans ce PR. Peu importe l'ordre dans lequel vous enregistrez les services. Il y a un cas de test avec 36 combinaisons de portées/commandes/etc. S'il vous plait, faite moi part de votre avis.

Edit : Cela a également modifié le résultat du scénario de dépendance circulaire. Désormais, au lieu d'une exception de dépendance circulaire, vous obtiendrez l'exception "Échec de la résolution du service de type ..." ou toute autre exception, qui en elle-même peut informer le développeur de la circularité.

J'aime cette idée de fonctionnalité, mais je la déplace dans le backlog car nous fermons actuellement de nouvelles fonctionnalités.

La prise en charge des décorateurs serait formidable, c'est la seule grande fonctionnalité qui me manque en ce moment.

Je sais, utiliser un conteneur différent serait facile, mais je n'aime pas le fait que votre DI et le conteneur tiers doivent être configurés différemment. C'est bizarre/déroutant d'avoir deux méthodes différentes pour la configuration du conteneur dans une application.

Je sais, utiliser un conteneur différent serait facile, mais je n'aime pas le fait que votre DI et le conteneur tiers doivent être configurés différemment. C'est bizarre/déroutant d'avoir deux méthodes différentes pour la configuration du conteneur dans une application.

Vous ne configurez qu'un seul conteneur. Vous devez vouloir dire transformer IServiceCollection (un tas de métadonnées pour décrire les enregistrements de service) dans l'API de conteneur spécifique. Cela ne configure pas 2 conteneurs (et cela ne disparaît pas).

PS : Ne vous attendez pas à ce que nous ajoutions des fonctionnalités à ce conteneur simplement parce que vous devez utiliser un conteneur tiers pour faire quelque chose qui n'est pas pris en charge. Nous ajouterons des fonctionnalités à ce conteneur très judicieusement.

Merci pour ta réponse David ! J'aime beaucoup l'abstraction IServiceCollection car elle est simple et me permet également de l'utiliser dans mes propres bibliothèques. et mes projets hôtes peuvent toujours décider d'utiliser un conteneur différent.

Cependant, dans l'une de mes bibliothèques, j'aurais besoin d'utiliser des décorateurs. Maintenant, je suis un peu coincé... Je peux soit décider de ne pas utiliser du tout IServiceCollection dans mes bibliothèques, soit utiliser un conteneur tiers et donc avoir plusieurs conteneurs dans mes bibliothèques (qui doivent ensuite être configurés/appelés différemment - soit par ConfigureServices ou par une autre méthode)

Je sais, idéalement, il n'y aurait pas de code DI dans mes bibliothèques, mais c'est vraiment bien d'avoir une configuration par défaut dans chaque bibliothèque (comme vous le faites partout aussi).

Recommanderiez-vous donc de ne pas utiliser IServiceCollection dans nos propres bibliothèques et d'utiliser à la place un conteneur tiers partout ?

Pourquoi voudriez-vous imposer un contenant spécifique aux consommateurs de votre bibliothèque ? IMO, ce n'est pas une décision à prendre pour vous, l'auteur de la bibliothèque. Ne serait-il pas préférable d'ajouter simplement des points d'extensibilité, comme des usines, des fournisseurs, des activateurs, peu importe, qui peuvent être implémentés pour n'importe quel conteneur utilisé par le consommateur ? :le sourire:

Dans mon cas, je parle des bibliothèques internes de mon entreprise. Je sais que ce serait mieux de le faire, mais vous savez... Délais :-)

Et pour ajouter un contre-argument - si nous ne devrions pas utiliser de conteneurs dans les bibliothèques, pourquoi ce projet github existe-t-il ??? :-)

BTW : mon scénario réel est une bibliothèque de code métier qui contient de nombreux gestionnaires de commandes. J'utilise quelque chose de similaire à MediatR pour appeler les gestionnaires. Je voudrais ajouter la validation, l'autorisation, ... en tant que décorateurs car c'est une solution extrêmement simple.

Puisqu'il n'y a pas de support de décorateur dans IServiceCollection, j'utilise déjà une interface IMessageInterceptor distincte et le répartiteur en appelle toutes les instances enregistrées avant et après avoir appelé le gestionnaire réel. c'est OK mais ça commence à devenir bizarre pour la gestion des erreurs, la gestion des transactions et ainsi de suite parce que vous ne pouvez pas simplement mettre un try..catch ou une instruction using dessus comme vous le feriez avec des décorateurs.

Je pourrais également faire quelque chose de similaire au nouveau pipeline de requête/Middleware ASP.NET qui est basé sur les délégués, mais cela me semble exagéré.

Peut-être avez-vous d'autres idées ?! J'apprécie vraiment vos commentaires!

Et pour ajouter un contre-argument - si nous ne devrions pas utiliser de conteneurs dans les bibliothèques, pourquoi ce projet github existe-t-il ??? :-)

Eh, ça existe d'utiliser des conteneurs dans _apps_ ?

Peut-être avez-vous d'autres idées ?! J'apprécie vraiment vos commentaires!

Qu'en est-il simplement de câbler manuellement la décoration ?

Un pseudo-code :

public class ErrorHandlingMediator : IMediator
{
    public ErrorHandlingMediator(IErrorHandler errorHandler, IMediator inner) { }
}

public class ValidatingMediator : IMediator
{
    public ErrorHandlingMediator(IEnumerable<IValidator> validators, IMediator inner) { }
}

public class TransactionMediator : IMediator
{
    public ErrorHandlingMediator(ITransactionManager transactionManager, IMediator inner) { }
}

// Add IErrorHandler, IValidators and ITransactionManager to the service collection.

services.AddScoped<IMediator>(provider => {
    var singleFactory = new Func<Type, object>(provider.GetService);
    var multiFactory = new Func<Type, IEnumerable<object>>(provider.GetServices);

    // Let's configure the following pipeline: Error Handling -> Validation -> Transaction -> Request Handler

    return new ErrorHandlingMediator(
        provider.GetService<IErrorHandler>(),
        new ValidatingMediator(
            provider.GetServices<IValdiator>(),
            new TransactionMediator(
                provider.GetService<ITransactionManager>(),
                new Mediator( // This is the Mediator type from MediatR...
                    singleFactory,
                    multiFactory))));
});

Cela peut bien sûr être fait complètement sans aucun conteneur, mais vous auriez besoin de crochets pour résoudre les différents services, comme IErrorHandler etc. :smile:

o_O - Je suis stupide, je n'y ai pas pensé. :-) Ce n'est pas si dynamique/beau mais je suppose que je peux certainement vivre avec ça. Merci Kristian !! Une bière est sur moi si jamais nous nous rencontrons à une conférence. :Bière:

Il y a beaucoup à apprendre ! :-)

Peut-être intéressant pour d'autres personnes, qui ont également du mal avec quelque chose comme ça : j'essayais de décorer l'interface IRequestHandler<TMessage> au lieu du médiateur. De cette façon, le médiateur aurait dû créer/résoudre cette chaîne de classes et je n'aurais pas aimé ce code là. Mais je suppose que même dans ce cas, j'aurais pu le résoudre différemment en fournissant un autre crochet pour la création du gestionnaire. Merci encore Kristian !

Nous construisons un cadre qui étend le cadre MVC et dans le cadre de cela, nous voulons pouvoir décorer certains services fournis par MVC avec des fonctionnalités supplémentaires. En tant que framework, nous ne voulons pas imposer un conteneur spécifique à l'application qui utilisera notre framework, mais nous ne savons pas non plus quel service concret l'application a configuré au préalable.

J'imagine que le même problème se poserait pour tout Framework qui essaie de fournir le même type d'extensibilité générique que ASP.NET Core via la configuration du service. Pour étendre la fonctionnalité, vous devez soit contrôler le conteneur, c'est-à-dire être l'application, soit faire une hypothèse sur le service concret que vous allez étendre. Sinon, vous devrez vous rabattre sur l'exposition de points d'extension spécifiques tels que des usines ou des fournisseurs dans votre cadre. Est-ce une description juste?

Donc, de ce point de vue, il semble que cette fonctionnalité proposée serait d'une aide immense.

Je ne pense pas que beaucoup de gens s'en rendent compte, ou ils s'en moquent tout simplement, mais en empilant des fonctionnalités dans cette implémentation minimale du "plus petit dénominateur commun" d'un conteneur IoC, vous excluez essentiellement les autres conteneurs qui peuvent ' ne prend pas en charge ou ne prendra pas en charge cet ensemble de fonctionnalités requis.

Je ne peux pas parler pour les autres, mais je suis (au moins) conscient du problème Conforming Container. Mais puisque la décision a été prise d'en implémenter une dans le framework, je suppose qu'il y aura un conflit d'intérêts entre le souhait d'inclure et de prendre en charge autant d'implémentations de conteneurs que possible et le souhait des développeurs de framework qui ne contrôlent pas le conteneur d'avoir accès à un maximum de fonctionnalités.

Personnellement, je ne me considère pas assez expérimenté pour suggérer où cet équilibre devrait se situer, mais je voulais ajouter un point de vue que je n'avais pas vu être soulevé dans cette conversation.

Je suppose qu'il y aura un conflit d'intérêts entre le souhait d'inclure et de prendre en charge autant d'implémentations de conteneurs que possible et le souhait des développeurs de framework qui ne contrôlent pas le conteneur d'avoir accès à autant de fonctionnalités que possible.

Vous mentionnez ces développeurs de framework qui veulent accéder à autant de fonctionnalités que possible. Pourquoi ces frameworks ne peuvent-ils pas utiliser d'autres conteneurs qui _ont déjà_ ces fonctionnalités ? Je ne vois pas en quoi ce conteneur est spécial et peut/doit être utilisé dans toutes sortes de bibliothèques et de frameworks, alors que d'autres conteneurs ne le peuvent pas ?

Aussi, comme je l'ai mentionné ci-dessus; beaucoup envisageraient de forcer un conteneur spécifique sur les consommateurs de votre mauvaise forme de framework/bibliothèque.

Personnellement, je ne me considère pas assez expérimenté pour suggérer où cet équilibre devrait se situer

Si j'ai bien compris l'équipe, je pense que l'équilibre est quelque chose comme "Tout ce dont le framework a besoin pour se composer. Pour quelque chose de plus avancé, nous recommandons BYOC".

Si Microsoft devait empiler des fonctionnalités pour ce conteneur, elles finiraient par tuer, ou au moins aspirer beaucoup d'air de la pièce pour d'autres conteneurs existants dans l'espace .NET, comme ils l'ont fait auparavant avec divers autres efforts de la communauté. Maintenant, il n'y a toujours aucune garantie que cela ne se produira pas, mais limiter la portée du conteneur limitera au moins une partie de cet effet.

Je ne sais pas à quel point il est performant, mais cela semble bien fonctionner ; https://gist.github.com/khellang/c9d39444f713eab04c26dc09d5687196 :sourire:

Pourquoi ces frameworks ne peuvent-ils pas utiliser d'autres conteneurs disposant déjà de ces fonctionnalités ?

Je crois que vous avez répondu vous-même. En tant que framework, nous ne voulons pas forcer un conteneur spécifique sur l'application consommatrice.

Je ne vois pas en quoi ce conteneur est spécial et peut/doit être utilisé dans toutes sortes de bibliothèques et de frameworks, alors que d'autres conteneurs ne le peuvent pas ?

Je suppose que c'est une abstraction qui est déjà dans le framework .NET. Bien sûr, nous pourrions choisir d'exposer notre propre abstraction ou une autre abstraction existante d'un conteneur conforme auquel nous ne sommes pas limités aux fonctionnalités requises par Microsoft dans leur cadre, mais cela signifierait que nous avons besoin d'adaptateurs de conteneur supplémentaires, ce qui n'aide pas vraiment. personne à long terme.
Sinon, nous ignorons complètement ServiceCollection et exposons nos propres points d'extension, mais cela pourrait potentiellement empêcher nos consommateurs d'accéder aux points d'extension .NET qu'ils attendent.

Je pense que dans ce cas, nous devons nous insérer juste dans le sens que vous avez montré dans votre exemple. Nous avons expérimenté quelque chose de similaire bien que je n'admette pas aussi élégant que votre solution. Il faudra juste mesurer l'impact qu'aura la création d'un prestataire pour chaque décorateur. Il existe également un problème potentiel selon lequel l'implémentation décorée pourrait recevoir une dépendance de service incorrecte injectée si elle est à son tour remplacée après la décoration.

Je comprends le problème pour tous les conteneurs que vous décrivez et j'ai une grande sympathie pour ce côté du problème. Mais, je pense aussi que Microsoft a examiné l'implémentation des conteneurs principalement à partir des frameworks .NET et du côté application des choses et a raté certains des besoins des développeurs de framework. Mais peut-être que ce groupe est si petit que ces besoins devraient être minimisés.

Je crois que vous avez répondu vous-même. En tant que framework, nous ne voulons pas forcer un conteneur spécifique sur l'application consommatrice.

Exactement. Vous faites le point pour moi :wink: Pourquoi Microsoft.Extensions.DependencyInjection n'est-il pas un "conteneur spécifique" ?

J'ai également déclaré que si vous ne voulez pas dépendre d'"un conteneur spécifique" dans votre framework/bibliothèque, vous devez mettre en place des crochets pour "externaliser" la composition.

Je suppose que c'est une abstraction qui est déjà dans le framework .NET.

Non ce n'est pas. C'est un package NuGet dont vous dépendez, comme tout autre conteneur IoC :smile:

Bien sûr, nous pourrions choisir d'exposer notre propre abstraction ou une autre abstraction existante d'un conteneur conforme auquel nous ne sommes pas limités aux fonctionnalités requises par Microsoft dans leur cadre, mais cela signifierait que nous avons besoin d'adaptateurs de conteneur supplémentaires, ce qui n'aide pas vraiment. personne à long terme.

Ou vous pouvez mettre en place des points d'extensibilité pour ne pas utiliser de "conteneur conforme" du tout.

BTW, savez-vous que plusieurs conteneurs ont déjà ces adaptateurs pour MS.Ext.DI ? Lisez mon blog à ce sujet :wink:

Vous pouvez également simplement utiliser IServiceProvider dans votre bibliothèque. Cette interface se trouve dans le bon vieux BCL (mscorlib sous l'espace de noms System ) depuis .NET 1.1. L'implémentation de cette interface pour un conteneur représente littéralement 4 lignes de code si vous ne comptez pas le passe-partout de la définition de classe et l'implémentation de l'interface elle-même.

Sinon, nous ignorons complètement ServiceCollection et exposons nos propres points d'extension, mais cela pourrait potentiellement empêcher nos consommateurs d'accéder aux points d'extension .NET qu'ils attendent.

De quels points d'extension .NET parlez-vous ?

Il faudra juste mesurer l'impact qu'aura la création d'un prestataire pour chaque décorateur.

Je ne pense pas que tu aies besoin de faire ça ?

Il existe également un problème potentiel selon lequel l'implémentation décorée pourrait recevoir une dépendance de service incorrecte injectée si elle est à son tour remplacée après la décoration.

Comment pourriez-vous vous en sortir ? Indice : vous ne pouvez pas, IServiceCollection est modifiable :stuck_out_tongue_winking_eye :

Je pense également que Microsoft a examiné la mise en œuvre des conteneurs principalement à partir des frameworks .NET et du côté application des choses et a manqué certains des besoins des développeurs de framework.

Microsoft a déjà construit un framework assez décent au-dessus de l'abstraction IServiceProvider , à savoir ASP.NET :smile: Ce conteneur est censé être une implémentation simple pour répondre aux besoins de ce framework, rien de plus, rien de moins .

Salut les gens !

Je viens de peaufiner et d'inclure les API Decorate<TService> de mon essentiel ci-dessus dans Scrutor (v1.9).

Voici un exemple sur la façon de l'utiliser (du README.md):

var collection = new ServiceCollection();

// First, add our service to the collection.
collection.AddSingleton<IDecoratedService, Decorated>();

// Then, decorate Decorated with the Decorator type.
collection.Decorate<IDecoratedService, Decorator>();

// Finally, decorate Decorator with the OtherDecorator type.
// As you can see, OtherDecorator requires a separate service, IService. We can get that from the provider argument.
collection.Decorate<IDecoratedService>((inner, provider) => new OtherDecorator(inner, provider.GetRequiredService<IService>()));

var serviceProvider = collection.BuildServiceProvider();

// When we resolve the IDecoratedService service, we'll get the following structure:
// OtherDecorator -> Decorator -> Decorated
var instance = serviceProvider.GetRequiredService<IDecoratedService>();

@khellang oui, c'est à peu près ce que nous avons dû ajouter à IdentityServer...

@khellang Merci d'avoir fourni votre projet, cela m'a aidé.

Ce serait bien si la fonctionnalité de décorateur était ajoutée à l'offre par défaut.

@brockallen Comment les décorateurs ont-ils été utilisés dans l'implémentation interne d'IdentityServer4 ?

@brockallen Comment les décorateurs ont-ils été utilisés dans l'implémentation interne d'IdentityServer4 ?

Principalement la mise en cache autour des services qui chargent des données à partir d'un magasin, mais nous devons également le faire pour encapsuler certains des services d'authentification intégrés afin d'améliorer leurs fonctionnalités.

@davidfowl L'utilisation de décorateurs avec DI n'est pas une fonctionnalité très spécifique (à mon avis); au contraire, c'est un modèle fondamental dans les principes SOLID qui facilite facilement le principe SRP.

Cependant, il est assez facile (mais pas aussi élégant) de contourner cela en utilisant la méthode lamda d'usine fournie par le conteneur. J'aimerais voir un support générique pour cela.

var collection = new ServiceCollection() .AddSingleton<IPackageService>(p => new FaultHandlingPackageService(p.GetService<PackageService>()));

@andersborum C'est précisément ce que je fais avec Scrutor . Regardez le code. Ce n'est rien.

Ce problème a été déplacé vers aspnet/Home#2350

Cette page vous a été utile?
0 / 5 - 0 notes