Реализация шаблона параметров с помощью класса, внедренного во время ConfigureServices - AddScoped - PullRequest
1 голос
/ 25 сентября 2019

У меня есть небольшой класс, чтобы получить серию информации о моем пользователе по нескольким моим приложениям MVC.Минимальный воспроизводимый пример был бы:

public class InformationGetter
    {
        public string GetUserInformation(string connectionStr, string storedProcedureName, int userId)
        {
            // Do SQL work
            return info;
        }
}

Я внедряю его на шаге ConfigureServices, используя

services.AddScoped<InformationGetter>

И затем в моих классах я просто вызываю его из DI.

Теперь очевидно, что connectionStr и StoreProcedure изменяются только для каждого приложения, но сейчас я передаю его как параметр.

Я попытался сделать эти параметры общедоступными и настроить их с помощью services.Configure, но когдаЯ вызываю его с моих контроллеров, получаю нулевые значения.

            services.AddOptions();
            services.Configure<InformationGetter>(options =>
            {
                options.ConnectionString = Configuration.GetSection("Model").GetSection("ConnectionString").Value;
                options.StoredProcedureName = "prInformationGetter";
            });

Я не уверен, что причина, по которой это не получается, заключается в том, что мне не хватает интерфейса в моем исходном классе или я не могуПонимаю эту концепцию.

Я также думал о том, чтобы сделать что-то вроде services.AddInformationGetter(options => {}), но я понимаю, что этот шаблон предназначен для реализации промежуточного программного обеспечения, а не DI специально.

Я попытался проверить документацию (docs.microsoft.com), но запутался еще больше.

1 Ответ

1 голос
/ 25 сентября 2019

Может быть неправильное понимание задействованных концепций.

Configure<TOption> зарегистрирует IOptions<TOptions>.Теперь в вашем примере есть две отдельные регистрации.

Один раз при регистрации класса

services.AddScoped<InformationGetter>()

и другой при регистрации параметров.

Выполните следующие действия

//..

services.AddOptions();

//Adds IOptions<InformationGetter>
services.Configure<InformationGetter>(options => {
    options.ConnectionString = Configuration.GetSection("Model").GetSection("ConnectionString").Value;
    options.StoredProcedureName = "prInformationGetter";
});

//Adds InformationGetter but gets it from the registered options
services.AddScoped<InformationGetter>(sp => 
    sp.GetRequiredService<IOptions<InformationGetter>>().Value
);

//...

Регистрация в области действия будет использовать фабричный делегат для извлечения зарегистрированных опций и возврата нужного типа.

public class InformationGetter {
    public string ConnectionString { get; set; }
    public string StoredProcedureName { get; set; }
    //...

    public string GetUserInformation(int userId) {
        // Do SQL work
        return info;
    }
}

InformationGetter выглядит как служба.

Я бы предложил рефакторинг в соответствии с более единым принципом ответственности (SRP) и разделением интересов (Soc).

//Needed by InformationGetter to perform its function
public class InformationGetterOptions {
    public string ConnectionString { get; set; }
    public string StoredProcedureName { get; set; }
}

//abstraction of InformationGetter
public interface IInformationGetter {
    string GetUserInformation(int userId);
}

//implementation.
public class InformationGetter : IInformationGetter{  
    private readonly InformationGetterOptions options;

    public InformationGetter(InformationGetterOptions options) {
        this.options = options;
    }

    public string GetUserInformation(int userId) {

        //use values in options to connect

        // Do SQL work
        return info;
    }
}

Я бы вообще отказался от шаблона опций и просто зарегистрировал классиспользуя фабрику делегатов, извлекая то, что мне нужно из конфигурации.Таким образом, ваш код не будет тесно связан с проблемами инфраструктуры, такими как IOptions

public void ConfigureServices(IServiceCollection services) {

    //...

    InformationGetterOptions options = new InformationGetterOptions {
        ConnectionString = Configuration.GetSection("Model").GetSection("ConnectionString").Value;
        StoredProcedureName = "prInformationGetter";
    };
    services.AddSingleton(options);
    services.AddScoped<IInformationGetter, InformationGetter>();

    //...
}

Теперь IInformationGetter можно вводить там, где это необходимо, и иметь все необходимые зависимости для выполнения своей функции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...