Подтвердить что ты не робот

Инъекция зависимостей с классами, отличными от класса контроллера

На этом этапе я легко вставляю вещи в свои контроллеры, в некоторых случаях создаю свой собственный класс ResolverServices. Жизнь хороша.

Что я не могу понять, как это сделать, так это заставить фреймворк автоматически вводить в классы неконтроллеров. Что делает работа с каркасом, автоматически вставляемым в мой контроллер IOptions, который фактически является конфигурацией моего проекта:

public class MessageCenterController : Controller
{
    private readonly MyOptions _options;

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

Я думаю, что у меня может быть то же самое для моих собственных классов, и я полагаю, что я близок, когда я имитирую контроллер, например:

public class MyHelper
{
    private readonly ProfileOptions _options;

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

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

Я думаю, что, когда я сбой, это когда я называю это следующим образом:

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

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

Проблема, с которой я отслеживаю это, - это практически все, что говорит о том, что DI говорит об этом на уровне контроллера. Я попытался поискать, где это происходит в исходном коде объекта Controller, но там он выглядит безумным.

Я знаю, что могу вручную создать экземпляр IOptions и передать его конструктору MyHelper, но похоже, что я должен уметь это сделать, поскольку он работает для Controllers.

Ваша помощь приветствуется.

4b9b3361

Ответ 1

Ниже приведен рабочий пример использования DI без использования MVC-контроллеров. Это то, что мне нужно было сделать, чтобы понять процесс, поэтому, возможно, это поможет кому-то еще.

Объект ShoppingCart получает через DI экземпляр INotifier (который уведомляет клиента о своем заказе.)

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();
        }
    }
}

Ответ 2

Допустим, MyHelper используется MyService, который, в свою очередь, используется вашим контроллером.

Способ разрешить эту ситуацию:

  • Зарегистрируйте и MyService, и MyHelper в Startup.ConfigureServices.

    services.AddTransient<MyService>();
    services.AddTransient<MyHelper>();
    
  • Контроллер получает экземпляр MyService в своем конструкторе.

    public HomeController(MyService service) { ... }
    
  • Конструктор MyService, в свою очередь, получит экземпляр MyHelper.

    public MyService(MyHelper helper) { ... }
    

Инфраструктура DI сможет без проблем разрешить весь граф объектов. Если вы беспокоитесь о том, что новые экземпляры создаются при каждом разрешении объекта, вы можете прочитать о различных параметрах времени жизни и регистрации, таких как время жизни синглтона или запроса.

Вы должны быть очень подозрительны, когда считаете, что вам нужно вручную создать экземпляр какой-либо службы, поскольку вы можете оказаться в антишаблоне поиска служб. Лучше оставить создание объектов в DI-контейнере. Если вы действительно окажетесь в такой ситуации (допустим, вы создаете абстрактную фабрику), то вы можете использовать IServiceProvider напрямую (либо запросите IServiceProvider в своем конструкторе, либо используйте тот, который представлен в httpContext).

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

Я бы рекомендовал прочитать специальную документацию о платформе DI ASP.Net 5 и о внедрении зависимостей в целом.

Ответ 3

К сожалению, прямого пути нет. Единственный способ, которым мне удалось заставить его работать, - создать статический класс и использовать его везде, как показано ниже:

public static class SiteUtils
{

 public static string AppName { get; set; }

    public static string strConnection { get; set; }

}

Затем в вашем классе запуска заполните его, как показано ниже:

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");
}

Хотя это плохой шаблон, так как это останется на весь жизненный цикл приложения, и я не смог найти лучшего способа использовать его вне контроллера.

Ответ 4

Вот более полный пример для прямого ответа на вопрос OP, основанный на текущей документации DI.NET Core 2.2 здесь. Добавление этого ответа может помочь новичку в .NET Core DI, а также потому, что этот вопрос - лучший результат поиска Google.

Сначала добавьте интерфейс для MyHelper:

public interface IMyHelper
{
    bool CheckIt();
}

Во-вторых, обновите класс MyHelper для реализации интерфейса (в Visual Studio нажмите ctrl-. для реализации интерфейса):

public class MyHelper : IMyHelper
{
    private readonly ProfileOptions _options;

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

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

В-третьих, зарегистрируйте интерфейс в качестве службы, предоставляемой инфраструктурой, в контейнере службы DI. Сделайте это, зарегистрировав службу IMyHelper с конкретным типом MyHelper в методе ConfigureServices в Startup.cs.

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

В-четвертых, создайте приватную переменную для ссылки на экземпляр службы. Передайте сервис в качестве аргумента в конструкторе (через внедрение конструктора), затем инициализируйте переменную экземпляром сервиса. Ссылайтесь на любые свойства или вызовите методы в этом экземпляре пользовательского класса с помощью закрытой переменной.

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
        }
    }
}