Skip to content
This repository was archived by the owner on Jan 5, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions libraries/Microsoft.Bot.Builder.Dialogs/DialogExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,18 @@ public static async Task RunAsync(this Dialog dialog, ITurnContext turnContext,

var dialogContext = await dialogSet.CreateContextAsync(turnContext, cancellationToken).ConfigureAwait(false);

await InternalRunAsync(turnContext, dialog.Id, dialogContext, cancellationToken).ConfigureAwait(false);
await InternalRunAsync(turnContext, dialog.Id, dialogContext, null, cancellationToken).ConfigureAwait(false);
}

internal static async Task<DialogTurnResult> InternalRunAsync(ITurnContext turnContext, string dialogId, DialogContext dialogContext, CancellationToken cancellationToken)
internal static async Task<DialogTurnResult> InternalRunAsync(ITurnContext turnContext, string dialogId, DialogContext dialogContext, DialogStateManagerConfiguration stateConfiguration, CancellationToken cancellationToken)
{
// map TurnState into root dialog context.services
foreach (var service in turnContext.TurnState)
{
dialogContext.Services[service.Key] = service.Value;
}

var dialogStateManager = new DialogStateManager(dialogContext);
var dialogStateManager = new DialogStateManager(dialogContext, stateConfiguration);
await dialogStateManager.LoadAllScopesAsync(cancellationToken).ConfigureAwait(false);
dialogContext.Context.TurnState.Add(dialogStateManager);

Expand Down
2 changes: 1 addition & 1 deletion libraries/Microsoft.Bot.Builder.Dialogs/DialogManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ public async Task<DialogManagerResult> OnTurnAsync(ITurnContext context, Cancell
var dc = new DialogContext(Dialogs, context, dialogState);

// Call the common dialog "continue/begin" execution pattern shared with the classic RunAsync extension method
var turnResult = await DialogExtensions.InternalRunAsync(context, _rootDialogId, dc, cancellationToken).ConfigureAwait(false);
var turnResult = await DialogExtensions.InternalRunAsync(context, _rootDialogId, dc, StateConfiguration, cancellationToken).ConfigureAwait(false);

// save BotState changes
await botStateSet.SaveAllChangesAsync(dc.Context, false, cancellationToken).ConfigureAwait(false);
Expand Down
73 changes: 73 additions & 0 deletions tests/Microsoft.Bot.Builder.Dialogs.Tests/DialogManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@
using Microsoft.Bot.Builder.Dialogs.Adaptive;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Actions;
using Microsoft.Bot.Builder.Dialogs.Adaptive.Conditions;
using Microsoft.Bot.Builder.Dialogs.Memory;
using Microsoft.Bot.Builder.Dialogs.Memory.Scopes;
using Microsoft.Bot.Builder.Skills;
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Moq;
using Xunit;

namespace Microsoft.Bot.Builder.Dialogs.Tests
Expand Down Expand Up @@ -380,6 +383,46 @@ public async Task DialogManager_ContainerRegistration_DoubleNesting()
Assert.DoesNotContain(dm.Dialogs.GetDialogs(), d => d.GetType() == typeof(SendActivity));
}

[Fact]
public async Task DialogManager_StateConfiguration()
{
// Arrange
var dialog = new WaterfallDialog("test-dialog");

var memoryScope = new CustomMemoryScope();
var pathResolver = new CustomPathResolver();

var dialogManager = new DialogManager(dialog);
dialogManager.StateConfiguration = new DialogStateManagerConfiguration();
dialogManager.StateConfiguration.MemoryScopes.Add(memoryScope);
dialogManager.StateConfiguration.PathResolvers.Add(pathResolver);

// The test dialog being used here happens to not send anything so we only need to mock the type.
var adapterMock = new Mock<BotAdapter>();

// ChannelId and Conversation.Id are required for ConversationState and
// ChannelId and From.Id are required for UserState.
var activity = new Activity
{
ChannelId = "test-channel",
Conversation = new ConversationAccount { Id = "test-conversation-id" },
From = new ChannelAccount { Id = "test-id" }
};

// Act
DialogStateManager actual;
using (var turnContext = new TurnContext(adapterMock.Object, activity))
{
turnContext.TurnState.Add(new ConversationState(new MemoryStorage()));
await dialogManager.OnTurnAsync(turnContext);
actual = turnContext.TurnState.Get<DialogStateManager>();
}

// Assert
Assert.Contains(memoryScope, actual.Configuration.MemoryScopes);
Assert.Contains(pathResolver, actual.Configuration.PathResolvers);
}

[Theory]
[InlineData(SkillFlowTestCase.RootBotOnly, false)]
[InlineData(SkillFlowTestCase.RootBotConsumingSkill, false)]
Expand Down Expand Up @@ -529,6 +572,36 @@ private TestFlow CreateFlow(Dialog dialog, IStorage storage, string conversation
});
}

private class CustomMemoryScope : MemoryScope
{
public CustomMemoryScope()
: base("custom")
{
}

public override object GetMemory(DialogContext dc)
{
throw new NotImplementedException();
}

public override void SetMemory(DialogContext dc, object memory)
{
throw new NotImplementedException();
}
}

private class CustomPathResolver : IPathResolver
{
public CustomPathResolver()
{
}

public string TransformPath(string path)
{
throw new NotImplementedException();
}
}

private class AskForNameDialog : ComponentDialog, IDialogDependencies
{
private readonly string _property;
Expand Down