Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Post
  • Reply
NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

pointsofdata posted:

I don't understand the theory, but my question is: would typeclasses give us discriminated unions in c#?

Nope, totally unrelated. DUs are about the shape of data, typeclasses are about what functions exist that operate on that data.

Adbot
ADBOT LOVES YOU

Cuntpunch
Oct 3, 2003

A monkey in a long line of kings

mystes posted:

Someone on Reddit a couple months ago claimed that they didn't want to try to add typeclasses to f# because there was a distinct possibility they were actually going to get added to .net in general.

https://github.com/fsharp/fslang-suggestions/issues/243#issuecomment-282480054
Along with other relevant stuff in this years-old comment thread.

F# has been burned a number of times by being first-to-implement, even without 'built-in' CLR support - at which point C# comes along later, drags the CLR team with them, and then F# needs to worry about interop/compatibility as well as not breaking backwards compatability for itself. See: Tuples, Async, Records.

Along with that - SRTP in F# is already most of the way to doing a lot of typeclass-y stuff, even if the syntax is obtuse to a fault.

mystes
May 31, 2006

Cuntpunch posted:

https://github.com/fsharp/fslang-suggestions/issues/243#issuecomment-282480054
Along with other relevant stuff in this years-old comment thread.

F# has been burned a number of times by being first-to-implement, even without 'built-in' CLR support - at which point C# comes along later, drags the CLR team with them, and then F# needs to worry about interop/compatibility as well as not breaking backwards compatability for itself. See: Tuples, Async, Records.

Along with that - SRTP in F# is already most of the way to doing a lot of typeclass-y stuff, even if the syntax is obtuse to a fault.
It makes sense not to implement an incompatible version just for f# if it might get added to c# later, but I wish there was a way to get a spec agreed to for both languages even if c# didn't have immediate plans to support it, or something like that.

Cuntpunch
Oct 3, 2003

A monkey in a long line of kings

mystes posted:

It makes sense not to implement an incompatible version just for f# if it might get added to c# later, but I wish there was a way to get a spec agreed to for both languages even if c# didn't have immediate plans to support it, or something like that.

The trouble is that you also end up on different timelines, and also *outlooks* on things. In that blog post mentioned, there's this chain of discussion from a veteran in the F# community: https://devblogs.microsoft.com/dotnet/preview-features-in-net-6-generic-math/#comment-9927

There is, at times, what seems to be a mismatch between outlooks - another great read on where C# jumped the gun and F# kind of suffers from interop concerns is around default interface methods - not even a feature that F# wants to implement. https://github.com/dotnet/csharplang/issues/288#issuecomment-288114545

So taking these two very different languages, paradigms, and outlooks - and then trying to plan on something today that C# may (never?) implement is going to be tricky to get their buy-in on...today.

redleader
Aug 18, 2005

Engage according to operational parameters
remember that "clr" stands for "c# language runtime"

Ireland Sucks
May 16, 2004

What's the good way of doing platform independent, writable-at-runtime program state/settings?

I've built my program around using the app.config/ConfigurationManager, which is a bit clunky with the conversions but it worked until I came across this issue where standalone exes don't work well with it.

Literally everyone says app.config is old and crap so I start changing everything over to using appsettings.json/JsonConfigurationProvider and oh - great - turns out that's readonly.

It looks like there are workarounds for both systems to make them do what I want, but is there a better way? Maybe a third party nuget configuration package that everyone loves?

I've been using the C# 8 in a nutshell book which has been brilliant for steering me away from obsolete patterns i've picked up from stackoverflow, but it says nothing about configuration storage.

insta
Jan 28, 2009
I just went through this!

The Configuration system in Core (and above) is a hierarchical, multi-layered providers-based approach. All configuration key/values are ultimately turned into a colon-separated key, with a value. So, the following JSON becomes:

