Throttling HttpClient requests by some variable
<p>I'm writing a class that uses HttpClient to access an API and I want to throttle the number of concurrent calls that can be made to a certain function in this class. The trick is though that the limit is per tenant and multiple tenants might be using their own instance of the class at a time.</p>

<p>My Tenant class is just a container for read-only context information.</p>

<pre><code>public class Tenant
{
    public string Name { get; }
    public string ApiKey { get; }
}
</code></pre>

<p>Here's the ApiClient:</p>

<pre><code>public class ApiClient
{
    private readonly Tenant tenant;

    public ApiClient(Tenant tenant)
    {
        this.tenant = tenant;
    }

    public async Task&lt;string&gt; DoSomething()
    {
        var response = await this.SendCoreAsync();
        return response.ToString();
    }

    private Task&lt;XElement&gt; SendCore()
    {
        using (var httpClient = new HttpClient())
        {
            var httpRequest = this.BuildHttpRequest();
            var httpResponse = await httpClient.SendAsync(httpRequest);
            return XElement.Parse(await httpResponse.Content.ReadAsStringAsync());
        }
    }
}
</code></pre>

<p>What I want to do is throttle the <code>SendCore</code> method and limit it to two concurrent requests <strong>per tenant</strong>. I've read suggestions of using <code>TPL</code> or <code>SemaphoreSlim</code> to do basic throttling (such as here: <a href="https://stackoverflow.com/questions/22492383/throttling-asynchronous-tasks">Throttling asynchronous tasks</a>), but I'm not clear on how to add in the further complication of the tenant.</p>

<p>Thanks for the suggestions.</p>

<p><strong>UPDATE</strong></p>

<p>I've attempted to use a set of <code>SemaphoreSlim</code> objects (one per tenant) contained in a <code>ConcurrentDictionary</code>. This seems to work, but I'm not sure if this is ideal. The new code is:</p>

<pre><code>public class ApiClient
{
    private static readonly ConcurrentDictionary&lt;string, SemaphoreSlim&gt; Semaphores = new ConcurrentDictionary&lt;string, SemaphoreSlim&gt;();
    private readonly Tenant tenant;
    private readonly SemaphoreSlim semaphore;

    public ApiClient(Tenant tenant)
    {
        this.tenant = tenant;
        this.semaphore = Semaphores.GetOrAdd(this.tenant.Name, k =&gt; new SemaphoreSlim(2));
    }

    public async Task&lt;string&gt; DoSomething()
    {
        var response = await this.SendCoreAsync);
        return response.ToString();
    }

    private Task&lt;XElement&gt; SendCore()
    {
        await this.semaphore.WaitAsync();
        try
        {
            using (var httpClient = new HttpClient())
            {
                var httpRequest = this.BuildHttpRequest();
                var httpResponse = await httpClient.SendAsync(httpRequest);
                return XElement.Parse(await httpResponse.Content.ReadAsStringAsync());
            }
        }
        finally
        {
            this.semaphore.Release();
        }
    }
}
</code></pre>