121 votes

L'Injection de dépendance avec les classes autres que la classe Contrôleur

À ce point, je suis d'injecter des choses dans mon Contrôleurs avec facilité, dans certains cas, la construction de ma propre ResolverServices classe. La vie est bonne.

Ce que je ne peux pas comprendre comment le faire est d'obtenir le cadre d'injecter automatiquement dans le non-classes de contrôleur. Ce n'travail est de disposer d'un cadre injecter automatiquement dans mon contrôleur IOptions, ce qui est 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 classes, un je suppose que je suis proche quand j'ai imiter 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 où je ne suis pas, c'est quand je l'appelle comme ceci:

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

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

Le problème que j'ai suivi cette baisse est pratiquement tout ce qui parle de DI est d'en parler au niveau du contrôleur. J'ai essayé de la chasse où il arrive dans l' Controller objet en code source, mais il devient un peu fou là-dedans.

Je sais que je peux créer manuellement une instance de IOptions et de passer à l' MyHelper constructeur, mais il semble que je devrais être en mesure d'obtenir le cadre de le faire depuis qu'il travaille pour Controllers.

Votre aide est très appréciée.

68voto

Robert Paulsen Points 2339

Ci-dessous est un exemple de l'utilisation de DI sans tout ce qui implique des Contrôleurs MVC. C'est ce que j'avais à faire pour comprendre le processus, peut-être que ça aidera quelqu'un d'autre.

Le ShoppingCart objet obtient, via DI, une instance de 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("admin@store.com", 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("customer@home.com", "2 Widgets");

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

44voto

Daniel J.G. Points 23478

Disons - MyHelper est utilisée par MyService qui est utilisé par votre contrôleur.

La manière de résoudre cette situation est la suivante:

  • S'inscrire à la fois MyService et MyHelper en 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 de bénéficier en contrepartie d'une instance de MyHelper.

    public MyService(MyHelper helper) { ... }
    

Le DI-cadre sera en mesure de résoudre l'ensemble de l'objet graphique sans problèmes. Si vous êtes inquiet au sujet de nouvelles instances créées chaque fois qu'un objet est résolu, vous pouvez lire les différentes durée de vie et les options d'enregistrement comme le singleton ou à la demande de la durée de vie.

Vous devez être vraiment suspect quand vous pensez que vous devez créer manuellement une instance d'un service, comme vous pourriez vous retrouver dans le service localisateur anti-modèle. Mieux vaut laisser de créer les objets du Conteneur d'injection de dépendances. Si vraiment vous trouvez dans cette situation (par exemple, si vous créez un résumé de l'usine), alors vous pouvez utiliser l' IServiceProvider directement (Soit demander un IServiceProvider dans votre constructeur ou utilisez celui qui est exposé dans le httpContext).

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

Je vous recommande de lire les spécifique de la documentation sur le ASP.Net 5 DI-cadre et sur l'injection de dépendance en général.

9voto

Ali Points 244

Malheureusement, il n'existe pas de manière directe. La seule façon que j'ai réussi à le faire fonctionner est de créer une classe statique et l'aide que partout ailleurs comme ci-dessous:

public static class SiteUtils
{

 public static string AppName { get; set; }

    public static string strConnection { get; set; }

}

Puis dans votre démarrage de classe, le remplir comme ci-dessous:

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 qui est mauvais modèle, car cela va rester pour l'ensemble du cycle de vie de l'application et je ne pouvais pas trouver mieux pour l'utiliser en dehors du contrôleur.

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