C
C#11mo ago
Junaid Aslam

Unable to configure values in DI using the extension method

I'm having difficulty binding config values through... Details in the following SO question: https://stackoverflow.com/questions/77808154/unable-to-configure-services-using-the-extension-method-in-net
94 Replies
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
I have already shared the repo URL on the other thread and dropped multiple messages. As for "copying" names, I need to get this stuff working first. Refactoing always comes later.
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
MODiX
MODiX11mo ago
Junaid Aslam
Thanks. I have implemented as per your guideline and pushed to a branch here: https://github.com/jaslam94/Junaid.GoogleGemini.Net/tree/feature/di It would be really kind of you to go through it. Now when I try to use it in a web api, the Httpclient is empty with no baseaddress or request header. Here is how I am calling it there: builder.Services.AddGemini<GeminiClient, FooHttpClientOptions>(option => { option.Url = new Uri("https://generativelanguage.googleapis.com"); option.Credentials = new BasicAuthCredential { ApiKey = "xxxxxxxxxxxxxxxxxxxxxx" }; }); builder.Services.AddScoped<IGeminiClient, GeminiClient>(); builder.Services.AddScoped<ITextService, TextService>();
React with ❌ to remove this embed.
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
Correct this behavior. Is fixing the name even important? You have been taunting me in the other thread with your lols while I have been respectful.
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
Kindly give half the time to the problem I've posted rather than wasting each other's time.
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
I am thankful for the help you've done but people on the threads don't know where you have been. So just be polite. That's all. You can read all of my messages on the other thread. I have been super polite and respectful.
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
I am sorry for my comments above. I was just frustrated.
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
Yes
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
Yes, it's feature/di branch.. Thanks
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
I am passing the parameters through the action delegate as mentioned in my question. Pardon the late reply, it's over midnight here.
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
No I just changed the name of the functions in the SO post AddMyApp means AddGemini
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
Thank you
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
Hi bro. Thanks for your help. Sorry, I couldn't reply earlier. I've been away.
Junaid Aslam
Junaid AslamOP11mo ago
I have understood why it wasn't working in my case: https://stackoverflow.com/a/77812809/6181928
Stack Overflow
Unable to configure services using the extension method in .net
I have written a C# wrapper library that targets .net6,.net7,.net8. I want to add the DI support so that users can configure it by calling the services extension method AddMyApp. namespace Project1...
Junaid Aslam
Junaid AslamOP11mo ago
I am not using the interface IGeminiClient and it has started working. You have also gotten the GeminiClient from the pipeline in your sample project:
No description
Junaid Aslam
Junaid AslamOP11mo ago
Anyways, my code works now. I have one more request. In order to use a different HttpClient, this is how I am registering the service: builder.Services.AddTransient<ITextService, TextService>(_ => { return new TextService(new GeminiClient(GetCustomHttpClient())); }); Do you think this is right?
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
Thanks for your reply. I have removed IGeminiClient completely. I am just using GeminiClient. Didn't understand this line.
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
Understood bro. Thanks I should let the AddHttpClient handle the lifetime. I assume its Singleton with some underlying stuff related to how the object is disposed. But it feels like running in circles where if I allow the users to have their own httpclient (which I must), I need to inject httpclientfactory in extension method or geminiclient (httpclient helper) itself. I don't want to inject IServiceProvider in the GeminiClient. I want users to easily use and configure their own httpclients. also it can cause issues with sockets If I use typed clients then everytime a new typed client like GeminiClient would be made for each custom httpclient. Why re-write the entire client when all of the methods are the same? Sorry bro I have asked you too many questions. Thanks again for your help so far. @TeBeCo To allow consumers to use custom client, I am going ahead with typed client logic and doing this: public class CustomClient : GeminiClient { public CustomClient(HttpClient httpClient) : base(httpClient) { } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// builder.Services.AddTransient<GeminiAuthHandler<GeminiHttpClientOptions>>(); builder.Services.AddHttpClient<CustomClient>((sp, client) => { var options = sp.GetRequiredService<IOptions<GeminiHttpClientOptions>>().Value; client.BaseAddress = options.Url; }) .ConfigurePrimaryHttpMessageHandler(() => { var proxy = new WebProxy { Address = new Uri("http://localhost:1080/") }; var httpClientHandler = new HttpClientHandler { Proxy = proxy, UseProxy = true }; httpClientHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; return httpClientHandler; }) .AddHttpMessageHandler<GeminiAuthHandler<GeminiHttpClientOptions>>(); builder.Services.AddTransient<ITextService, TextService>((sp) => { var client = sp.GetRequiredService<CustomClient>(); return new TextService(client); }); Kindly check this and let me know.
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
But TextService takes GeminiClient as argument and if I don't instantiate like I have done here, it gives me "Unable to resolve service for type 'Junaid.GoogleGemini.Net.Infrastructure.GeminiClient' while attempting to activate 'Junaid.GoogleGemini.Net.Services.TextService'.)'" this is just for testing.. don't worry this would be for the consumer to do.. they can register typed client however they want..
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
least of my worries for now answer this please
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
I totally understand, this is something I'll work on next Also tried doing this to resolve GeminiClient: builder.Services.AddTransient<GeminiClient, CustomClient>(); ...but that didn't work either. it doesn't 🙂
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
"your lambda is literally what DI does already" my lambda returns CustomClient with TextService while if I don't use it then it tries to resolve GeminiClient bro.
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
then why is it giving me: Unable to resolve service for type 'Junaid.GoogleGemini.Net.Infrastructure.GeminiClient' while attempting to activate 'Junaid.GoogleGemini.Net.Services.TextService'.)
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
nope it has one ctor which takes GeminiClient as argument and CustomClient is derived from GeminiClient public class CustomClient : GeminiClient { public CustomClient(HttpClient httpClient) : base(httpClient) { } }
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
should it be builder.Services.AddHttpClient<GeminiClient, CustomClient> ?
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
yeah I removed that interface.. it worked just tried it
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
which one?
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
in case there is no Customclient, they would need to do .AddGemini<GeminiClient>
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
oh.. interesting
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
you're right. as always... I have a small confusion let me do this: .AddGemini().WithCustomClient<T>() and then I will get back to you
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
what do you recommend then?
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
cool. public static IServiceCollection AddGemini(this IServiceCollection services) { services.AddTransient<GeminiAuthHandler<GeminiHttpClientOptions>>(); services.AddHttpClient<GeminiClient>((sp, client) => { var options = sp.GetRequiredService<IOptions<GeminiHttpClientOptions>>().Value; client.BaseAddress = options.Url; }) .AddHttpMessageHandler<GeminiAuthHandler<GeminiHttpClientOptions>>(); AddGemini<GeminiClient>(services); return services; } private static IServiceCollection AddGemini<TClient>(this IServiceCollection services) { services.AddTransient<ITextService, TextService>(); services.AddTransient<IVisionService, VisionService>(); services.AddTransient<IChatService, ChatService>(); services.AddTransient<IEmbeddingService, EmbeddingService>(); services.AddTransient<IModelInfoService, ModelInfoService>(); return services; } check this kindly this is the base case: .AddGemini() I had a confusion that whether I should remove the AddHttpClient<GeminiClient> altogether so this is not a part of the AddGemini method at all or in WithCustomClient<TClient> I should remove the existing AddHttpClient?
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
public static IServiceCollection AddGemini(this IServiceCollection services) { AddGemini<GeminiClient>(services); return services; } private static IServiceCollection AddGemini<TClient>(this IServiceCollection services) where TClient : GeminiClient { services.AddTransient<GeminiAuthHandler<GeminiHttpClientOptions>>(); services.AddHttpClient<TClient>((sp, client) => { var options = sp.GetRequiredService<IOptions<GeminiHttpClientOptions>>().Value; client.BaseAddress = options.Url; }) .AddHttpMessageHandler<GeminiAuthHandler<GeminiHttpClientOptions>>(); services.AddTransient<ITextService, TextService>(); services.AddTransient<IVisionService, VisionService>(); services.AddTransient<IChatService, ChatService>(); services.AddTransient<IEmbeddingService, EmbeddingService>(); services.AddTransient<IModelInfoService, ModelInfoService>(); return services; } Like this ??
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
didn't understand what you mean by one T extension
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
oh.. leave it for now.. The issue with .AddGemini().WithCustomClient<TClient> is that AddGemini still configures default HttpClient
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
public static IServiceCollection WithCustomClient<TClient>(this IServiceCollection services) where TClient : GeminiClient { AddGemini<TClient>(services); return services; }
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
you mean: public static IServiceCollection AddGemini(this IServiceCollection services) { WithCustomClient<GeminiClient>(services); return services; } ?
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
MODiX
MODiX11mo ago
TeBeCo
if you want to i'd suyggest to actually look at how aspnetcore does that for AddControler().AddJson... or the .Addhttpclient<>().Addhandler<T> .....
React with ❌ to remove this embed.
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
any other library that use this? aspnet core source is a pain to look at..
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
FluentValidation and StringBuilder uses builder pattern as well.
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
hmm.. I'll take a look. For now I released what I have done so far.. doing it with builder pattren would be the next release thanks for your help so far @TeBeCo a small improvement: public static IServiceCollection AddGemini(this IServiceCollection services,Func<HttpClientHandler>? proxyHandler=null) { return RegisterCoreServices<GeminiClient>(services, proxyHandler); } public static IServiceCollection AddGemini<TGeminiClient>(this IServiceCollection services,Func<HttpClientHandler>? proxyHandler=null) where TGeminiClient : class, IGeminiClient { return RegisterCoreServices<TGeminiClient>(services, proxyHandler); } private static IServiceCollection RegisterCoreServices<TGeminiClient>(IServiceCollection services, Func<HttpClientHandler>? proxyHandler) where TGeminiClient : class, IGeminiClient { services.AddTransient<GeminiAuthHandler<GeminiHttpClientOptions>>(); var httpClientBuilder = services.AddHttpClient<IGeminiClient, TGeminiClient>((IServiceProvider serviceProvider, HttpClient client) => { var options = serviceProvider.GetRequiredService<IOptions<GeminiHttpClientOptions>>().Value; client.BaseAddress = options.Url; }); if (proxyHandler is not null) { httpClientBuilder.ConfigurePrimaryHttpMessageHandler(proxyHandler); } httpClientBuilder.AddHttpMessageHandler<GeminiAuthHandler<GeminiHttpClientOptions>>(); services.AddTransient<ITextService, TextService>(); services.AddTransient<IVisionService, VisionService>(); services.AddTransient<IChatService, ChatService>(); services.AddTransient<IEmbeddingService, EmbeddingService>(); services.AddTransient<IModelInfoService, ModelInfoService>(); return services; } What do you think about it?
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
yeah.. actually someone wants to contribute to my repo and they sent me this.. the only use case they fixed here is httpClientHandler being injected and being optional i think I am missing something.. you told me about using Builder and that I should check how its done in httpclient
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
I did not understand bro The consumer is not adding HttpClient, it's the extension method, that's doing so they will just call AddGemini or call AddGemini(()=> {... handler});
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
bro this is the bit that can you explain more or direct me to some doc where I can read about it like Func<HttpClientHandler>? proxyHandler=null param breaks DI?
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
Ok.. but to add anything to DI its instance is made, no? So if the HttpClientHandler is not in DI and I am sending it to the ext method as Func<HttpClientHandler>, how's is that a bad thing? Where should this be done? Inside AddHttpClient(....) ??
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
ok.. what's ovetler ?
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP11mo ago
Code Review Stack Exchange
How can I design the configuration of the library with Typed HttpCl...
I have written a .Net Client wrapper for Google Gemini (Generative AI) REST API and published it as a Nuget package. I am seeking a review of the code. I have spent quite some time in designing how...
Junaid Aslam
Junaid AslamOP11mo ago
and the first answer says "GeminiAuthHandler can be omitted as you can use HttpClient.DefaultRequestHeaders either from the instance or from the factory."
Unknown User
Unknown User11mo ago
Message Not Public
Sign In & Join Server To View
Junaid Aslam
Junaid AslamOP10mo ago
I agree. Would you please take a look at their suggestion about refactoring the services to GeminiService? It looked good to me but just need a second opinion from you.
Want results from more Discord servers?
Add your server