code:
{
   "auth" : {
      "keys" : {
          "prod" : "abc123"
       },
       "lifespan" : {
           "passwords" : "30",
           "tokens" : "3600"
       }
   }
}

"auth:keys:prod" : "abc123",
"auth:lifespan:passwords" : "30",
"auth"lifespan:tokens" : "3600"
What's important here is the second part, the colon-separated strings. If you override a ConfigurationSource, you can call "Set" yourself and plop arbitrary config values into the config hierarchy. We do this at work to load things from a database (in a config that does not understand proper hierarchy). We also hardcode defaults in one provider, read from appsettings.json, read from the DB, and read from multiple XML files.

So, if you have multiple sources, what gets updated on Save?

Mr Shiny Pants
Nov 12, 2012

Ireland Sucks posted:

What's the good way of doing platform independent, writable-at-runtime program state/settings?

I've built my program around using the app.config/ConfigurationManager, which is a bit clunky with the conversions but it worked until I came across this issue where standalone exes don't work well with it.

Literally everyone says app.config is old and crap so I start changing everything over to using appsettings.json/JsonConfigurationProvider and oh - great - turns out that's readonly.

It looks like there are workarounds for both systems to make them do what I want, but is there a better way? Maybe a third party nuget configuration package that everyone loves?

I've been using the C# 8 in a nutshell book which has been brilliant for steering me away from obsolete patterns i've picked up from stackoverflow, but it says nothing about configuration storage.

For Containers I use the Environment stuff. For my own little projects I just create a small object that holds my configuration data, set some sane defaults, and I serialize this to disk as a JSON file, next time the program runs I deserialize it from disk.

It is pretty simple but works pretty much every time.

Edit: Added bonus: if NewtonSoft can deserialize/serialize it, you can pretty much use anything you want in the file and it is pretty easy to use a text editor to add data without needing to recompile.
It might not be "the" way but I find it works well.

Mr Shiny Pants fucked around with this message at 08:00 on Sep 6, 2021

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

If it's writable at runtime it's not really 'application configuration' in the way that most developers use the term, and if you go looking for that you'll get unhelpful results (like appsettings.json, environment variables, CLI arguments etc... all of which are read-only and/or ephemeral, on purpose).

If it's writable at runtime it's application state, and it's also user data, so you want to search for 'how do I persist a small amount of data'. The answer is likely to be a simple JSON file (overwrite/reload on save) or, once the data grows enough, a SQLite database. And you want to save them in a user-owned folder if different users on the same machine are supposed to be able to have their own configuration each.

Ireland Sucks
May 16, 2004

Thanks, yeah - it looks like JSON deserialisers are good enough to make this relatively painless so I'll do something custom.

Boz0r
Sep 7, 2006
The Rocketship in action.
I have an Order class with a list of a bunch of specific Items. Some of these items are mandatory, some are limited 1 one, and some have no limit, and we have a concrete subclass for each of these items. How would you enforce these rules in code?

One of my coworkers suggested adding an enum to each class, but I don't like mixing object data with class data like that.

Hammerite
Mar 9, 2007

And you don't remember what I said here, either, but it was pompous and stupid.
Jade Ear Joe

Boz0r posted:

I have an Order class with a list of a bunch of specific Items. Some of these items are mandatory, some are limited 1 one, and some have no limit, and we have a concrete subclass for each of these items. How would you enforce these rules in code?

One of my coworkers suggested adding an enum to each class, but I don't like mixing object data with class data like that.

Not sure I really understand what you're describing. Your Order class has a property which is a List<OrderItem>? Are the types of OrderItem that exist fixed (changing it requires a new version of the software), or do users of the system have to be able to add more?

You could make OrderItem an abstract class and require concrete subclasses to have a ValidateOrder() method which validates a list of OrderItems, I guess. Then you can validate an Order's list of OrderItems by iterating over the list and calling ValidateOrder() on each item.

