web-dev-qa-db-fra.com

Injection de dépendance avec des classes autres qu'une classe de contrôleur

À ce stade, j'injecte facilement des éléments dans mes contrôleurs, dans certains cas, je construis ma propre classe ResolverServices. La vie est belle.

Ce que je n'arrive pas à comprendre, c'est que le framework s'injecte automatiquement dans les classes non contrôleurs. Ce qui fonctionne, c’est que le framework injecte automatiquement dans mon contrôleur IOptions, ce qui correspond effectivement à la configuration de mon projet:

public class MessageCenterController : Controller
{
    private readonly MyOptions _options;

    public MessageCenterController(IOptions<MyOptions> options)
    {
        _options = options.Value;
    }
}

Je pense que je peux avoir la même chose pour mes propres cours et je suppose que je suis proche quand je mime le contrôleur, comme ceci:

public class MyHelper
{
    private readonly ProfileOptions _options;

    public MyHelper(IOptions<ProfileOptions> options)
    {
        _options = options.Value;
    }

    public bool CheckIt()
    {
        return _options.SomeBoolValue;
    }
}

Je pense que j'échoue lorsque je l'appelle comme ceci:

public void DoSomething()
{
    var helper = new MyHelper(??????);

    if (helper.CheckIt())
    {
        // Do Something
    }
}

Le problème que j’ai rencontré est pratiquement tout ce qui parle de DI en parle au niveau du contrôleur. J'ai essayé de rechercher où cela se passe dans le code source de l'objet Controller, mais cela devient un peu fou.

Je sais que je peux créer manuellement une instance d’IOptions et la transmettre au constructeur MyHelper, mais il me semble que je devrais pouvoir obtenir le framework comme cela car il fonctionne pour Controllers.

Votre aide est appréciée.

53
Robert Paulsen

Vous trouverez ci-dessous un exemple d'utilisation de DI sans aucun élément impliquant des contrôleurs MVC. C'est ce que je devais faire pour comprendre le processus, alors peut-être que cela pourrait aider quelqu'un d'autre.

L'objet ShoppingCart obtient, via DI, une instance d'INotifier (qui informe le client de sa commande.)

using Microsoft.Extensions.DependencyInjection;
using System;

namespace DiSample
{
    // STEP 1: Define an interface.
    /// <summary>
    /// Defines how a user is notified. 
    /// </summary>
    public interface INotifier
    {
        void Send(string from, string to, string subject, string body);
    }

    // STEP 2: Implement the interface
    /// <summary>
    /// Implementation of INotifier that notifies users by email.
    /// </summary>
    public class EmailNotifier : INotifier
    {
        public void Send(string from, string to, string subject, string body)
        {
            // TODO: Connect to something that will send an email.
        }
    }

    // STEP 3: Create a class that requires an implementation of the interface.
    public class ShoppingCart
    {
        INotifier _notifier;

        public ShoppingCart(INotifier notifier)
        {
            _notifier = notifier;
        }

        public void PlaceOrder(string customerEmail, string orderInfo)
        {
            _notifier.Send("[email protected]", customerEmail, $"Order Placed", $"Thank you for your order of {orderInfo}");
        }

    }

    public class Program
    {
        // STEP 4: Create console app to setup DI
        static void Main(string[] args)
        {
            // create service collection
            var serviceCollection = new ServiceCollection();

            // ConfigureServices(serviceCollection)
            serviceCollection.AddTransient<INotifier, EmailNotifier>();

            // create service provider
            var serviceProvider = serviceCollection.BuildServiceProvider();

            // This is where DI magic happens:
            var myCart = ActivatorUtilities.CreateInstance<ShoppingCart>(serviceProvider);

            myCart.PlaceOrder("[email protected]", "2 Widgets");

            System.Console.Write("Press any key to end.");
            System.Console.ReadLine();
        }
    }
}
24
Robert Paulsen

Disons que MyHelper est utilisé par MyService, qui à son tour est utilisé par votre contrôleur.

