Мне интересно, можно ли динамически привязать раздел конфигурации к объекту. Обычно для привязки раздела конфигурации мы пишем такой код:
var section = Configuration.GetSection(nameof(MyCustomSection));
services.Configure<MyCustomSection>(o => secto.Bind(o));
Мне интересно, можно ли это сделать без объявления типа <MyCustomSection>.
//This doesn't work, just trying to show you how I would like to do it
services.Configure(MyType, o => section.Bind(o));
Например, если я хочу привязать инъекцию, я могу сделать это так:
services.AddTransient<IDateTime, SystemDateTime>();
Но я также могу сделать это динамически, например:
services.AddTransient(Type1, Type2));
Возможно ли то же самое для services.Configure? Я посмотрел на параметры метода, но, похоже, он его не поддерживает. Просто интересно, есть ли другой способ, или, может быть, я просто что-то упускаю из виду?
Обновлено:
services.AddSingleton(p =>
{
var type = new MySection();
Configuration.GetSection("MySection").Bind(type);
return type;
});
Затем я вызываю это в таком классе:
public class Test {
public Test(IOptions<MySection> section)
{
var finalValue = section.Value;
}
}
finalValue всегда равен нулю;





Во-первых, все, что делает Configure, это
Следовательно, если у Configure нет перегрузки для выполнения того, что вы хотите, вы можете просто перейти к отдельным задачам, т.е.
services.AddSingleton(p =>
{
var config = Activator.CreateInstance(type);
Configuration.GetSection("Foo").Bind(config);
return config;
}
Я предполагаю, что это какая-то проблема с тем, как вы получаете SectionName, т.е. он, вероятно, возвращает null или что-то в этом роде, так что на самом деле не удается найти раздел для привязки. FWIW, я тестировал этот код в своем собственном проекте и могу подтвердить, что он работает.
Проблема в том, что я пытался вызвать IOptions <MyType>, а не только MyType. Это решение в конечном итоге дает мне динамическое связывание, но оно больше не проходит через IOptions.
В любом случае вам не нужен IOptions. Единственная ценность - в IOptionsSnapshot, но я, к сожалению, не смог найти способ заставить это работать. Таким образом, вы не получите перезагрузку с помощью этого метода, но в зависимости от вашего приложения это может не иметь значения.
Если вы действительно этого хотите, вы можете использовать отражение для вызова services.Configure<TOptions>() с аргументом динамического универсального типа во время выполнения. Однако может быть более простой способ получить желаемый результат (используя IOptions<>), используя некоторую модификацию ответа Криса.
Используйте это так:
services.Configure(MyType, o => { var castObj = (MyType)o; section.Bind(castObj); });
using System;
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
namespace WebApplication1
{
public static class MyServiceExtensions
{
public static IServiceCollection Configure(this IServiceCollection services, Type type, Action<object> configureOptions)
{
// Static type that contains the extension method
var extMethodType = typeof(OptionsServiceCollectionExtensions);
// Find the overload for Configure<TOptions>(IServiceCollection, Action<TOptions>)
// This could be more specific to make sure that all type arguments are exactly correct.
// As it stands, this returns the correct overload but future updates to OptionsServiceCollectionExtensions
// may add additional overloads which will require this to be updated.
var genericConfigureMethodInfo = extMethodType.GetMethods()
.Where(m => m.IsGenericMethod && m.Name == "Configure")
.Select(m => new
{
Method = m,
Params = m.GetParameters(),
Args = m.GetGenericArguments() // Generic Type[] (ex [TOptions])
})
.Where(m => m.Args.Length == 1 && m.Params.Length == 2
&& m.Params[0].ParameterType == typeof(IServiceCollection))
.Select(m => m.Method)
.Single();
var method = genericConfigureMethodInfo.MakeGenericMethod(type);
// Invoke the method via reflection with our converted Action<objct> delegate
// Since this is an extension method, it is static and services is passed
// as the first parameter instead of the target object
method.Invoke(null, new object[] { services, configureOptions });
return services;
}
}
}
Я думаю, что ответ правильный, но это уж слишком, ха-ха. Я действительно надеялся, что есть встроенный способ, который я упускаю.
Вы можете найти более чистый способ внедрить настроенные параметры через IOptions, если вы посмотрели на реализацию .Configure<>() с открытым исходным кодом, но это решение должно работать нормально. Я не проводил никаких тестов с наследованием типов, поэтому может быть что-то упущено из виду
Я пробовал следовать вашему примеру, и он всегда дает мне нулевое значение. Я обновил свой вопрос кодом, который я пробовал на основе вашего примера. Вы знаете, не упустил ли я что-нибудь?