That doesn't help you with mandatory items (i.e. the list is invalid if it doesn't contain any items of type X). You could handle that by adding another validation step which checks for the absence of required items. Or instead of iterating over the items, you could iterate over the types of items that exist and validate one type at a time. The types of item that exist might be stored in a database, or it might be stored in a list that is automatically populated using reflection, or it might be hardcoded (in which case you would have to remember to update the hardcoded list whenever you add a new one, which is a potential source of bugs).

What do you mean by "add an enum to each class"? What does this putative enum type represent, what are its members? What do you mean when you say "mixing object data with class data"? You mean you don't like objects having properties that are supposed to depend only on the class, i.e. are the same for each object belonging to a specific class?

You need to explain your constraints a bit better

mystes
May 31, 2006

It seems like it might be easier to do this without a new class for each item type by just having a single class (possibly with the actual item type as a type parameter) that you instantiate once for each item type and configure with the minimum/maximum number allowed and other validation requirements and then either call a factory method that returns an actual container instance or you just call a method to validate a list of the actual item type.

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Enforcing the "at most one" requirement is easy enough - just have a UniqueItem subclass and a NonUniqueItem subclass. Your Order class will have a HashSet<UniqueItem> (with the comparison key being the product ID or some such) and a regular List<NonUniqueItem>, plus a read-only property exposing an IEnumerable<Item> consisting of the union of the two properties.

The "at least one" requirement is more complicated, because how do you guarantee that you know all the MandatoryItem instances that exist? What should happen if somebody creates a new one while you're creating the Order?

Without more context, I would pass the list of currently-defined MandatoryItems to the constructor of the OrderFactory class. Then every Order created via OrderFactory.Create() will already contain one of each mandatory item, by default and without the option to remove them. Whenever somebody flags or unflags a product as mandatory, update or replace the OrderFactory.

Cuntpunch
Oct 3, 2003

A monkey in a long line of kings

Boz0r posted:

I have an Order class with a list of a bunch of specific Items. Some of these items are mandatory, some are limited 1 one, and some have no limit, and we have a concrete subclass for each of these items. How would you enforce these rules in code?

One of my coworkers suggested adding an enum to each class, but I don't like mixing object data with class data like that.

As a starting point, I'd simply maintain an enumerable consisting of a bunch of Predicate<Order> lambdas. When the time comes to validate an order, run it through all of the predicates to ensure all return true.
Have a rule that says "No more than 3 Widgets"? That's cool:
code:
public bool MaxWidgets(Order o) => o.LineItems.Where(li => li.ProductId == "Widget").All(widget => widget.Quantity <= 3);
Slap it into your generalized rules list and you're off to the races. Bonus points because you can encode almost anything you want in a single rule - and have as many of these sort of rules as you want without ever needing to actually dick around in your class hierarchy or think hard about interface definitions.


To expand on this thought process: Mixing it into the types around products and orders themselves seems awkward and misplaced abstraction. A product exists in the world without any context that it is part of an order. an order exists as a tally of things. It already mostly makes sense to combine these together via a third thing: a LineItem - such that a raw product(id, manufacturer, price, etc) and order-specific details about it (quantity) can be composed. Trying to encode rules into line items (either via a predicate approach as above, or via subclasses) seems generally more awkward, because it isolates the concept of validity of an order solely to its lineitems, and not to other (plausibly) useful data that's already associated to the order as well - consider that there's no good way in this approach to encode "You can have one or the other, but not both of these on an order" or other outside-the-context-of-a-single-line-item rules: and that goes for trying to do something with Predicate<LineItem> as well.

Cuntpunch fucked around with this message at 21:21 on Sep 12, 2021