La façon de résoudre cette situation est la suivante:

  • S'enregistrer à la fois MyService et MyHelper dans Startup.ConfigureServices.

    services.AddTransient<MyService>();
    services.AddTransient<MyHelper>();
    
  • Le contrôleur reçoit une instance de MyService dans son constructeur.

    public HomeController(MyService service) { ... }
    
  • MyService constructeur recevra à son tour une instance de MyHelper.

    public MyService(MyHelper helper) { ... }
    

Le framework DI pourra résoudre le graphe d'objet entier sans problèmes. Si vous craignez que de nouvelles instances ne soient créées chaque fois qu'un objet est résolu, vous pouvez en savoir plus sur les différentes options de durée de vie et d'enregistrement telles que le singleton ou les durées de vie de la requête.

Vous devriez être vraiment méfiant lorsque vous pensez que vous devez créer manuellement une instance d'un service, car vous pourriez vous retrouver dans le anti-pattern de localisateur de service . Mieux vaut laisser les objets créer dans le conteneur DI. Si vous vous trouvez vraiment dans cette situation (supposons que vous créez une fabrique abstraite), vous pouvez utiliser directement le IServiceProvider (demandez un IServiceProvider dans votre constructeur ou utilisez celui-ci exposé dans le httpContext ).

var foo = serviceProvider.GetRequiredService<MyHelper>();

Je recommanderais de lire le document spécifique documentation sur le cadre ASP.Net 5 DI et sur l’injection de dépendance en général.

21
Daniel J.G.

Malheureusement, il n'y a pas de moyen direct. Le seul moyen de réussir à le faire fonctionner est de créer une classe statique et de l'utiliser partout ailleurs comme ci-dessous:

public static class SiteUtils
{

 public static string AppName { get; set; }

    public static string strConnection { get; set; }

}

Ensuite, dans votre classe de démarrage, remplissez-le comme suit:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    //normal as detauls , removed for space 
    // set my variables all over the site

    SiteUtils.strConnection = Configuration.GetConnectionString("DefaultConnection");
    SiteUtils.AppName = Configuration.GetValue<string>("AppName");
}

Bien que ce soit un mauvais schéma, cela restera pendant tout le cycle de vie de l'application et je ne pouvais pas trouver un meilleur moyen de l'utiliser en dehors du contrôleur.

5
Ali

Voici un exemple plus complet pour répondre directement à la question du PO basée sur la documentation actuelle de .NET Core 2.2 DI ici . Ajouter cette réponse, car cela pourrait aider une personne novice dans .NET Core DI et parce que cette question constitue le premier résultat de recherche de Google.

Premièrement, ajoutez une interface pour MyHelper:

public interface IMyHelper
{
    bool CheckIt();
}

Deuxièmement, mettez à jour la classe MyHelper pour implémenter l'interface (dans Visual Studio, appuyez sur ctrl-. Pour implémenter l'interface):

public class MyHelper : IMyHelper
{
    private readonly ProfileOptions _options;

    public MyHelper(IOptions<ProfileOptions> options)
    {
        _options = options.Value;
    {

    public bool CheckIt()
    {
        return _options.SomeBoolValue;
    }
}

Troisièmement, enregistrez l'interface en tant que service fourni par la structure dans le conteneur de services DI. Faites cela en enregistrant le service IMyHelper avec le type concret MyHelper dans la méthode ConfigureServices dans Startup.cs.

public void ConfigureServices(IServiceCollection services)
{
    ...
    services.AddScoped<IMyHelper, MyHelper>();
    ...
}

Quatrièmement, créez une variable privée pour référencer une instance du service. Passez le service en tant qu’argument dans le constructeur (via l’injection de constructeur), puis initialisez la variable avec l’instance de service. Référencez toutes les propriétés ou méthodes d'appel sur cette instance de la classe personnalisée via la variable privée.

public class MessageCenterController : Controller
{
    private readonly MyOptions _options;
    private readonly IMyHelper _myHelper;

    public MessageCenterController(
        IOptions<MyOptions> options,
        IMyHelper myHelper
    )
    {
        _options = options.value;
        _myHelper = myHelper;
    }

    public void DoSomething()
    {
        if (_myHelper.CheckIt())
        {
            // Do Something
        }
    }
}
0
sfors