7

When working with Azure .NET SDKs generated from swagger specs (such as those associated with Azure Resource Manager) the resulting libraries leverage the Microsoft AutoRest Client Runtime and the various "Clients" all inherit from "ServiceClient".

We've been working with DocumentDB Client, and reading a lot about issues with using the native HttpClient in .NET. Microsoft suggests using singleton patterns for both of these clients because of how they work internally, despite the well-known issues with using the singleton pattern. In these cases, it is necessary.

As a result, we've developed a strategy for using and managing singletons for these cases, so we want to know if we should use the same strategy for the Azure REST clients derived from ServiceClient. If it uses HttpClient it would make sense.

Note: This question isn't a question looking for general developer advice about singletons or clients, but rather a specific question for the Microsoft dev teams associated with the AutoRest client runtime based on knowledge of it's inner-workings.

solvingJ
  • 1,321
  • 1
  • 19
  • 30

3 Answers3

3

I've been trying to solve this same issue. I'm using a number of autorest service clients, but have to re-instantiate them every request to pass user-specific client credentials. With Microsoft.Rest.ClientRuntime 2.3.6 you can now instantiate a ServiceClient with your own HttpClient. This allows me to use a transient ServiceClient with a singleton HttpClient. I simply added a new constructor to the generated autorest client.

public partial class MyClient : ServiceClient<IMyClient>, IMyClient
{

    public MyClient(Uri baseUri, ServiceClientCredentials credentials, HttpClient client) : base(client)
    {
        if (baseUri == null)
        {
            throw new ArgumentNullException("baseUri");
        }
        if (credentials == null)
        {
            throw new ArgumentNullException("credentials");
        }

        this.Initialize();
        this.Credentials = credentials;
        Credentials?.InitializeServiceClient(this);
        this.BaseUri = baseUri;

    }
    [...]
}

This, however, would result in an ObjectDisposedException after the first request. This is because the ServiceClient disposes HttpClients, regardless if you passed it in or not. method

protected virtual void Dispose(bool disposing)
{
    if (!_disposed)
    {
        _disposed = true;

        // Dispose the client
        HttpClient.Dispose();
        HttpClient = null;
        FirstMessageHandler = null;
        HttpClientHandler = null;
    }
}

I simply overrode the Dispose method in 'MyClient' to do nothing since the only object getting disposed is the HttpClient.

protected override void Dispose(bool disposing) { }

I haven't noticed any fallout from this as the FirstMessageHandler and HttpClientHandler are only instantiated when ServiceClient creates the HttpClient for you. This approach has allowed me to use a single HttpClient across multiple AutoRest generated ServiceClients with custom user credentials on each request.

I'd be interested to see if anyone sees any fallout from this approach.

Mike H
  • 503
  • 3
  • 13
1

Yes and no. :-) You don't need to use the Singleton design pattern, but it is advisable to share ServiceClient-derived instances wherever possible since each one encapsulates an HttpClient.

For some Azure libraries, sharing a single client isn't always possible. For example, the SearchIndexClient in the Azure Search library can only target one index at a time, so if your app uses multiple indexes you'll need to pool them somehow. Here is a related question on this topic which has links to other discussions elsewhere.

Community
  • 1
  • 1
Bruce Johnston
  • 8,344
  • 3
  • 32
  • 42
  • Thank you for the answer. Kind of a messy situation it seems. About what I was expecting, but not what I was hoping :( – solvingJ Apr 19 '17 at 17:56
  • Even if you could instantiate ServiceClients willy-nilly, you'd still need a way to share an HttpClient because the TCP connections they use are a limited resource. Would this be better? – Bruce Johnston Apr 19 '17 at 19:21
  • One problem seems to be that the design of HttpClient has deviated from the norm for HttpClients in other languages, where they are typically disposable. At the very least, it violates the principle of least astonishment. It forces the common construct of an HTTP client (and derived libraries) to become a cross-cutting concerns. This creates challenges when working in non-traditional net environments where DI/IOC can't be used. For example, Azure Functions. In these cases, the particular design of the .NET HttpClient forces developers to engineer solutions for sharing global objects. – solvingJ Apr 19 '17 at 19:43
  • I should clarify that my comment above is not intended to deride the HttpClient, but moreso to point out that the .NET framework does not provide a native mechanism for using it the way that it was intended. It's design effectively assumes the user will engineer a way to share it, which means 3rd party DI libraries or dubious patterns like servicelocator or singleton. This lack of a native platform mechanism for cross-cutting concerns exists in most languages. So, with HttpClient (as well as many other cross-cutting concerns), the average developer is left in a very awkward situation. – solvingJ Apr 19 '17 at 20:05
1

You can now share HttpClient instances beyween instances of ServiceClient, so there is no longer a large reason to use a singleton pattern

  • That sounds interesting Mark, thank you! Any links to documentation about this? Also, you say not a large reason... does that mean that there's still a bit of a reason in some cases? Can you elaborate just a bit on when we should consider doing that, or just explain what the remaining issues are? – solvingJ Apr 21 '17 at 03:57
  • Mark, doesn't your suggestion actually require the use a singleton pattern? How else would I "share" a common instance across many functions in many areas of an application? Are you saying that the ServiceClient creates and shares it's own instance automatically? – solvingJ May 22 '17 at 12:23
  • No, I am saying that you could use an http client with many service clients. – Mark Cowlishaw - MSFT May 23 '17 at 16:20
  • But, in order to achieve sharing, one needs DI or Singleton or similar. – solvingJ May 24 '17 at 14:07
  • Mark, could you, please, take a look at my stackoverflow issue? I am trying to use singleton HttpClient with Microsoft.Rest.ServiceClient, but getting System.MissingMethodException and now clue what is the problem... https://stackoverflow.com/questions/52728789/using-singleton-httpclient-with-microsoft-rest-serviceclient-getting-system-mi – Prokurors Oct 22 '18 at 15:02