Just-In-Timeberlake
Aug 18, 2003
I feel like I'm missing something simple here, but not sure what. I'm trying to use an online service to convert HTML to PDF, and then return a base64 encoded string of the pdf. I can make the call fine, but when I put the returned base64 string into a decoder, I'm only getting like the first 20% of the pdf, the rest is blank. Using Postman and hitting the HtmlToPdf api directly returns the full pdf. Looking at the base64 string, there's a section of "AAAAAAAAAA......etc" in there, so obviously something is going wrong in the encoding. I suspect it's that I'm encoding the entire response and not just the .pdf part, but I'm not sure, or how to get just that part. Here's the code for the call:

code:
internal async Task<string> ConvertHtmlToPdfAsync(string html)
        {
	
	    string apiKey = "key here";

            try
            {
                byte[] dataStream = Encoding.UTF8.GetBytes($"html={html}");
                
                var httpWebRequest = (HttpWebRequest)WebRequest.Create("https://htmlpdfapi.com/api/v1/pdf");
                
                httpWebRequest.ContentType = "application/json";
                httpWebRequest.Headers.Add("Authentication", apiKey);
                httpWebRequest.Method = "POST";
                httpWebRequest.ContentType = "application/x-www-form-urlencoded";
                httpWebRequest.ContentLength = dataStream.Length;

                using (Stream newStream = await httpWebRequest.GetRequestStreamAsync()) {
                    newStream.Write(dataStream, 0, dataStream.Length);
                };

                var response = await httpWebRequest.GetResponseAsync();
                
                byte[] bytes;
                using (var memoryStream = new MemoryStream())
                {
                    response.GetResponseStream().CopyTo(memoryStream);
                    bytes = memoryStream.ToArray();
                }

                return Convert.ToBase64String(bytes);

            } catch (Exception ex)
            {
                return "";
            }
            
        }

Bruegels Fuckbooks
Sep 14, 2004

Now, listen - I know the two of you are very different from each other in a lot of ways, but you have to understand that as far as Grandpa's concerned, you're both pieces of shit! Yeah. I can prove it mathematically.

Just-In-Timeberlake posted:

I feel like I'm missing something simple here, but not sure what. I'm trying to use an online service to convert HTML to PDF, and then return a base64 encoded string of the pdf. I can make the call fine, but when I put the returned base64 string into a decoder, I'm only getting like the first 20% of the pdf, the rest is blank. Using Postman and hitting the HtmlToPdf api directly returns the full pdf. Looking at the base64 string, there's a section of "AAAAAAAAAA......etc" in there, so obviously something is going wrong in the encoding. I suspect it's that I'm encoding the entire response and not just the .pdf part, but I'm not sure, or how to get just that part. Here's the code for the call:

code:
...
                var response = await httpWebRequest.GetResponseAsync();
                
                byte[] bytes;
                using (var memoryStream = new MemoryStream())
                {
                    response.GetResponseStream().CopyTo(memoryStream);
                    bytes = memoryStream.ToArray();
                }

                return Convert.ToBase64String(bytes);


Think you're missing a ReadToEnd()...

Just-In-Timeberlake
Aug 18, 2003
I rewrote it using HttpClient and it works now, go figure

code:
internal async Task<string> ConvertHtmlToPdfAsync(string html)
        {
            string apiKey = "key";

            try
            {
                using (HttpClient httpClient = new HttpClient())
                {
                    httpClient.DefaultRequestHeaders.Add("Authentication", apiKey);

                    var response = await httpClient.PostAsync("https://htmlpdfapi.com/api/v1/pdf", 
                        new FormUrlEncodedContent(new[]
                            {
                                new KeyValuePair<string, string>("html", html)
                            }
                        ));

                    return Convert.ToBase64String(await response.Content.ReadAsByteArrayAsync());

                }
                
            } catch (Exception ex)
            {
                return "";
            }
            
        }

mystes
May 31, 2006

It might be an issue with gzip compression. Some of the older stuff doesn't handle that automatically by default.

Mr Shiny Pants
Nov 12, 2012

mystes posted:

It might be an issue with gzip compression. Some of the older stuff doesn't handle that automatically by default.

