So, I've registered a named client with the services collection in my Startup.cs:
services.AddHttpClient(someServiceName,
client => client.BaseAddress = baseAddress);
and now can inject an IHttpClientFactory from my service provider.
Using this IHttpClientFactory, I conjure up a client instance:
var client = httpClientFactory.CreateClient(someServiceName)
Once upon a time, it was necessary to be very careful about the disposing of HttpClient instances, as it was rarely the right thing to do.
However, now we have HttpClientFactory, does this matter any more? Should/Can this client be disposed without worry? e.g.
using (var httpClient = httpClientFactory.CreateClient(someServiceName))
using (var response = await httpClient.PostAsync(somePath, someData))
{
var content = await response.Content.ReadAsAsync<SomeResponse>();
//...
}
Calling the Dispose method is not required but you can still call it if you need for some reasons.
Proof: HttpClient and lifetime management
Disposal of the client isn't required. Disposal cancels outgoing requests and guarantees the given HttpClient instance can't be used after calling Dispose. IHttpClientFactory tracks and disposes resources used by HttpClient instances. The HttpClient instances can generally be treated as .NET objects not requiring disposal.
Check the source of DefaultHttpClientFactory:
public HttpClient CreateClient(string name)
{
if (name == null)
{
throw new ArgumentNullException(nameof(name));
}
var handler = CreateHandler(name);
var client = new HttpClient(handler, disposeHandler: false);
var options = _optionsMonitor.Get(name);
for (var i = 0; i < options.HttpClientActions.Count; i++)
{
options.HttpClientActions[i](client);
}
return client;
}
The instance of HttpMessageHandler stores unmanaged resources of the HttpClient. In the classical scenario the HttpClient creates the instance of HttpMessageHandler and disposes it while itself disposing.
You can see in the above code that different instances of HttpClient shares single instance of HttpMessageHandler and doesn't dispose it (disposeHandler: false).
So, the call of the HttpClient.Dispose does nothing. But it's not dangerous.
No. You should not dispose of your client. To be more general, you should not dispose of anything retrieved via a DI container, which in ASP.NET Core is by default the service collection. The lifetime is managed by the DI container, so if you dispose of the client, but it's later injected into something, you'll get an ObjectDisposedException. Let the container handle disposal.
This is actually a common confusion with IDisposable classes. You should personally only implement IDisposable if your class itself owns dependencies. If all its dependencies are injected, you should not implement IDisposable, since it doesn't own anything that needs disposal. Likewise, you should not dispose of anything injected into your class, as it doesn't own those dependencies. Only dispose of things you specifically new up. If you don't see the keyword new, you probably shouldn't be disposing.
Related
I am moving an asp.net mvc5 application using EF6 to asp.net core MVC 3.0 using EF Core.
In my mvc5 application I have some administrative operation that modify the database and take a long time, so I use a pattern when I create a new DBContext that is not the one that is associated with the request context and then run the task in the background using Task.Run. This has been working fine for years.
In converting to .net core it was unclear how to create a new DBContext in the way that I was doing it in my old codebase. It seems like I should be able to create a Transient DBContext in these cases and all should be fine.
So I created a subclass of MyDbContext called MyTransientDbContex and in my Configure class I added this service:
services.AddDbContext<MyTransientDbContex>(options =>
options.UseSqlServer(
context.Configuration.GetConnectionString("MyContextConnection")),
ServiceLifetime.Transient, ServiceLifetime.Transient);
In my controller I inject the context in the action that needs the transient service and spawn a thread to do something with it:
public ActionResult Update([FromServices] MyTransientContext context) {
Task.Run(() =>
{
try {
// Do some long running operation with context
}
Catch (Exception e) {
// Report Exception
}
finally {
context.Dispose();
}
}
return RedirectToAction("Status");
}
I would not expect my transient context to be disposed until the finally block. But I am getting this exception when attempting to access the context on the background thread:
Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'MyTransientContext'.'
And indeed the _disposed flag is set to true on the context object.
I put a breakpoint on the constructer for MyTransientContext and "Made an Object ID" of the this pointer so that I could track the object. This transient object is being created and is the same one that is inject into my controller action. It's also the same object that I'm trying to reference when the exception is thrown.
I tried setting a data breakpoint on the _disposed member in order to get a callstack on when disposed is being set to true, but the breakpoint won't bind.
I also tried overriding the Dispose method on MyTransientContext, and it isn't called until my explicit dispose in the finally block, which is after the exception is thrown and caught.
I feel like I'm missing something fundamental here. Isn't this what the transient services are for? What would dispose a Transient service?
One last detail - MyTransientContext is derived from MyContext, which is in turn derived from IdentityDbContext (Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContex)
Edit: The reason that I went down the path of using a Transient was because of this ef core document page: https://learn.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext. It states that "...any code that explicitly executes multiple threads in parallel should ensure that DbContext instances aren't ever accessed concurrently. Using dependency injection, this can be achieved by either registering the context as scoped and creating scopes (using IServiceScopeFactory) for each thread, or by registering the DbContext as transient (using the overload of AddDbContext which takes a ServiceLifetime parameter)."
As xabikos pointed out, this seems to be overriden by the scoping of the asp.net DI system, where it looks like anything created by that system is scoped to the request context, including Transient objects. Can someone point out where that's documented so that I can better understand how to work with the limitations?
f you want manage the lifetime of service, you can instantiate it manually (or use a factory) :
public ActionResult Update()
{
Task.Run(() =>
{
using(var context = new MyTransientContext(...))
{
try
{
// Do some long running operation with context
}
catch (Exception e)
{
// Report Exception
}
}
}
return RedirectToAction("Status");
}
Or you can use IServiceProvider to get and manage a service :
public class MyController
{
private IServiceProvider _services;
public MyController(IServiceProvider services)
{
_services = services;
}
public ActionResult Update()
{
var context = (MyTransientContext)_services.GetService(typeof(MyTransientContext));
Task.Run(() =>
{
using (context)
{
try
{
// Do some long running operation with context
}
catch (Exception e)
{
// Report Exception
}
}
}
return RedirectToAction("Status");
}
}
You mixed the concepts of transient objects that are created by internal DI container asp.net core provides.
You configure the MyTransientContext to be transient in the internal DI system. This practically means that every time a scope is created then a new instance is returned. For asp.net application this scope matches an HTTP request. When the requests ends then all the objects are disposed if applicable.
Now in your code, that is a synchronous action method you spawn a Task with Task.Run. This is an async operation and you don't await for this. Practically during execution this will be started but not wait to finish, the redirect will happen and the request will end. At this point if you try to use the injected instance you will get the exception.
If you would like to solve this you need change to an async action and await on the Task.Run. And most likely you don't need to spawn a new Task. But you need to understand that this is not probably the best way as it will need for the long operation to finish before the redirect takes place.
An alternative to this would be to use a messaging mechanism, and send a message that triggers this operation. And you have another component, like worker service that listens for those messages and process them.
Is there a way to configure App Insights to collect the operation name when monitoring a WCF service? All requests get lumped together by URL (which are just POSTs that end in .svc), so there is no easy way to determine which particular operation was called on the service.
Does there need to be a custom Telemetry Initializer that can somehow determine which operation was actually called and set a custom property? if so, how do you determine the current WCF operation name?
Another option for collecting data on WCF operations is to use the Microsoft.ApplicationInsights.Wcf Nuget package. You can read more about this here.
Brett,
Operation name can be customized in two ways:
1) Using a custom telemetry initializer - that specifically sets operation name.
For more information about telemetry initializers: Custom Telemetry Initializers
2) From sdk version 2-beta3, auto-generated request telemetry is accessible though HttpContext extension method:
System.Web.HttpContextExtension.GetRequestTelemetry
Once the request telemetry is retrieved, operation name associated with it can be changed.
Please let me know if this addressed your question.
Thanks,
Karthik
If you want to get the name of the WCF method called from a client in application insight you can use the following ITelemetryInitializer
With .net 5.0, the httprequest object is stored in the raw object properties of the telemetry context.
public class SoapActionHeaderTelemetryInitializer : ITelemetryInitializer
{
private static readonly Regex _soapActionUri = new Regex("^\"(?<uri>.*)\"$", RegexOptions.Compiled);
public void Initialize(ITelemetry telemetry)
{
DependencyTelemetry httpDependency = telemetry as DependencyTelemetry;
if (httpDependency != null)
{
httpDependency.Context.TryGetRawObject("HttpRequest", out var request);
if (request is HttpRequestMessage httpRequest)
{
if (httpRequest.Headers.TryGetValues("SOAPAction", out var values) && values.Any())
{
// SOAP Action is contained within quote : https://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383528
var soapAction = _soapActionUri.Match(values.First()).Groups["uri"].Value;
telemetry.Context.GlobalProperties["SOAPAction"] = soapAction;
}
}
}
}
}
I have a web application. I found that performance bottleneck could be that i am creating Http client again and again for every request.
public static class DemoHttpClient
{
public static HttpClient GetClient()
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(DemoConstants.DemoAPI);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
return client;
}
}
public class DemoConstants
{
public const string DemoAPI = "http://localhost/";
}
I am planning to implement singleton for this. And found this very helpful article.
http://csharpindepth.com/Articles/General/Singleton.aspx
I am confused as to how exactly ASP.NET MVC web application lifecycle is with when it is deployed on the server. Assuming there will be multiple threads calling same resource, the resource further again and again making new http clients..
What should we do here..
1) Lazily load HTTP client?
2) Not lazily load it?
Which particular approach should we use?
This doesn't sound like a good idea. In particular, take a peek into the docs of the HttpClient class:
Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
https://msdn.microsoft.com/en-us/library/system.net.http.httpclient%28v=vs.118%29.aspx
This means that accessing the very same singleton instance from multiple threads will lead to undefined issues.
What you could do however, is you could reuse the same instance across a single request. This can be done by storing an instance in the Items container:
private static string ITEMSKEY = "____hclient";
public static HttpClient GetClient()
{
if ( HttpContext.Current.Items[ITEMSKEY] == null )
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri(DemoConstants.DemoAPI);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
HttpContext.Current.Items.Add( ITEMSKEY, client );
}
return (HttpClient)HttpContext.Current.Items[ITEMSKEY];
}
Note, that since the HttpClient implements IDisposable, it still could be a good idea to dispose such instance somewhere in the pipeline, for example in the EndRequest event of the application pipeline.
Update: as noted in a comment by #LukeH, the updated version of the docs for the .NET 4.5 and 4.6 states that some of methods of the HttpClient class are thread safe:
https://msdn.microsoft.com/en-us/library/system.net.http.httpclient%28v=vs.110%29.aspx
The updated remarks section states that a single instance is basically a collection of shared settings applied to all requests executed by this instance. Then, the docs says:
In addition, every HttpClient instance uses its own connection pool, isolating its requests from requests executed by other HttpClient instances.
This means that the isolation of different pools could still make sense, my personal recommendation would still be then to not to have a singleton, as you possibly would still need to change some settings between consecutive requests.
I'm trying to use OWIN, SignalR and Autofac in a single project.
I'm setting things up with regards to signalR as follows:
// Create the AutoFac container builder:
var builder = new ContainerBuilder();
// ...[Register various other things in here]...
// register signalR Hubs
builder.RegisterHubs(Assembly.GetExecutingAssembly());
// Build the container:
var container = builder.Build();
// Configure SignalR with the dependency resolver.
app.MapSignalR(new HubConfiguration
{
Resolver = new AutofacDependencyResolver(container)
});
My issue is that when I use the Autofac SignalR integration, I can no longer get a signalR Hub Context on the server (in a webapi controller for example) and so can't send messages from server side to the connected clients. Something like the following is how I do this when I'm not using the Autofac signalR integration:
var hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
hubContext.Clients.All.notification("Test Message");
But this doesn't work when I add Autofac into the mix - I don't get any error message and I do seem to get a hubContext, but calls on it don't actually seem to get to the clients.
If I comment out the use of the dependency resolver for signalR in the call to MapSignalR, the call to GetHubContext works again and messages reach the signalR clients sucessfully, but of course I can no longer use IoC on my hubs. e.g.
// Configure SignalR with the dependency resolver.
app.MapSignalR(new HubConfiguration
{
// Resolver = new AutofacDependencyResolver(container)
});
Can anybody tell me why using the AutofacDependencyResolver stops GlobalHost.ConnectionManager.GetHubContext from working correctly??
NOTE: One other thing I have tried is instead of using GlobalHost.ConnectionManager.GetHubContext() I tried injecting an IConnectionManager into the webapi controller from which I want to send a message to clients, then calling GetHubContext on that, but Autofac couldn't resolve the IConnectionManager.
I did find the following article by Piotr Szmyd which apparently allows this:
http://www.szmyd.com.pl/blog/wiring-signalr-with-autofac
but this appears to be based on obsolete signalR builds, and while there seems to be a nuget package for it here:
http://www.nuget.org/packages/SignalR.AutoFac/
it also seems well out of date.
If you use a custom dependency resolver with SignalR, you can no longer use GlobalHost unless you modify it:
GlobalHost.DependencyResolver = new AutofacDependencyResolver(container);
IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
// A custom HubConfiguration is now unnecessary, since MapSignalR will
// use the resolver from GlobalHost by default.
app.MapSignalR();
If you don't want to modify GlobalHost, you will have to manually resolve your IConnectionManager:
IDependencyResolver resolver = new AutofacDependencyResolver(container);
IHubContext hubContext = resolver.Resolve<IConnectionManager>().GetHubContext<MyHub>();
app.MapSignalR(new HubConfiguration
{
Resolver = resolver
});
For a complete answer, with SignalR, Autofac, and OWIN, I did the following:
// register IPersistantConnectionContext for access to SignalR connection
// and IDependencyResolver to enable inection of the container into its
// construction for the config object.
var builder = new ContainerBuilder();
builder.RegisterType<Autofac.Integration.SignalR.AutofacDependencyResolver>()
.As<IDependencyResolver>()
.SingleInstance();
builder.Register((context, p) =>
context.Resolve<IDependencyResolver>()
.Resolve<Microsoft.AspNet.SignalR.Infrastructure.IConnectionManager>()
.GetConnectionContext<SignalRConnection>());
// ... other registrations
var container = builder.Build();
var signalrConfiguration = new ConnectionConfiguration
{
Resolver = container.Resolve<IDependencyResolver>(),
};
app.UseAutofacMiddleware(container);
app.MapSignalR<SignalRConnection>("/signalr", signalrConfiguration);
// ... other middleware
In my controllers, I included a parameter of the type IPersistentConnectionContext and the correct instance is injected.
I was using a PersistentConnection, but it should be similar for Hubs.
To expand on Nathan's answer, I am using something similar with the key line being
builder.RegisterHubs(Assembly.GetExecutingAssembly()).SingleInstance();
in Startup.cs.
The line "SingleInstance()" ensures that only a single "instance" of the hub is used throughout the application.
Then I just use straightforward dependency injection into the constructor of the controller to get a pointer to the hub.
I am using SignalR 1.1 with .NET clients.
I have a single method in my hub that accepts an object of BaseMessage class and broadcasts it to all clients:
public void SendMessage(BaseMessage message)
{
Clients.All.BroadCastMessage(message);
}
Clients will pass derived messages into this method:
_hub.Invoke("SendMessage", new ErrorMessage("Some Error")).Wait();
The client has a single message handler:
_hub.On<BaseMessage>("BroadCastMessage", OnMessageReceived);
I've specified TypeNameHandling.All serializer settings at application startup:
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All};
var serializer = new JsonNetSerializer(settings);
GlobalHost.DependencyResolver.Register(typeof(IJsonSerializer), () => serializer);
But when the client sends a derived message, the server receives a base message.
How should I configure serializer to be able to receive derived messages?
Note: I can do serialization/deserialization manually and pass strings to the server, but this causes double serialization.
SignalR is not supporting this scenario as it would require your JSON payload to contain information about the derived type and assembly. See this sample.
Adding type information in your payload would not play well with browsers. I suggest you create individual hub methods to handle each of your derived types.