Skip to content

Commit 8d11edf

Browse files
authored
Move ClientWorkspace abstractions from cloud machine into System.ClientModel (#48245)
Move ClientWorkspace abstractions from cloud machine into System.ClientModel
1 parent 6b952dc commit 8d11edf

File tree

11 files changed

+753
-1
lines changed

11 files changed

+753
-1
lines changed

sdk/core/System.ClientModel/api/System.ClientModel.net6.0.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,33 @@ protected AsyncCollectionResult() { }
7777
public abstract System.ClientModel.ContinuationToken? GetContinuationToken(System.ClientModel.ClientResult page);
7878
public abstract System.Collections.Generic.IAsyncEnumerable<System.ClientModel.ClientResult> GetRawPagesAsync();
7979
}
80+
public enum ClientAuthenticationMethod
81+
{
82+
Credential = 0,
83+
ApiKey = 1,
84+
NoAuth = 2,
85+
}
86+
public partial class ClientCache
87+
{
88+
public ClientCache() { }
89+
public T GetClient<T>(System.Func<T> createClient, string id = "") where T : class { throw null; }
90+
}
91+
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
92+
public readonly partial struct ClientConnection
93+
{
94+
private readonly object _dummy;
95+
private readonly int _dummyPrimitive;
96+
public ClientConnection(string id, string locator) { throw null; }
97+
public ClientConnection(string id, string locator, object credential) { throw null; }
98+
public ClientConnection(string id, string locator, string apiKey) { throw null; }
99+
public string? ApiKeyCredential { get { throw null; } }
100+
public System.ClientModel.Primitives.ClientAuthenticationMethod Authentication { get { throw null; } }
101+
public object? Credential { get { throw null; } }
102+
public string Id { get { throw null; } }
103+
public string Locator { get { throw null; } }
104+
public override string ToString() { throw null; }
105+
public bool TryGetLocatorAsUri(out System.Uri? uri) { throw null; }
106+
}
80107
[System.FlagsAttribute]
81108
public enum ClientErrorBehaviors
82109
{
@@ -141,6 +168,20 @@ protected CollectionResult() { }
141168
public abstract System.ClientModel.ContinuationToken? GetContinuationToken(System.ClientModel.ClientResult page);
142169
public abstract System.Collections.Generic.IEnumerable<System.ClientModel.ClientResult> GetRawPages();
143170
}
171+
public partial class ConnectionCollection : System.Collections.ObjectModel.KeyedCollection<string, System.ClientModel.Primitives.ClientConnection>
172+
{
173+
public ConnectionCollection() { }
174+
public void AddRange(System.Collections.Generic.IEnumerable<System.ClientModel.Primitives.ClientConnection> connections) { }
175+
protected override string GetKeyForItem(System.ClientModel.Primitives.ClientConnection item) { throw null; }
176+
}
177+
public abstract partial class ConnectionProvider
178+
{
179+
protected ConnectionProvider() { }
180+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
181+
public System.ClientModel.Primitives.ClientCache Subclients { get { throw null; } }
182+
public abstract System.Collections.Generic.IEnumerable<System.ClientModel.Primitives.ClientConnection> GetAllConnections();
183+
public abstract System.ClientModel.Primitives.ClientConnection GetConnection(string connectionId);
184+
}
144185
public partial class HttpClientPipelineTransport : System.ClientModel.Primitives.PipelineTransport, System.IDisposable
145186
{
146187
public HttpClientPipelineTransport() { }

sdk/core/System.ClientModel/api/System.ClientModel.net8.0.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,33 @@ protected AsyncCollectionResult() { }
7777
public abstract System.ClientModel.ContinuationToken? GetContinuationToken(System.ClientModel.ClientResult page);
7878
public abstract System.Collections.Generic.IAsyncEnumerable<System.ClientModel.ClientResult> GetRawPagesAsync();
7979
}
80+
public enum ClientAuthenticationMethod
81+
{
82+
Credential = 0,
83+
ApiKey = 1,
84+
NoAuth = 2,
85+
}
86+
public partial class ClientCache
87+
{
88+
public ClientCache() { }
89+
public T GetClient<T>(System.Func<T> createClient, string id = "") where T : class { throw null; }
90+
}
91+
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
92+
public readonly partial struct ClientConnection
93+
{
94+
private readonly object _dummy;
95+
private readonly int _dummyPrimitive;
96+
public ClientConnection(string id, string locator) { throw null; }
97+
public ClientConnection(string id, string locator, object credential) { throw null; }
98+
public ClientConnection(string id, string locator, string apiKey) { throw null; }
99+
public string? ApiKeyCredential { get { throw null; } }
100+
public System.ClientModel.Primitives.ClientAuthenticationMethod Authentication { get { throw null; } }
101+
public object? Credential { get { throw null; } }
102+
public string Id { get { throw null; } }
103+
public string Locator { get { throw null; } }
104+
public override string ToString() { throw null; }
105+
public bool TryGetLocatorAsUri(out System.Uri? uri) { throw null; }
106+
}
80107
[System.FlagsAttribute]
81108
public enum ClientErrorBehaviors
82109
{
@@ -141,6 +168,20 @@ protected CollectionResult() { }
141168
public abstract System.ClientModel.ContinuationToken? GetContinuationToken(System.ClientModel.ClientResult page);
142169
public abstract System.Collections.Generic.IEnumerable<System.ClientModel.ClientResult> GetRawPages();
143170
}
171+
public partial class ConnectionCollection : System.Collections.ObjectModel.KeyedCollection<string, System.ClientModel.Primitives.ClientConnection>
172+
{
173+
public ConnectionCollection() { }
174+
public void AddRange(System.Collections.Generic.IEnumerable<System.ClientModel.Primitives.ClientConnection> connections) { }
175+
protected override string GetKeyForItem(System.ClientModel.Primitives.ClientConnection item) { throw null; }
176+
}
177+
public abstract partial class ConnectionProvider
178+
{
179+
protected ConnectionProvider() { }
180+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
181+
public System.ClientModel.Primitives.ClientCache Subclients { get { throw null; } }
182+
public abstract System.Collections.Generic.IEnumerable<System.ClientModel.Primitives.ClientConnection> GetAllConnections();
183+
public abstract System.ClientModel.Primitives.ClientConnection GetConnection(string connectionId);
184+
}
144185
public partial class HttpClientPipelineTransport : System.ClientModel.Primitives.PipelineTransport, System.IDisposable
145186
{
146187
public HttpClientPipelineTransport() { }

sdk/core/System.ClientModel/api/System.ClientModel.netstandard2.0.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,33 @@ protected AsyncCollectionResult() { }
7777
public abstract System.ClientModel.ContinuationToken? GetContinuationToken(System.ClientModel.ClientResult page);
7878
public abstract System.Collections.Generic.IAsyncEnumerable<System.ClientModel.ClientResult> GetRawPagesAsync();
7979
}
80+
public enum ClientAuthenticationMethod
81+
{
82+
Credential = 0,
83+
ApiKey = 1,
84+
NoAuth = 2,
85+
}
86+
public partial class ClientCache
87+
{
88+
public ClientCache() { }
89+
public T GetClient<T>(System.Func<T> createClient, string id = "") where T : class { throw null; }
90+
}
91+
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
92+
public readonly partial struct ClientConnection
93+
{
94+
private readonly object _dummy;
95+
private readonly int _dummyPrimitive;
96+
public ClientConnection(string id, string locator) { throw null; }
97+
public ClientConnection(string id, string locator, object credential) { throw null; }
98+
public ClientConnection(string id, string locator, string apiKey) { throw null; }
99+
public string? ApiKeyCredential { get { throw null; } }
100+
public System.ClientModel.Primitives.ClientAuthenticationMethod Authentication { get { throw null; } }
101+
public object? Credential { get { throw null; } }
102+
public string Id { get { throw null; } }
103+
public string Locator { get { throw null; } }
104+
public override string ToString() { throw null; }
105+
public bool TryGetLocatorAsUri(out System.Uri? uri) { throw null; }
106+
}
80107
[System.FlagsAttribute]
81108
public enum ClientErrorBehaviors
82109
{
@@ -141,6 +168,20 @@ protected CollectionResult() { }
141168
public abstract System.ClientModel.ContinuationToken? GetContinuationToken(System.ClientModel.ClientResult page);
142169
public abstract System.Collections.Generic.IEnumerable<System.ClientModel.ClientResult> GetRawPages();
143170
}
171+
public partial class ConnectionCollection : System.Collections.ObjectModel.KeyedCollection<string, System.ClientModel.Primitives.ClientConnection>
172+
{
173+
public ConnectionCollection() { }
174+
public void AddRange(System.Collections.Generic.IEnumerable<System.ClientModel.Primitives.ClientConnection> connections) { }
175+
protected override string GetKeyForItem(System.ClientModel.Primitives.ClientConnection item) { throw null; }
176+
}
177+
public abstract partial class ConnectionProvider
178+
{
179+
protected ConnectionProvider() { }
180+
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
181+
public System.ClientModel.Primitives.ClientCache Subclients { get { throw null; } }
182+
public abstract System.Collections.Generic.IEnumerable<System.ClientModel.Primitives.ClientConnection> GetAllConnections();
183+
public abstract System.ClientModel.Primitives.ClientConnection GetConnection(string connectionId);
184+
}
144185
public partial class HttpClientPipelineTransport : System.ClientModel.Primitives.PipelineTransport, System.IDisposable
145186
{
146187
public HttpClientPipelineTransport() { }
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
namespace System.ClientModel.Primitives;
5+
/// <summary>
6+
/// Specifies the kind of connection used by the client.
7+
/// </summary>
8+
public enum ClientAuthenticationMethod
9+
{
10+
/// <summary>
11+
/// Represents a connection using Credential.
12+
/// </summary>
13+
Credential,
14+
15+
/// <summary>
16+
/// Represents a connection using an API key.
17+
/// </summary>
18+
ApiKey,
19+
20+
/// <summary>
21+
/// Represents a connection using an out-of-band method.
22+
/// </summary>
23+
NoAuth
24+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System.Collections.Generic;
5+
using System.Diagnostics;
6+
using System.Linq;
7+
using System.Threading;
8+
9+
namespace System.ClientModel.Primitives;
10+
11+
/// <summary>
12+
/// A cache for storing client instances, ensuring efficient reuse.
13+
/// Implements an LRU eviction policy.
14+
/// </summary>
15+
public class ClientCache
16+
{
17+
private readonly Dictionary<(Type, string), ClientEntry> _clients = new();
18+
private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.NoRecursion);
19+
20+
private const int MaxCacheSize = 100;
21+
22+
/// <summary>
23+
/// Retrieves a client from the cache or creates a new one if it doesn't exist.
24+
/// Updates the last-used timestamp on every hit.
25+
/// </summary>
26+
/// <typeparam name="T">The type of the client.</typeparam>
27+
/// <param name="createClient">A factory function to create the client if not cached.</param>
28+
/// <param name="id">An optional identifier for the client instance.</param>
29+
/// <returns>The cached or newly created client instance.</returns>
30+
public T GetClient<T>(Func<T> createClient, string id = "") where T : class
31+
{
32+
(Type, string) key = (typeof(T), id ?? string.Empty);
33+
34+
// If the client exists, update its timestamp.
35+
if (_clients.TryGetValue(key, out var cached))
36+
{
37+
cached.LastUsed = Stopwatch.GetTimestamp();
38+
return (T)cached.Client;
39+
}
40+
41+
// Client not found in cache, create a new one.
42+
_lock.EnterWriteLock();
43+
try
44+
{
45+
T created = createClient();
46+
_clients[key] = new ClientEntry(created, Stopwatch.GetTimestamp());
47+
48+
// After insertion, if cache exceeds the limit, perform cleanup.
49+
if (_clients.Count > MaxCacheSize)
50+
{
51+
Cleanup();
52+
}
53+
return created;
54+
}
55+
finally
56+
{
57+
_lock.ExitWriteLock();
58+
}
59+
}
60+
61+
/// <summary>
62+
/// Removes the least recently used cached clients until the cache size is below the limit.
63+
/// </summary>
64+
private void Cleanup()
65+
{
66+
int excess = _clients.Count - MaxCacheSize;
67+
if (excess <= 0)
68+
{
69+
return;
70+
}
71+
72+
// Remove the 'excess' number of items based on the oldest timestamp.
73+
foreach (var key in _clients.OrderBy(kvp => kvp.Value.LastUsed).Take(excess).Select(kvp => kvp.Key).ToList())
74+
{
75+
if (_clients.TryGetValue(key, out var instance))
76+
{
77+
_clients.Remove(key);
78+
if (instance.Client is IDisposable disposable)
79+
{
80+
disposable.Dispose();
81+
}
82+
}
83+
}
84+
}
85+
}
86+
87+
/// <summary>
88+
/// Represents a cached client and its last-used timestamp.
89+
/// </summary>
90+
internal class ClientEntry
91+
{
92+
public object Client { get; }
93+
public long LastUsed { get; set; }
94+
95+
public ClientEntry(object client, long lastUsed)
96+
{
97+
Client = client;
98+
LastUsed = lastUsed;
99+
}
100+
}

0 commit comments

Comments
 (0)