This is probably it. You will need to run it trough a stream that can read a ZIP stream.

Missing a Flush() at the end of copying it to a memorystream.

Dog on Fire
Oct 2, 2004

Does anyone know of a good way to call JavaScript from .NET Core? Specifically, I'd like my ASP.NET service to call a function in an npm package and run it on the back-end.

I'm trying to get it work using INodeServices, but it seems that this requires the JavaScript function to have a callback function as an argument. However, this JavaScript that I'm trying to use does not take a callback – it only returns a Promise.

I could ask the developers of the npm to add a function that takes that callback, but could I solve it on my service without bothering them?

Also, my ASP.NET service is only an API and doesn't have any HTML so I've avoided Blazor, but if that's the best way to get this working then I'd be fine with giving it a go. I'm also now trying the SpaServices extensions that the MS page is talking about, but it seems to not really have any documentation at all. Quite a bizarre situation.

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Dehumanize yourself and face to writing a trivial, internal-only express.js microservice that exposes the npm functions you want as HTTP endpoints.

mystes
May 31, 2006

Dog on Fire posted:

Does anyone know of a good way to call JavaScript from .NET Core? Specifically, I'd like my ASP.NET service to call a function in an npm package and run it on the back-end.

I'm trying to get it work using INodeServices, but it seems that this requires the JavaScript function to have a callback function as an argument. However, this JavaScript that I'm trying to use does not take a callback – it only returns a Promise.

I could ask the developers of the npm to add a function that takes that callback, but could I solve it on my service without bothering them?

Also, my ASP.NET service is only an API and doesn't have any HTML so I've avoided Blazor, but if that's the best way to get this working then I'd be fine with giving it a go. I'm also now trying the SpaServices extensions that the MS page is talking about, but it seems to not really have any documentation at all. Quite a bizarre situation.
Can't you just write a trivial javascript wrapper function? Also if INodeServices doesn't work for some reason I guess you could just run it as a process and get the result via stdout?

Dog on Fire
Oct 2, 2004

mystes posted:

Can't you just write a trivial javascript wrapper function? Also if INodeServices doesn't work for some reason I guess you could just run it as a process and get the result via stdout?

I don't know why I didn't think of the JS wrapper function, thanks!

Nth Doctor
Sep 7, 2010

Darkrai used Dream Eater!
It's super effective!


Just-In-Timeberlake posted:

I rewrote it using HttpClient and it works now, go figure

code:
internal async Task<string> ConvertHtmlToPdfAsync(string html)
        {
            string apiKey = "key";

            try
            {
                using (HttpClient httpClient = new HttpClient())
                {
                    httpClient.DefaultRequestHeaders.Add("Authentication", apiKey);

                    var response = await httpClient.PostAsync("https://htmlpdfapi.com/api/v1/pdf", 
                        new FormUrlEncodedContent(new[]
                            {
                                new KeyValuePair<string, string>("html", html)
                            }
                        ));

                    return Convert.ToBase64String(await response.Content.ReadAsByteArrayAsync());

                }
                
            } catch (Exception ex)
            {
                return "";
            }
            
        }

IIRC, HttpClient is intended to be used as a singleton.

Toast Museum
Dec 3, 2005

30% Iron Chef

Nth Doctor posted:

IIRC, HttpClient is intended to be used as a singleton.

Not necessarily a singleton, but not disposed with each use, either.

Mr Shiny Pants
Nov 12, 2012

Toast Museum posted:

Not necessarily a singleton, but not disposed with each use, either.

And if one of these errors out, it will take all your other outstanding requests with it if I am not mistaken. Or did they fix that?

distortion park
Apr 25, 2011


I think you're meant to use HttpClientFactory now
https://docs.microsoft.com/en-us/do...t-http-requests

Red Mike
Jul 11, 2011

Mr Shiny Pants posted:

And if one of these errors out, it will take all your other outstanding requests with it if I am not mistaken. Or did they fix that?

