-
-
Notifications
You must be signed in to change notification settings - Fork 222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Правильное использование DI для VkApi объекта #1578
Comments
Если коротко, то ваше решение ужасно. Вы не понимаете смысл контейнера зависимостей. Его суть в том, что он забирает на себя разрешение всех зависимостей в приложении. А в приведенном вами коде вы пытаетесь зарезолвить что то сами, да еще и в каждом скоупе создаете дополнительные Я не пытаюсь принизить, отнюдь. Просто покажу как должно быть правильно. Фактически public class A
{
public A(B b, C c)
{
// ...
}
} Если мы хотим public static void AddA(
this IServiceCollection services)
{
services.AddScoped<A>();
services.AddScoped<B>();
services.AddScoped<C>();
} Иногда бывает такое, что public class A
{
public A(B b, C c, string name)
{
// ...
}
} В таких случаях можно использовать фабрику: public static void AddA(
this IServiceCollection services)
{
services.AddScoped<B>();
services.AddScoped<C>();
services.AddScoped<A>(provider =>
new A(provider.GetRequiredService<B>(), provider.GetRequiredService<C>(), "CONST NAME")); // Или через ActivatorUtilities
} Здесь стоит оговориться, что фабрики вообще очень мощный инструмент для передачи дополнительных параметров в контейнере зависимостей. Погуглите использование фабрик в После того, как все зависимости будут зарегистрированы в контейнере зависимостей, собирается Из всего вышесказанного делаем вывод, что В вашем случае код должен быть примерно такой (я не вникал [и не хочу] в суть ваших зависимостей): public static void AddVkontakteClient<TProxyProvider, TCaptchaSolver>(
this IServiceCollection services,
Action<VkontakteApiOptions> configure)
where TProxyProvider : class, IProxyProvider
where TCaptchaSolver : class, ICaptchaSolver
{
services.Configure(configure);
services.AddScoped<IVkontakteApiClient, VkontakteApiClient>();
services.AddSingleton(typeof(IProxyProvider), typeof(TProxyProvider));
services.AddSingleton<ProxyHttpHandler>();
services
.AddHttpClient<IRestClient, RestClient>()
.ConfigurePrimaryHttpMessageHandler<ProxyHttpHandler>();
services.AddAudioBypass();
services.AddSingleton(typeof(ICaptchaSolver), typeof(TCaptchaSolver));
var vkApi = new VkApi(services);
services.AddSingleton(vkApi);
} Соответственно, когда вам понадобится public class A
{
public A(VkApi apiClient)
{
// apiClient уже имеет TCaptchaSolver, AudioBypass и все, что вы ему там нарегистрировали.
}
} |
Мало того, вы показали пример, как зарегистрировать VkApi как синглтон, что на мой взгляд неверно, так как внутри используется IRestClient, у которого я переопределил регистрацию с singleton на transient за счет такой регистрации:
но получается за счет того, что вы регаете VkApi как синглетон, то и IRestClient в нем будет ТОЖЕ как синглетон, что не очень хотелось бы. Поправьте если я не прав |
Согласен,
Вы не правы.
Не нужно вообще думать о том, как что то внешнее реализовано. Вы должны ориентироваться только на публичное API. Представьте, что у вас нет исходников |
Но как RestClient не будет синглетоном, если VkNet его в зависимостях принимает?)) VkNet имеет один экземпляр на все приложение, а значит зависимость IRestClient внутри него будет ТОЖЕ на все приложение |
А, я понял что вы имеете ввиду. Нет, В общем, что мешает сделать так? services.AddScoped(provider => new VkApi(services));
// Или если версия .NET больше 6-ой: services.AddScoped(_ => new VkApi(services)); |
мешает то, что так не выйдет. а не выходит из-за того, что внутри VkApi зачем-то билд идет в IServiceProvider этой коллекциию. |
Нет, вы не правы. Приложение не уронит, но вот то что экземпляр RestClient будет один единственный на такой вот VkApi - это 100% |
Не проще ли просто взять, и сделать НОРМАЛЬНЫЙ DI для вашего VkApi? |
может, кому-то поможет обойти текущие ограничения. я пока вот таким костылем обошелся: сделал сервис который возвращает инстанс VkApi, и позволяет использовать DI-контейнер приложения. сам сервис регаю как singleton. понятно что инстанс надо кэшить и все такое, тут только пример инициализации public class VkApiProvider(IServiceProvider serviceProvider)
{
/// <summary>
/// Hack for VkApi to use existing services
/// </summary>
/// <remarks>
/// * Will break if VkNet.AudioBypassService and VkNet assemblies have different versions<br/>
/// * Works only because VkNet constructor inits stuff once and then service provider field is not used anywhere
/// </remarks>
public VkApi GetVkApi()
{
var instance = new VkApi();
instance.GetType().GetTypeInfo().GetDeclaredMethod("Initialization")!.Invoke(instance, [serviceProvider]);
return instance;
}
} фокус в том, что service collection внутри VkApi никак не используется, а инициализацию можно безболезненно повторить после вызова конструктора, уже со своим service provider |
Я написал сервис VkApiManager с методами GetInstace внутри которых ручками создаётся VkApi и все нужные зависимости я уже получаю от ServiceProvider и кладу туда (например CaptchaSolver, логгер) |
Здравствуйте
Пилю сейчас проект с использованием вашей библиотеки (спасибо за проект).
У меня есть вопрос, касаемо DI, который я не могу понять как правильно решить. Вся проблема заключается в том, что VkApi принимает "почему-то" в параметрах IServiceCollection.
У меня есть вот такой вот код:
Он регистрирует VkontakteApiClient как scoped, при этом VkontakteApiClient принимает у меня VkApi тоже как scoped.
Я хочу узнать два момента:
public static void AddVkontakteClient<TProxyProvider, TCaptchaSolver>
не очень хочется (так как он там не нужен)The text was updated successfully, but these errors were encountered: