C
C#•2mo ago
Vortac

Using ` Microsoft.Extensions.DependencyInjection` in a library

I'm currently writing a wrapper around a REST API and would like to use Microsoft's DI library (normally I just use constructor injection) but I'm a bit confused as to how to do so. I've created a ServiceCollectionExtensions class and registered the services that I need:
public static class AirplanesLiveServiceCollectionExtensions
{
public static IServiceCollection AddAirplanesLiveClient(this IServiceCollection services)
{
services
.AddRefitClient<IAirplanesLiveApi>()
.ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com"));

return services.AddTransient<AirplanesLiveClient>();
}
}
public static class AirplanesLiveServiceCollectionExtensions
{
public static IServiceCollection AddAirplanesLiveClient(this IServiceCollection services)
{
services
.AddRefitClient<IAirplanesLiveApi>()
.ConfigureHttpClient(c => c.BaseAddress = new Uri("https://api.example.com"));

return services.AddTransient<AirplanesLiveClient>();
}
}
However, I'm not sure what I need to do next. Do I then use this somehow in Program.cs of an example project which uses the library?
33 Replies
Unknown User
Unknown User•2mo ago
Message Not Public
Sign In & Join Server To View
Vortac
VortacOP•2mo ago
I inject the client itself? Or the client given by DI? Also, what do I pass as IServiceCollection?
Unknown User
Unknown User•2mo ago
Message Not Public
Sign In & Join Server To View
MODiX
MODiX•2mo ago
If your code is too long, you can post to https://paste.mod.gg/, save, and copy the link into chat for others to see your shared code!
Vortac
VortacOP•2mo ago
I'm familiar with it as a concept and how its a solution to the IoC problem. Normally I do ctor injection, ie:
var rom = RomLoader.Load();
var audioEngine = new AudioEngine();
var display = new Display();
var emulator = new Emulator(rom, audioEngine, display);
var rom = RomLoader.Load();
var audioEngine = new AudioEngine();
var display = new Display();
var emulator = new Emulator(rom, audioEngine, display);
I've used Autofac in the past Which code? IAirplanesLiveApi and AirplanesLiveClient?
Unknown User
Unknown User•2mo ago
Message Not Public
Sign In & Join Server To View
Vortac
VortacOP•2mo ago
BlazeBin - wtmwhkaeczmb
A tool for sharing your source code with the world!
Vortac
VortacOP•2mo ago
BlazeBin - vszuikjazrsc
A tool for sharing your source code with the world!
Unknown User
Unknown User•2mo ago
Message Not Public
Sign In & Join Server To View
Vortac
VortacOP•2mo ago
into which ctor? I thought I needed to add IAirplanesLiveApi into the AirplanesLiveClient ctor as an argument Right now I just set the value directly in the ctor instead of injecting it, but I'm doing that because I was working off the simple sample that Refit provides @viceroypenguin I'm also confused because in your example, it seems the BaseAddress is set twice? https://github.com/viceroypenguin/AlphaVantage.Net/blob/acc24936b7eeb8249a5a9f753f8b03c1c0043346/Source/AlphaVantage.Net/AlphaVantageServiceCollectionExtensions.cs#L86 https://github.com/viceroypenguin/AlphaVantage.Net/blob/acc24936b7eeb8249a5a9f753f8b03c1c0043346/Source/AlphaVantage.Net/AlphaVantageClient.cs#L63 This is what I have so far: https://github.com/markjamesm/AirplanesLive.NET/tree/main The project is working at a barebones level (I need to add things like proper type handling, rate limiting, etc), but I'm confused regarding the DI portion
Unknown User
Unknown User•2mo ago
Message Not Public
Sign In & Join Server To View
viceroypenguin
viceroypenguin•2mo ago
@Vortac @TeBeCo I wanted AlphaVantageClient to be a type that could be used without DI as well, so the public AlphaVantage() constructor does not take an IAlphaVantageApi, it creates one from scratch using fixed configuration. This way, one could us the AlphaVantageClient in a linqpad script without having to set up a DI. just pass the api key and a config value and th rest gets handled automaticlly. this constructor should not be called often, but may be desired from time to time. the other constructor, the internal one, is the one that is called by the DI system, with a refit client that is configured in the servicecollectionextensions file the reason i do the duality is to cover potential future case where the refit client cannot be consumed directly. i've encountered this before, in cases where certain authntication mechanisms or certain multi-call apis are not really possible via delegatinghandler or some other internal control. so by internalizing the refit client, i have the freedom to change it in the future if necessary, while the public client is concrete and can be consumed directly. meh, it's actually helpful to me so taht I knwo which specific questions to answer. allowed me to skim the convo quicker. 🙂
Vortac
VortacOP•2mo ago
Testing using just HTTPClient, the responses are returning the correct values instead of 0 with the Refit library Not sure what was going wrong
viceroypenguin
viceroypenguin•2mo ago
can you trim down to a specific case and show both the working httpclient code and the non-working refit code?
Vortac
VortacOP•2mo ago
The main chat channel recommended I switch to plain HTTPClient So I tried it as a test since I wasn't finding any obvious issues Refit was returning some of the properties correctly, and some as 0 I took a look at the object in the debugger
viceroypenguin
viceroypenguin•2mo ago
i'll take a look in a bit. am distracted atm
Vortac
VortacOP•2mo ago
No worries
viceroypenguin
viceroypenguin•2mo ago
oooooh. you screwed up the models the [AliasAs] is only for query parameters and form parameters on things sent to the server as part of the request the json that comes back runs through STJ, the same as for plain httpclient so the models need to be annotated with [JsonPropertyName], same as they do in the httpclient version @Vortac
Vortac
VortacOP•2mo ago
ahh It only affected some properties and not others which is what confused me there was no explicit error
viceroypenguin
viceroypenguin•2mo ago
well keep in mind that STJ automatically translates camelCase in the json to PascalCase in the C# model for you, so any properties that match in everything except case of the first char will translate automatically
Vortac
VortacOP•2mo ago
Is it a big deal to introduce Refit as a dependency in the library? I have both methods working now (Refit and plain HTTPClient) and just want to confirm
viceroypenguin
viceroypenguin•2mo ago
i don't think so. i don't have a problem introducing things like refit into my library as a required dependency. it's how i wrote my library. if consumers don't like it, they can write their own.
Vortac
VortacOP•2mo ago
Thanks for all the help btw, this is much appreciated
viceroypenguin
viceroypenguin•2mo ago
there are several schools of thought on transitive dependencies * some people like to operate with absolutely no transitive dependencies. my library should stand on it's own, with only the bcl as a dependency * others introduce any dependency they want with no care to what's necessary * my philosphy is to limit dependencies, but also recognize that my time in building something is precious, so if it makes my life easier in building the library, then i'll add it. if consumers don't like it, they can spend the time writing their own. in the case of an api client, refit is absolutely a time-saver; the alternative is to use an openapi generator (if an openapi spec is available). most of the time, however, the code provided by such a generator is crap, so refit is my choice.
Vortac
VortacOP•2mo ago
The only thing I can think of regarding transitive dependencies is that some places need legal to review the code prior to using it, and that includes libraries. But that isn't a concern for this API
viceroypenguin
viceroypenguin•2mo ago
which again, falls into the "not my problem" bucket i don't write something to solve every problem everyone has ever. or to address things for every potential consumer. i write things to make my life easier, and if i can do so in a way that may help others, then i do so. it's up to them to decide if it actually makes their life easier (and legal may be a concern that makes it not easier for them). but i know that there are companies that don't allow any non-microsoft libraries due to legal. so why would i try to concern myself with that scenario? i write my stuff MIT license, and i've done interviews with companies before for legal and/or regulatory compliance, so i don't mind working with potential consumers. but i'm not going to concern myself with it unless they come to me with a concern and proposal because unless they pay me, they don't get to choose how i spend my time, and that includes how i write a library
Vortac
VortacOP•2mo ago
With API limits of
Free:
500 requests per day.
If split across the entire day, that's 1 request every ~180 seconds.

Contributor:
8,640 requests per day.
If split across the entire day, that's 1 request every 10 seconds.
Free:
500 requests per day.
If split across the entire day, that's 1 request every ~180 seconds.

Contributor:
8,640 requests per day.
If split across the entire day, that's 1 request every 10 seconds.
Am I better off setting rate limits by minutes, or a day? Currently the API's limit is 1 request/sec but that's changing in a month so I was initially going to go per minute 1 req/s with no limit
viceroypenguin
viceroypenguin•2mo ago
you could do by day, but know that people who run short-form apps (cli, etc.) are going to get absolutely fucked by that i'd do it by minute or by hour. esp. if it's configurable; if someone knows what they're doing, they can jack it up for a thing they're doing. this also gets into the two ways to configure your lib; one with defaults and one where they provide their own configuration of ihttpclient
viceroypenguin
viceroypenguin•2mo ago
correct. the public constructor is for when someone wants to run a quick app, and doesn't want to build a full DI setup to get a single thing to go do a thing. like in a linqpad script the internal one is the one registered with DI and receive the configured httpclient

Did you find this page helpful?