If you don't pass a different cancellation token in for every request/endpoint, then a timeout (specifically a timeout, which shows up as a TaskCanceledException) will cancel and raise the exception for every pending request. This is just because they use one cancellation token per instance if you don't pass it in, and it's initialised at instantiation. I believe the 'instance' is the underlying handler rather than the HttpClient class, so you'd have one per endpoint which acts as basically a connection pool for that endpoint. So if your HTTP client does requests to two completely hosts, the two shouldn't be interfering with each other.

Either way, HttpClientFactory is the way forwards and it's a lot easier to use. I don't know if named clients are the best way vs just calling the factory to get a generic one, but named clients are the only easy way I'm aware of to set up default headers and things only in one place.

Its only downside is that it's tied to the DI framework and not actually generic, but so long as you're using the standard generic/web host it should be OK. If you're migrating a legacy app across that can't use those hosts, you're basically going to have to reimplement it in whatever DI framework you're using.

insta
Jan 28, 2009
Glad they didn't waste too much time making the esoteric thing of "web requests" too easy.

Just-In-Timeberlake
Aug 18, 2003
Thanks for the heads up on all that.


insta posted:

Glad they didn't waste too much time making the esoteric thing of "web requests" too easy.

for real, there's like 20 different ways to do the same thing.

Mr Shiny Pants
Nov 12, 2012

Red Mike posted:

If you don't pass a different cancellation token in for every request/endpoint, then a timeout (specifically a timeout, which shows up as a TaskCanceledException) will cancel and raise the exception for every pending request. This is just because they use one cancellation token per instance if you don't pass it in, and it's initialised at instantiation. I believe the 'instance' is the underlying handler rather than the HttpClient class, so you'd have one per endpoint which acts as basically a connection pool for that endpoint. So if your HTTP client does requests to two completely hosts, the two shouldn't be interfering with each other.

Either way, HttpClientFactory is the way forwards and it's a lot easier to use. I don't know if named clients are the best way vs just calling the factory to get a generic one, but named clients are the only easy way I'm aware of to set up default headers and things only in one place.

Its only downside is that it's tied to the DI framework and not actually generic, but so long as you're using the standard generic/web host it should be OK. If you're migrating a legacy app across that can't use those hosts, you're basically going to have to reimplement it in whatever DI framework you're using.

So were back to copying Java again with the factory stuff? Oh man. I like my WebRequest to be honest, it is simple and works and every time.

Calidus
Oct 31, 2011

Stand back I'm going to try science!
If your not using the factory (or not using it properly) you can run into socket exhaustion.

NihilCredo
Jun 6, 2011

iram omni possibili modo preme:
plus una illa te diffamabit, quam multæ virtutes commendabunt

Red Mike posted:

Either way, HttpClientFactory is the way forwards and it's a lot easier to use. I don't know if named clients are the best way vs just calling the factory to get a generic one, but named clients are the only easy way I'm aware of to set up default headers and things only in one place.

Its only downside is that it's tied to the DI framework and not actually generic, but so long as you're using the standard generic/web host it should be OK. If you're migrating a legacy app across that can't use those hosts, you're basically going to have to reimplement it in whatever DI framework you're using.

The infuriating stuff is that they could very easily havemade the default implementation of HttpClientFactory public and you'd just be able to new it up in e.g. a basic console app or even a powershell script, but instead it's declared internal so you have to go through some pointless DI poo poo just to get access to a factory that retains/disposes the underlying HttpMessageHandlers in an efficient way.

Since the default impl, linked above, is 385 lines including whitespace, braces, and comments, and hasn't had functional changes for at least three years, I urge everybody who doesn't need 50MB of DI code to either rip off the code directly, or use the nuget package that did exactly that

Munkeymon
Aug 14, 2003

Motherfucker's got an
armor-piercing crowbar! Rigoddamndicu𝜆ous.



Dog on Fire posted:

