diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting/AgentSessionStore.cs b/dotnet/src/Microsoft.Agents.AI.Hosting/AgentSessionStore.cs
index 2f57e26409..1d5373e38a 100644
--- a/dotnet/src/Microsoft.Agents.AI.Hosting/AgentSessionStore.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Hosting/AgentSessionStore.cs
@@ -1,7 +1,9 @@
// Copyright (c) Microsoft. All rights reserved.
+using System;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Shared.Diagnostics;
namespace Microsoft.Agents.AI.Hosting;
@@ -43,4 +45,35 @@ public abstract class AgentSessionStore
AIAgent agent,
string conversationId,
CancellationToken cancellationToken = default);
+
+ /// Asks the for an object of the specified type .
+ /// The type of object being requested.
+ /// An optional key that can be used to help identify the target service.
+ /// The found object, otherwise .
+ /// is .
+ ///
+ /// The purpose of this method is to allow for the retrieval of strongly-typed services that might be provided by the ,
+ /// including itself or any services it might be wrapping. This is particularly useful for inspecting delegation chains
+ /// to verify that specific store implementations are present.
+ ///
+ public virtual object? GetService(Type serviceType, object? serviceKey = null)
+ {
+ _ = Throw.IfNull(serviceType);
+
+ return serviceKey is null && serviceType.IsInstanceOfType(this)
+ ? this
+ : null;
+ }
+
+ /// Asks the for an object of type .
+ /// The type of the object to be retrieved.
+ /// An optional key that can be used to help identify the target service.
+ /// The found object, otherwise .
+ ///
+ /// The purpose of this method is to allow for the retrieval of strongly typed services that may be provided by the ,
+ /// including itself or any services it might be wrapping. This is particularly useful for inspecting delegation chains
+ /// to verify that specific store implementations are present.
+ ///
+ public TService? GetService(object? serviceKey = null)
+ => this.GetService(typeof(TService), serviceKey) is TService service ? service : default;
}
diff --git a/dotnet/src/Microsoft.Agents.AI.Hosting/DelegatingAgentSessionStore.cs b/dotnet/src/Microsoft.Agents.AI.Hosting/DelegatingAgentSessionStore.cs
index 6e8314a74c..e80d3b907a 100644
--- a/dotnet/src/Microsoft.Agents.AI.Hosting/DelegatingAgentSessionStore.cs
+++ b/dotnet/src/Microsoft.Agents.AI.Hosting/DelegatingAgentSessionStore.cs
@@ -59,4 +59,23 @@ public abstract class DelegatingAgentSessionStore : AgentSessionStore
///
public override ValueTask SaveSessionAsync(AIAgent agent, string conversationId, AgentSession session, CancellationToken cancellationToken = default)
=> this.InnerStore.SaveSessionAsync(agent, conversationId, session, cancellationToken);
+
+ ///
+ ///
+ /// This implementation first checks if this instance satisfies the service request.
+ /// If not, it chains the request to the inner store, allowing services to be retrieved
+ /// from any store in the delegation chain.
+ ///
+ public override object? GetService(Type serviceType, object? serviceKey = null)
+ {
+ // First, check if this instance satisfies the request
+ object? service = base.GetService(serviceType, serviceKey);
+ if (service is not null)
+ {
+ return service;
+ }
+
+ // Chain to the inner store
+ return this.InnerStore.GetService(serviceType, serviceKey);
+ }
}
diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.UnitTests/DelegatingAgentSessionStoreTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.UnitTests/DelegatingAgentSessionStoreTests.cs
index f73776f172..f04ad7c20d 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Hosting.UnitTests/DelegatingAgentSessionStoreTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.UnitTests/DelegatingAgentSessionStoreTests.cs
@@ -191,6 +191,182 @@ public class DelegatingAgentSessionStoreTests
#endregion
+ #region GetService Tests
+
+ ///
+ /// Verify that GetService returns itself when requesting the exact type.
+ ///
+ [Fact]
+ public void GetServiceReturnsItselfForExactType()
+ {
+ // Act
+ var result = this._delegatingStore.GetService(typeof(TestDelegatingAgentSessionStore));
+
+ // Assert
+ Assert.Same(this._delegatingStore, result);
+ }
+
+ ///
+ /// Verify that GetService returns itself when requesting a base type.
+ ///
+ [Fact]
+ public void GetServiceReturnsItselfForBaseType()
+ {
+ // Act
+ var result = this._delegatingStore.GetService(typeof(DelegatingAgentSessionStore));
+
+ // Assert
+ Assert.Same(this._delegatingStore, result);
+ }
+
+ ///
+ /// Verify that GetService returns itself when requesting AgentSessionStore.
+ ///
+ [Fact]
+ public void GetServiceReturnsItselfForAgentSessionStoreType()
+ {
+ // Act
+ var result = this._delegatingStore.GetService(typeof(AgentSessionStore));
+
+ // Assert
+ Assert.Same(this._delegatingStore, result);
+ }
+
+ ///
+ /// Verify that GetService chains to inner store when type is not satisfied by outer store.
+ ///
+ [Fact]
+ public void GetServiceChainsToInnerStore()
+ {
+ // Arrange
+ var innerStore = new ConcreteAgentSessionStore();
+ var delegatingStore = new TestDelegatingAgentSessionStore(innerStore);
+
+ // Act
+ var result = delegatingStore.GetService(typeof(ConcreteAgentSessionStore));
+
+ // Assert
+ Assert.Same(innerStore, result);
+ }
+
+ ///
+ /// Verify that GetService chains through multiple delegation layers.
+ ///
+ [Fact]
+ public void GetServiceChainsThoughMultipleDelegationLayers()
+ {
+ // Arrange - create a three-layer chain: outer -> middle -> inner
+ var innerStore = new ConcreteAgentSessionStore();
+ var middleStore = new AnotherDelegatingAgentSessionStore(innerStore);
+ var outerStore = new TestDelegatingAgentSessionStore(middleStore);
+
+ // Act - request the innermost store type
+ var result = outerStore.GetService(typeof(ConcreteAgentSessionStore));
+
+ // Assert
+ Assert.Same(innerStore, result);
+ }
+
+ ///
+ /// Verify that GetService can find a store in the middle of the delegation chain.
+ ///
+ [Fact]
+ public void GetServiceFindsMiddleStoreInChain()
+ {
+ // Arrange - create a three-layer chain: outer -> middle -> inner
+ var innerStore = new ConcreteAgentSessionStore();
+ var middleStore = new AnotherDelegatingAgentSessionStore(innerStore);
+ var outerStore = new TestDelegatingAgentSessionStore(middleStore);
+
+ // Act - request the middle store type
+ var result = outerStore.GetService(typeof(AnotherDelegatingAgentSessionStore));
+
+ // Assert
+ Assert.Same(middleStore, result);
+ }
+
+ ///
+ /// Verify that GetService returns null when the requested type is not found in the chain.
+ ///
+ [Fact]
+ public void GetServiceReturnsNullWhenTypeNotFound()
+ {
+ // Arrange
+ var innerStore = new ConcreteAgentSessionStore();
+ var delegatingStore = new TestDelegatingAgentSessionStore(innerStore);
+
+ // Act
+ var result = delegatingStore.GetService(typeof(string));
+
+ // Assert
+ Assert.Null(result);
+ }
+
+ ///
+ /// Verify that GetService returns null when a service key is provided but not matched.
+ ///
+ [Fact]
+ public void GetServiceReturnsNullWhenServiceKeyProvided()
+ {
+ // Act
+ var result = this._delegatingStore.GetService(typeof(TestDelegatingAgentSessionStore), "some-key");
+
+ // Assert
+ Assert.Null(result);
+ }
+
+ ///
+ /// Verify that GetService throws ArgumentNullException when serviceType is null.
+ ///
+ [Fact]
+ public void GetServiceThrowsWhenServiceTypeIsNull() =>
+ Assert.Throws("serviceType", () => this._delegatingStore.GetService(null!));
+
+ ///
+ /// Verify that GetService generic method works correctly.
+ ///
+ [Fact]
+ public void GetServiceGenericReturnsItself()
+ {
+ // Act
+ var result = this._delegatingStore.GetService();
+
+ // Assert
+ Assert.Same(this._delegatingStore, result);
+ }
+
+ ///
+ /// Verify that GetService generic method chains to inner store.
+ ///
+ [Fact]
+ public void GetServiceGenericChainsToInnerStore()
+ {
+ // Arrange
+ var innerStore = new ConcreteAgentSessionStore();
+ var delegatingStore = new TestDelegatingAgentSessionStore(innerStore);
+
+ // Act
+ var result = delegatingStore.GetService();
+
+ // Assert
+ Assert.Same(innerStore, result);
+ }
+
+ ///
+ /// Verify that GetService generic method returns null when type not found.
+ ///
+ [Fact]
+ public void GetServiceGenericReturnsNullWhenTypeNotFound()
+ {
+ // Act
+ var result = this._delegatingStore.GetService();
+
+ // Assert
+ Assert.Null(result);
+ }
+
+ #endregion
+
#region Test Implementation
///
@@ -201,6 +377,23 @@ public class DelegatingAgentSessionStoreTests
public new AgentSessionStore InnerStore => base.InnerStore;
}
+ ///
+ /// Another delegating store implementation for testing multi-layer chains.
+ ///
+ private sealed class AnotherDelegatingAgentSessionStore(AgentSessionStore innerStore) : DelegatingAgentSessionStore(innerStore);
+
+ ///
+ /// Concrete (non-delegating) session store for testing GetService chaining.
+ ///
+ private sealed class ConcreteAgentSessionStore : AgentSessionStore
+ {
+ public override ValueTask GetSessionAsync(AIAgent agent, string conversationId, CancellationToken cancellationToken = default)
+ => new(new TestAgentSession());
+
+ public override ValueTask SaveSessionAsync(AIAgent agent, string conversationId, AgentSession session, CancellationToken cancellationToken = default)
+ => ValueTask.CompletedTask;
+ }
+
private sealed class TestAgentSession : AgentSession;
#endregion
diff --git a/dotnet/tests/Microsoft.Agents.AI.Hosting.UnitTests/IsolationKeyScopedAgentSessionStoreTests.cs b/dotnet/tests/Microsoft.Agents.AI.Hosting.UnitTests/IsolationKeyScopedAgentSessionStoreTests.cs
index f2635162ae..18fe3095e3 100644
--- a/dotnet/tests/Microsoft.Agents.AI.Hosting.UnitTests/IsolationKeyScopedAgentSessionStoreTests.cs
+++ b/dotnet/tests/Microsoft.Agents.AI.Hosting.UnitTests/IsolationKeyScopedAgentSessionStoreTests.cs
@@ -364,6 +364,45 @@ public class IsolationKeyScopedAgentSessionStoreTests
#endregion
+ #region GetService Tests
+
+ ///
+ /// Verify that GetService can retrieve IsolationKeyScopedAgentSessionStore from a delegation chain.
+ ///
+ [Fact]
+ public void GetServiceReturnsIsolationKeyScopedAgentSessionStore()
+ {
+ // Arrange
+ var provider = new TestSessionIsolationKeyProvider(TestIsolationKey);
+ var store = new IsolationKeyScopedAgentSessionStore(this._innerStoreMock.Object, provider);
+
+ // Act
+ var result = store.GetService();
+
+ // Assert
+ Assert.Same(store, result);
+ }
+
+ ///
+ /// Verify that GetService chains through to find inner store types.
+ ///
+ [Fact]
+ public void GetServiceChainsToInnerStore()
+ {
+ // Arrange
+ var concreteInnerStore = new ConcreteAgentSessionStore();
+ var provider = new TestSessionIsolationKeyProvider(TestIsolationKey);
+ var store = new IsolationKeyScopedAgentSessionStore(concreteInnerStore, provider);
+
+ // Act
+ var result = store.GetService();
+
+ // Assert
+ Assert.Same(concreteInnerStore, result);
+ }
+
+ #endregion
+
#region Helper Classes
///
@@ -386,5 +425,17 @@ public class IsolationKeyScopedAgentSessionStoreTests
private sealed class TestAgentSession : AgentSession;
+ ///
+ /// Concrete (non-delegating) session store for testing GetService chaining.
+ ///
+ private sealed class ConcreteAgentSessionStore : AgentSessionStore
+ {
+ public override ValueTask GetSessionAsync(AIAgent agent, string conversationId, CancellationToken cancellationToken = default)
+ => new(new TestAgentSession());
+
+ public override ValueTask SaveSessionAsync(AIAgent agent, string conversationId, AgentSession session, CancellationToken cancellationToken = default)
+ => ValueTask.CompletedTask;
+ }
+
#endregion
}