The big problem is precisely that it is misleading. By having implemented the interface IDisposble
everyone thinks they should use the using
. And I don’t know if in the first implementation of this class I didn’t need, what I do know is that the documentation initially said nothing of this, or because it taught wrong or because it was like this before (you can’t trust 100% documentation, something close to this can, but it never gives all the details).
It makes sense to have this interface in this class because it may actually have reasons for at some point the resource linked to this object being discarded. But the most common use is that it lasts the entire application.
Think about the object, starting with the name. People think of it as a connection but it is a client. How many HTTP clients do you need in your application? One, right? What for? It’s a mechanism, it’s like a file system, You don’t have to have more than one. The fact that you have only one does not mean that you can only make a request, it being there can order whenever you want, as long as you have access to the object.
For some reason there’s nothing static about him like file system, probably because the customer is little heavy and creating will always be something expensive for most applications, while creating every time it needs is too costly (which is the mistake that "everyone" makes).
In the correct form it is very common that this object is placed in a static field, so creates once, possibly in the first effective use, and no longer need, then just use.
The example of the other answer is not very good because it was written anyway. I imagine the intention there was that the field was static. Unless in a very simple example it does not seem appropriate to create in the main class, should have a class with responsibility to take care of it, but there is a mistake of use of the HttpClient
but organization of the code.
I learned that if the class has IDisposable
always use using
It is not so, it must in methods, that is, when the object must be created locally. And this must be relative, also have cases not to use, although probably has better way to do.
There are cases that you can create the object there, return it and then the object must retain the resource linked to it, so it is not there that will use the using
, but will probably use it elsewhere for liberation to be made. Luckily you don’t see it out there, but it is something that can exist and even be useful in some scenario. Something like this:
void Metodo() {
...
using (var arquivo = CriaRecurso()) { ... }
...
}
FileStream CriaRecurso() {
...
var file = new FileStream("abc.txt", FileMode.Create)
...
return file;
}
In addition it may be that the object with the resource can be stored within a class through a field and not a local variable. In this case there is no way to use the using
what you’ll have to do is at some point call the Dispose()
of that object. This can be through a destructor/finalizer method that every object has or through a Dispose()
created in this class, so if you use an object that is IDisposable
in a field practically forces this class to implement the IDisposable
, is viral. Then an object of this class will probably be placed in a using
.
It’s more rare to do this, but I’ve seen several scenarios you need. If people did so much code it would be optimized when there are gains.
In some cases it must be even in a static field which can dispense this class go through some destruction.
The lifespan necessary for the object will determine where and whether the using
.
You can never use the using
in that class?
It can, if you know that your application will only use the HTTP client there once or a very limited amount, then better not to. And I’m not saying it’s the same. It’s worse to do this more than once, it only makes sense if it’s once, at all times of execution. In fact in a scenario like this is better with the using
, but it’s rare to have such a scenario.
What goes wrong if you do it like this?
He will create sockets on the operating system. It is unclear why but it does not seem to release immediately and so an hour may run out sockets free. Or even if this does not occur each socket new spent resources on the machine.
There’s a page that shows this with property:
using static System.Console;
using System.Net.Http;
public class Program {
public static async Task Main() {
WriteLine("Starting connections");
for (int i = 0; i<10; i++) {
using (var client = new HttpClient()) {
var result = await client.GetAsync("http://aspnetmonsters.com");
WriteLine(result.StatusCode);
}
}
WriteLine("Connections done");
}
}
This works and produces this:
C:\code\socket> dotnet run
Project socket (.NETCoreApp,Version=v1.0) will be compiled because inputs were modified
Compiling socket for .NETCoreApp,Version=v1.0
Compilation succeeded.
0 Warning(s)
0 Error(s)
Time elapsed 00:00:01.2501667
Starting connections
OK
OK
OK
OK
OK
OK
OK
OK
OK
OK
Connections done
But investigating in the operating system see what happened:
C:\code\socket>NETSTAT.EXE
...
Proto Local Address Foreign Address State
TCP 10.211.55.6:12050 waws-prod-bay-017:http TIME_WAIT
TCP 10.211.55.6:12051 waws-prod-bay-017:http TIME_WAIT
TCP 10.211.55.6:12053 waws-prod-bay-017:http TIME_WAIT
TCP 10.211.55.6:12054 waws-prod-bay-017:http TIME_WAIT
TCP 10.211.55.6:12055 waws-prod-bay-017:http TIME_WAIT
TCP 10.211.55.6:12056 waws-prod-bay-017:http TIME_WAIT
TCP 10.211.55.6:12057 waws-prod-bay-017:http TIME_WAIT
TCP 10.211.55.6:12058 waws-prod-bay-017:http TIME_WAIT
TCP 10.211.55.6:12059 waws-prod-bay-017:http TIME_WAIT
TCP 10.211.55.6:12060 waws-prod-bay-017:http TIME_WAIT
TCP 10.211.55.6:12061 waws-prod-bay-017:http TIME_WAIT
TCP 10.211.55.6:12062 waws-prod-bay-017:http TIME_WAIT
TCP 127.0.0.1:1695 SIMONTIMMS742B:1696 ESTABLISHED
...
Bad, right? Imagine it happening a thousand times.
using static System.Console;
using System.Net.Http;
public class Program {
private static HttpClient Client = new HttpClient();
public static async Task Main() {
WriteLine("Starting connections");
for (int i = 0; i<10; i++) {
var result = await client.GetAsync("http://aspnetmonsters.com");
WriteLine(result.StatusCode);
}
WriteLine("Connections done");
}
}
I put in the Github for future reference.
The result is the same but now the operating system reports only one socket.
Note that this is ok because it is an example only for tests, the object and resource will be discarded at the end of the application that is when we want, but there are cases that need a more sophisticated control. Don’t always think this shape is the most suitable for your case, so it is necessary to learn how to do things broadly and not just decorate cake recipes.
Completion.
We need to understand everything about what we will use. Often it is not enough to read the official documentation, have to look for other documents, question. Programming is hard, even trying to do everything right can still go wrong.
The problem is that in the past the documentation did not say that life time should probably be the same as the whole application.
Some recommended readings:
At a time when didn’t have that in the documentation, I don’t even know if at first the object had different behavior. There are some things that teach wrong there yet. Microsoft invited me to improve some things, but it’s hard to take care of so much. And I don’t know how much I can really move. The answer here basically says what you need and it’s more important, if I think you need more I’ll give you an answer.
– Maniero
Read on this: https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/ and this is also useful: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-2.2#typed-clients And I recommend reading this for deeper understanding https://softwareengineering.stackexchange.com/a/370742/389 It may also be useful: https://docs.microsoft.com/en-us/azure/architecture/antipatterns/improper-instantiation/
– Maniero
@Maniero Thank you, I’m clear.
– ramaral
A question, it is very common nowadays to use dependency injection. In this case, injecting an Httpclient can be a problem? It could better explain how the 'using' and 'Disposis' work in dependency injections?
– Bruno Warmling
@Brunowarmling yes, DI is very badly used, in this case it is quite wrong even :)
– Maniero
@Brunowarmling why you want to inject an Httpclient?
– ramaral
@ramaral test?
– Maniero
For example, I have been using Pattern CQRS. In one of my commands, I have Httpclient as a dependency, to consume an API. This Httpclient is in turn created by an Httpclientfactory to include decorators and handle the authentication part of this API. So now reading all this... the question has arisen whether I’m doing it all wrong...
– Bruno Warmling
@Maniero I see no need. I may be looking bad but the class that uses Httpclient should be subject to integration tests and not unit tests.
– ramaral
@ramaral may be, but you don’t see a scenario where you need to mock this class to get it to you without actually using HTTP?
– Maniero
@Maniero What you mock is the class that uses Httpclient, so you don’t need it. If the reason of the DI is tests then the injected object has to be mockable, as you would a mock of an Httpclient?
– ramaral
@ramaral even agree although I think in cases like this often does not need DI (gives long discussion), but may have some case that needs the nearest level. The mock would be a class with the same API but responding with controlled data instead of connecting.
– Maniero
@Maniero This is the definition of mock. My question is how to do it. Httpclient only implements Idisposable, does not expose the "API". So how do you inject it? Whatever the injection method used, an Httpclient type is expected.
– ramaral
@That’s true, he wasn’t even made to think about it. It’s just that I think all this is so wrong that I even tried to find a way to use it so it doesn’t look radical and... I didn’t :D
– Maniero
@Maniero Does not give directly. But if you have the necessary commitment, set up an interface with the methods you want to use from Httpclient. Then wrapper over it, implement that interface, and use it instead. However, it’s best to stay radical. :)
– ramaral
@Maniero It seems that the question of "how to use" has been solved with Httpclientfactory
– ramaral