Does anyone know of a good way to call JavaScript from .NET Core? Specifically, I'd like my ASP.NET service to call a function in an npm package and run it on the back-end.

I'm trying to get it work using INodeServices, but it seems that this requires the JavaScript function to have a callback function as an argument. However, this JavaScript that I'm trying to use does not take a callback – it only returns a Promise.

I could ask the developers of the npm to add a function that takes that callback, but could I solve it on my service without bothering them?

Also, my ASP.NET service is only an API and doesn't have any HTML so I've avoided Blazor, but if that's the best way to get this working then I'd be fine with giving it a go. I'm also now trying the SpaServices extensions that the MS page is talking about, but it seems to not really have any documentation at all. Quite a bizarre situation.

I know you've got a workable solution but there's also https://github.com/sebastienros/jint

LongSack
Jan 17, 2003

Toast Museum posted:

Not necessarily a singleton, but not disposed with each use, either.

Umm so am i doing this wrong:
C# code:
using var client = new HttpClient();
client.AddToken(_storage);
var uri = _helper.Create(“Foo”,”ById”,fooId);
using var response = await client.GetAsync(uri);
…

Just-In-Timeberlake
Aug 18, 2003

LongSack posted:

Umm so am i doing this wrong:
C# code:
using var client = new HttpClient();
client.AddToken(_storage);
var uri = _helper.Create(“Foo”,”ById”,fooId);
using var response = await client.GetAsync(uri);
…

according to everything I read today to make the switch to HttpClientFactory, this will eventually bite you in the rear end, with the bonus knowing that going by the documentation, you're doing the right thing.

New Yorp New Yorp
Jul 18, 2003

Only in Kenya.
Pillbug

LongSack posted:

Umm so am i doing this wrong:
C# code:
using var client = new HttpClient();
client.AddToken(_storage);
var uri = _helper.Create(“Foo”,”ById”,fooId);
using var response = await client.GetAsync(uri);
…

Yeah. This is one of the exceptions to the rule that you should always dispose of anything that implements IDisposable

Mr Shiny Pants
Nov 12, 2012

NihilCredo posted:

The infuriating stuff is that they could very easily havemade the default implementation of HttpClientFactory public and you'd just be able to new it up in e.g. a basic console app or even a powershell script, but instead it's declared internal so you have to go through some pointless DI poo poo just to get access to a factory that retains/disposes the underlying HttpMessageHandlers in an efficient way.

Since the default impl, linked above, is 385 lines including whitespace, braces, and comments, and hasn't had functional changes for at least three years, I urge everybody who doesn't need 50MB of DI code to either rip off the code directly, or use the nuget package that did exactly that

Nice, the part that gets me: Who thought it would be a good idea in the first place? Why?

Adbot
ADBOT LOVES YOU

Cuntpunch
Oct 3, 2003

A monkey in a long line of kings

Mr Shiny Pants posted:

So were back to copying Java again with the factory stuff? Oh man. I like my WebRequest to be honest, it is simple and works and every time.

Classic hot /r/programmerhumor take.

Calidus posted:

If your not using the factory (or not using it properly) you can run into socket exhaustion.

This is the tricky bit of nuance around HttpClient and the awful API design that is still around for backwards compat: The vast majority of people using it will have no problem doing the by-the-book "create a new HttpClient for each request" nonsense. A significant majority will sidestep any potential issues by simply doing something like holding one at class scope for each class that needs to make requests, even if that means having a bunch active at once.

The socket-exhaustion/disposable pain comes from use cases involving a significant number of requests and doing so in the 'horrible' using-block way. Everyone curious should grab a spare laptop and intentionally try to trigger some of the deleterious behavior, it's educational in a way that all-caps headlines on Medium about 10 Reasons Why You Are Using HttpClient Wrong struggle to be.

  • 1
  • 2
  • 3
  • 4
  • 5
  • Post
  • Reply