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•11mo ago
Message Not Public
Sign In & Join Server To View
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
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>();
Quoted by
<@689473681302224947> from #Library code review and contributions required (click here)
React with ❌ to remove this embed.
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
Kindly give half the time to the problem I've posted rather than wasting each other's time.
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
I am sorry for my comments above. I was just frustrated.
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
Yes
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
Yes, it's feature/di branch..
Thanks
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
No
I just changed the name of the functions in the SO post
AddMyApp means AddGemini
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
Thank you
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
Hi bro. Thanks for your help. Sorry, I couldn't reply earlier. I've been away.
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...
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:
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•11mo ago
Message Not Public
Sign In & Join Server To View
Thanks for your reply. I have removed IGeminiClient completely. I am just using GeminiClient.
Didn't understand this line.
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
least of my worries for now
answer this please
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
"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•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
should it be builder.Services.AddHttpClient<GeminiClient, CustomClient>
?
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
yeah I removed that interface..
it worked
just tried it
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
which one?
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
in case there is no Customclient, they would need to do .AddGemini<GeminiClient>
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
oh.. interesting
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
what do you recommend then?
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
didn't understand what you mean by one T extension
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
oh.. leave it for now.. The issue with .AddGemini().WithCustomClient<TClient> is that AddGemini still configures default HttpClient
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
public static IServiceCollection WithCustomClient<TClient>(this IServiceCollection services) where TClient : GeminiClient
{
AddGemini<TClient>(services);
return services;
}
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
you mean:
public static IServiceCollection AddGemini(this IServiceCollection services)
{
WithCustomClient<GeminiClient>(services);
return services;
}
?
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
TeBeCo
if you want to i'd suyggest to actually look at how aspnetcore does that for
AddControler().AddJson...
or the .Addhttpclient<>().Addhandler<T>
.....Quoted by
<@689473681302224947> from #Unable to configure values in DI using the extension method (click here)
React with ❌ to remove this embed.
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
any other library that use this? aspnet core source is a pain to look at..
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
FluentValidation and StringBuilder uses builder pattern as well.
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
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•11mo ago
Message Not Public
Sign In & Join Server To View
ok.. what's ovetler ?
Unknown User•11mo ago
Message Not Public
Sign In & Join Server To View
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...
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•11mo ago
Message Not Public
Sign In & Join Server To View
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.