botbuilder-dotnet/libraries/Microsoft.Bot.Builder/MiddlewareSet.cs

101 строка
4.6 KiB
C#

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Bot.Builder
{
/// <summary>
/// Contains an ordered set of <see cref="IMiddleware"/>.
/// </summary>
public class MiddlewareSet : IMiddleware, IEnumerable<IMiddleware>
{
private readonly IList<IMiddleware> _middleware = new List<IMiddleware>();
/// <summary>
/// Adds a middleware object to the end of the set.
/// </summary>
/// <param name="middleware">The middleware to add.</param>
/// <returns>The updated middleware set.</returns>
/// <see cref="BotAdapter.Use(IMiddleware)"/>
public MiddlewareSet Use(IMiddleware middleware)
{
BotAssert.MiddlewareNotNull(middleware);
_middleware.Add(middleware);
return this;
}
/// <summary>
/// Processes an incoming activity.
/// </summary>
/// <param name="turnContext">The context object for this turn.</param>
/// <param name="next">The delegate to call to continue the bot middleware pipeline.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken)
{
await ReceiveActivityInternalAsync(turnContext, null, 0, cancellationToken).ConfigureAwait(false);
await next(cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Processes an activity.
/// </summary>
/// <param name="turnContext">The context object for the turn.</param>
/// <param name="callback">The delegate to call when the set finishes processing the activity.</param>
/// <param name="cancellationToken">A cancellation token that can be used by other objects
/// or threads to receive notice of cancellation.</param>
/// <returns>A task that represents the work queued to execute.</returns>
public async Task ReceiveActivityWithStatusAsync(ITurnContext turnContext, BotCallbackHandler callback, CancellationToken cancellationToken)
{
await ReceiveActivityInternalAsync(turnContext, callback, 0, cancellationToken).ConfigureAwait(false);
}
/// <summary>
/// Gets an enumerator that iterates over a collection of implementations of <see cref="IMiddleware"/> objects.
/// </summary>
/// <returns>An enumerator that can be used to iterate over the collection.</returns>
public IEnumerator<IMiddleware> GetEnumerator()
{
return this._middleware.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this._middleware.GetEnumerator();
}
private Task ReceiveActivityInternalAsync(ITurnContext turnContext, BotCallbackHandler callback, int nextMiddlewareIndex, CancellationToken cancellationToken)
{
// Check if we're at the end of the middleware list yet
if (nextMiddlewareIndex == _middleware.Count)
{
// If all the middleware ran, the "leading edge" of the tree is now complete.
// This means it's time to run any developer specified callback.
// Once this callback is done, the "trailing edge" calls are then completed. This
// allows code that looks like:
// Trace.TraceInformation("before");
// await next();
// Trace.TraceInformation("after");
// to run as expected.
// If a callback was provided invoke it now and return its task, otherwise just return the completed task
return callback?.Invoke(turnContext, cancellationToken) ?? Task.CompletedTask;
}
// Get the next piece of middleware
var nextMiddleware = _middleware[nextMiddlewareIndex];
// Execute the next middleware passing a closure that will recurse back into this method at the next piece of middleware as the NextDelegate
return nextMiddleware.OnTurnAsync(
turnContext,
(ct) => ReceiveActivityInternalAsync(turnContext, callback, nextMiddlewareIndex + 1, ct),
cancellationToken);
}
}
}