Chain.cs
1 //
2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license.
4 //
5 // Microsoft Bot Framework: http://botframework.com
6 //
7 // Bot Builder SDK GitHub:
8 // https://github.com/Microsoft/BotBuilder
9 //
10 // Copyright (c) Microsoft Corporation
11 // All rights reserved.
12 //
13 // MIT License:
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 
38 using Microsoft.Bot.Connector;
39 using System;
40 using System.Collections.Generic;
41 using System.Linq;
42 using System.Runtime.Serialization;
43 using System.Text;
44 using System.Text.RegularExpressions;
45 using System.Threading.Tasks;
46 
47 namespace Microsoft.Bot.Builder.Dialogs
48 {
52  public static partial class Chain
53  {
62  public delegate Task<IDialog<R>> Continuation<in T, R>(IBotContext context, IAwaitable<T> item);
63 
70  public static IDialog<T> From<T>(Func<IDialog<T>> MakeDialog)
71  {
72  return new FromDialog<T>(MakeDialog);
73  }
74 
82  public static IDialog<T> Do<T>(this IDialog<T> antecedent, Func<IBotContext, IAwaitable<T>, Task> callback)
83  {
84  return new DoDialog<T>(antecedent, callback);
85  }
86 
95  public static IDialog<R> Then<T, R>(this IDialog<T> Antecedent, Func<IBotContext, IAwaitable<T>, Task<R>> Action)
96  {
97  return new ThenDialog<T, R>(Antecedent, Action);
98  }
99 
106  public static IDialog<T> PostToUser<T>(this IDialog<T> antecedent)
107  {
108  return new PostToUserDialog<T>(antecedent);
109  }
110 
117  public static IDialog<IMessageActivity> WaitToBot<T>(this IDialog<T> antecedent)
118  {
119  return new WaitToBotDialog<T>(antecedent);
120  }
121 
130  {
131  return Chain.Return(string.Empty).WaitToBot();
132  }
133 
142  public static IDialog<T> PostEvent<T, E>(this IDialog<T> antecedent, E @event)
143  {
144  return new PostEventDialog<T, E>(antecedent, @event);
145  }
146 
155  public static IDialog<R> ContinueWith<T, R>(this IDialog<T> antecedent, Continuation<T, R> continuation)
156  {
157  return new ContinueWithDialog<T, R>(antecedent, continuation);
158  }
159 
168  public static IDialog<R> Select<T, R>(this IDialog<T> antecedent, Func<T, R> selector)
169  {
170  return new SelectDialog<T, R>(antecedent, selector);
171  }
172 
180  public static IDialog<T> Where<T>(this IDialog<T> antecedent, Func<T, bool> predicate)
181  {
182  return new WhereDialog<T>(antecedent, predicate);
183  }
184 
191  public static IDialog<T> Unwrap<T>(this IDialog<IDialog<T>> antecedent)
192  {
193  return new UnwrapDialog<T>(antecedent);
194  }
195 
206  public static IDialog<R> SelectMany<T, C, R>(this IDialog<T> antecedent, Func<T, IDialog<C>> function, Func<T, C, R> projection)
207  {
208  return new SelectManyDialog<T, C, R>(antecedent, function, projection);
209  }
210 
216  public static IDialog<T> Loop<T>(this IDialog<T> antecedent)
217  {
218  return new LoopDialog<T>(antecedent);
219  }
220 
238  public static IDialog<R> Void<T, R>(this IDialog<T> antecedent)
239  {
240  return new VoidDialog<T, R>(antecedent);
241  }
242 
255  public static IDialog<object> Void<T>(this IDialog<T> antecedent, IDialogStack stack)
256  {
257  var frames = stack.Frames;
258  if (frames.Count == 0)
259  {
260  throw new InvalidOperationException("Stack is empty");
261  }
262 
263  var frame = stack.Frames[0];
264  var restType = frame.GetType();
265  bool valid = restType.IsGenericType && restType.GetGenericTypeDefinition() == typeof(ResumeAfter<>);
266  if (valid)
267  {
268  var waitType = restType.GetGenericArguments()[0];
269  var voidType = typeof(VoidDialog<,>).MakeGenericType(typeof(T), waitType);
270  var instance = Activator.CreateInstance(voidType, antecedent);
271  var dialog = (IDialog<object>)instance;
272  return dialog;
273  }
274  else
275  {
276  throw new ArgumentOutOfRangeException(restType.Name);
277  }
278  }
279 
288  public static IDialog<T> Catch<T, E>(this IDialog<T> antecedent, Func<IDialog<T>, E, IDialog<T>> block) where E: Exception
289  {
290  return new CatchDialog<T, E>(antecedent, block);
291  }
292 
300  public static IDialog<T> Catch<T>(this IDialog<T> antecedent, Func<IDialog<T>, Exception, IDialog<T>> block)
301  {
302  return new CatchDialog<T, Exception>(antecedent, block);
303  }
304 
312  public static IDialog<T> DefaultIfException<T, E>(this IDialog<T> antecedent) where E : Exception
313  {
314  return new DefaultIfExceptionDialog<T, E>(antecedent);
315  }
316 
323  public static IDialog<T> DefaultIfException<T>(this IDialog<T> antecedent)
324  {
325  return new DefaultIfExceptionDialog<T, Exception>(antecedent);
326  }
327 
338  public static IDialog<R> Switch<T, R>(this IDialog<T> antecedent, params ICase<T, R>[] cases)
339  {
340  return new SwitchDialog<T, R>(antecedent, cases);
341  }
342 
352  public static IDialog<T> Return<T>(T item)
353  {
354  return new ReturnDialog<T>(item);
355  }
356 
365  public static IDialog<T> While<T>(this IDialog<T> initial, Func<T, IDialog<bool>> test, Func<T, IDialog<T>> body)
366  {
367  return new WhileDialog<T>(initial, test, body);
368  }
369 
377  public static IDialog<T> Fold<T>(this IDialog<IEnumerable<IDialog<T>>> antecedent, Func<T, T, T> folder)
378  {
379  return new FoldDialog<T>(antecedent, folder);
380  }
381 
391  public static IDialog<T> WithScorable<T, Item, Score>(this IDialog<T> antecedent, IScorable<Item, Score> scorable)
392  {
393  return new WithScorableDialog<T, Item, Score>(antecedent, scorable);
394  }
395 
404  public static ICase<T, R> Case<T, R>(Func<T, bool> condition, ContextualSelector<T, R> selector)
405  {
406  return new Case<T, R>(condition, selector);
407  }
408 
416  public static ICase<string, R> Case<R>(Regex regex, ContextualSelector<string, R> selector)
417  {
418  return new RegexCase<R>(regex, selector);
419  }
420 
428  public static ICase<T, R> Default<T, R>(ContextualSelector<T, R> selector)
429  {
430  return new DefaultCase<T, R>(selector);
431  }
432 
433  [Serializable]
434  private sealed class FromDialog<T> : IDialog<T>
435  {
436  public readonly Func<IDialog<T>> MakeDialog;
437  public FromDialog(Func<IDialog<T>> MakeDialog)
438  {
439  SetField.NotNull(out this.MakeDialog, nameof(MakeDialog), MakeDialog);
440  }
441  async Task IDialog<T>.StartAsync(IDialogContext context)
442  {
443  var dialog = this.MakeDialog();
444  context.Call<T>(dialog, ResumeAsync);
445  }
446  private async Task ResumeAsync(IDialogContext context, IAwaitable<T> result)
447  {
448  context.Done<T>(await result);
449  }
450  }
451 
452  [Serializable]
453  private sealed class DoDialog<T> : IDialog<T>
454  {
455  public readonly IDialog<T> Antecedent;
456  public readonly Func<IBotContext, IAwaitable<T>, Task> Action;
457  public DoDialog(IDialog<T> antecedent, Func<IBotContext, IAwaitable<T>, Task> Action)
458  {
459  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
460  SetField.NotNull(out this.Action, nameof(Action), Action);
461  }
462  async Task IDialog<T>.StartAsync(IDialogContext context)
463  {
464  context.Call<T>(this.Antecedent, ResumeAsync);
465  }
466  private async Task ResumeAsync(IDialogContext context, IAwaitable<T> result)
467  {
468  await this.Action(context, result);
469  context.Done<T>(await result);
470  }
471  }
472 
473  [Serializable]
474  private sealed class ThenDialog<T, R> : IDialog<R>
475  {
476  public readonly IDialog<T> Antecedent;
477  public readonly Func<IBotContext, IAwaitable<T>, Task<R>> Action;
478  public ThenDialog(IDialog<T> antecedent, Func<IBotContext, IAwaitable<T>, Task<R>> Action)
479  {
480  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
481  SetField.NotNull(out this.Action, nameof(Action), Action);
482  }
483 
484  async Task IDialog<R>.StartAsync(IDialogContext context)
485  {
486  context.Call<T>(this.Antecedent, ResumeAsync);
487  }
488  private async Task ResumeAsync(IDialogContext context, IAwaitable<T> result)
489  {
490  context.Done<R>(await this.Action(context, result));
491  }
492  }
493 
494  [Serializable]
495  private sealed class PostToUserDialog<T> : IDialog<T>
496  {
497  public readonly IDialog<T> Antecedent;
498  public PostToUserDialog(IDialog<T> antecedent)
499  {
500  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
501  }
502  async Task IDialog<T>.StartAsync(IDialogContext context)
503  {
504  context.Call<T>(this.Antecedent, ResumeAsync);
505  }
506  private async Task ResumeAsync(IDialogContext context, IAwaitable<T> result)
507  {
508  var item = await result;
509  await context.PostAsync(item.ToString());
510  context.Done<T>(item);
511  }
512  }
513 
514  [Serializable]
515  private sealed class WaitToBotDialog<T> : IDialog<IMessageActivity>
516  {
517  public readonly IDialog<T> Antecedent;
518  public WaitToBotDialog(IDialog<T> antecedent)
519  {
520  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
521  }
522  public async Task StartAsync(IDialogContext context)
523  {
524  context.Call<T>(this.Antecedent, ResumeAsync);
525  }
526  private async Task ResumeAsync(IDialogContext context, IAwaitable<T> result)
527  {
528  var item = await result;
529  context.Wait(MessageReceivedAsync);
530  }
531  public async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
532  {
533  context.Done(await argument);
534  }
535  }
536 
537  [Serializable]
538  private sealed class PostEventDialog<T, E> : IDialog<T>
539  {
540  public readonly IDialog<T> Antecedent;
541  public readonly E Event;
542 
543  public PostEventDialog(IDialog<T> antecedent, E @event)
544  {
545  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
546  this.Event = @event;
547  }
548 
549  public override string ToString()
550  {
551  return $"{this.GetType().Name}({this.Event})";
552  }
553 
554  async Task IDialog<T>.StartAsync(IDialogContext context)
555  {
556  context.Call<T>(this.Antecedent, AfterAntecedent);
557  }
558 
559  private T item;
560  private async Task AfterAntecedent(IDialogContext context, IAwaitable<T> result)
561  {
562  this.item = await result;
563  context.Post(this.Event, AfterEvent);
564  }
565 
566  private async Task AfterEvent(IDialogContext context, IAwaitable<E> result)
567  {
568  var @event = await result;
569  if (! object.ReferenceEquals(@event, this.Event))
570  {
571  throw new InvalidOperationException();
572  }
573 
574  context.Done(this.item);
575  }
576  }
577 
578  [Serializable]
579  private sealed class ContinueWithDialog<T, R> : IDialog<R>
580  {
581  public readonly IDialog<T> Antecedent;
582  public readonly Continuation<T, R> Continuation;
583 
584  public ContinueWithDialog(IDialog<T> antecedent, Continuation<T, R> continuation)
585  {
586  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
587  SetField.NotNull(out this.Continuation, nameof(continuation), continuation);
588  }
589 
590  async Task IDialog<R>.StartAsync(IDialogContext context)
591  {
592  context.Call<T>(this.Antecedent, ResumeAsync);
593  }
594 
595  private async Task ResumeAsync(IDialogContext context, IAwaitable<T> result)
596  {
597  var next = await this.Continuation(context, result);
598  context.Call<R>(next, DoneAsync);
599  }
600 
601  private async Task DoneAsync(IDialogContext context, IAwaitable<R> result)
602  {
603  context.Done(await result);
604  }
605  }
606 
607  [Serializable]
608  private sealed class SelectDialog<T, R> : IDialog<R>
609  {
610  public readonly IDialog<T> Antecedent;
611  public readonly Func<T, R> Selector;
612  public SelectDialog(IDialog<T> antecedent, Func<T, R> selector)
613  {
614  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
615  SetField.NotNull(out this.Selector, nameof(selector), selector);
616  }
617  async Task IDialog<R>.StartAsync(IDialogContext context)
618  {
619  context.Call<T>(this.Antecedent, AfterAntecedent);
620  }
621  private async Task AfterAntecedent(IDialogContext context, IAwaitable<T> result)
622  {
623  var itemT = await result;
624  var itemR = this.Selector(itemT);
625  context.Done(itemR);
626  }
627  }
628 
632  [Serializable]
633  public sealed class WhereCanceledException : OperationCanceledException
634  {
639  {
640  }
641 
647  private WhereCanceledException(SerializationInfo info, StreamingContext context)
648  : base(info, context)
649  {
650  }
651  }
652 
653  [Serializable]
654  private sealed class WhereDialog<T> : IDialog<T>
655  {
656  public readonly IDialog<T> Antecedent;
657  public readonly Func<T, bool> Predicate;
658  public WhereDialog(IDialog<T> antecedent, Func<T, bool> predicate)
659  {
660  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
661  SetField.NotNull(out this.Predicate, nameof(predicate), predicate);
662  }
663  async Task IDialog<T>.StartAsync(IDialogContext context)
664  {
665  context.Call<T>(this.Antecedent, AfterAntecedent);
666  }
667  private async Task AfterAntecedent(IDialogContext context, IAwaitable<T> result)
668  {
669  var itemT = await result;
670  var itemR = this.Predicate(itemT);
671  if (itemR)
672  {
673  context.Done(itemT);
674  }
675  else
676  {
677  throw new WhereCanceledException();
678  }
679  }
680  }
681 
682  [Serializable]
683  private sealed class UnwrapDialog<T> : IDialog<T>
684  {
685  public readonly IDialog<IDialog<T>> Antecedent;
686  public UnwrapDialog(IDialog<IDialog<T>> antecedent)
687  {
688  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
689  }
690  async Task IDialog<T>.StartAsync(IDialogContext context)
691  {
692  context.Call<IDialog<T>>(this.Antecedent, AfterAntecedent);
693  }
694  private async Task AfterAntecedent(IDialogContext context, IAwaitable<IDialog<T>> result)
695  {
696  var dialogT = await result;
697  context.Call<T>(dialogT, AfterDialog);
698  }
699  private async Task AfterDialog(IDialogContext context, IAwaitable<T> result)
700  {
701  var itemT = await result;
702  context.Done(itemT);
703  }
704  }
705 
706  // http://blogs.msdn.com/b/pfxteam/archive/2013/04/03/tasks-monads-and-linq.aspx
707  [Serializable]
708  private sealed class SelectManyDialog<T, C, R> : IDialog<R>
709  {
710  public readonly IDialog<T> Antecedent;
711  public readonly Func<T, IDialog<C>> Function;
712  public readonly Func<T, C, R> Projection;
713  public SelectManyDialog(IDialog<T> antecedent, Func<T, IDialog<C>> function, Func<T, C, R> projection)
714  {
715  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
716  SetField.NotNull(out this.Function, nameof(function), function);
717  SetField.NotNull(out this.Projection, nameof(projection), projection);
718  }
719  async Task IDialog<R>.StartAsync(IDialogContext context)
720  {
721  context.Call<T>(this.Antecedent, AfterAntecedent);
722  }
723  private T itemT;
724  private async Task AfterAntecedent(IDialogContext context, IAwaitable<T> result)
725  {
726  this.itemT = await result;
727  var dialog = this.Function(this.itemT);
728  context.Call<C>(dialog, AfterFunction);
729  }
730  private async Task AfterFunction(IDialogContext context, IAwaitable<C> result)
731  {
732  var itemC = await result;
733  var itemR = this.Projection(itemT, itemC);
734  context.Done(itemR);
735  }
736  }
737 
738  [Serializable]
739  private sealed class LoopDialog<T> : IDialog<T>
740  {
741  public readonly IDialog<T> Antecedent;
742  public LoopDialog(IDialog<T> antecedent)
743  {
744  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
745  }
746  async Task IDialog<T>.StartAsync(IDialogContext context)
747  {
748  context.Call<T>(this.Antecedent, ResumeAsync);
749  }
750  private async Task ResumeAsync(IDialogContext context, IAwaitable<T> result)
751  {
752  await result;
753  context.Call<T>(this.Antecedent, ResumeAsync);
754  }
755  }
756 
757  [Serializable]
758  private sealed class VoidDialog<T, R> : IDialog<R>
759  {
760  public readonly IDialog<T> Antecedent;
761  public VoidDialog(IDialog<T> antecedent)
762  {
763  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
764  }
765  async Task IDialog<R>.StartAsync(IDialogContext context)
766  {
767  context.Call<T>(this.Antecedent, ResumeAsync);
768  }
769  private async Task ResumeAsync(IDialogContext context, IAwaitable<T> result)
770  {
771  var ignore = await result;
772  context.Wait<R>(ItemReceived);
773  }
774  private async Task ItemReceived(IDialogContext context, IAwaitable<R> result)
775  {
776  var item = await result;
777  context.Done(item);
778  }
779  }
780 
781  [Serializable]
782  private sealed class CatchDialog<T, E> : IDialog<T> where E : Exception
783  {
784  public readonly IDialog<T> Antecedent;
785  public readonly Func<IDialog<T>, E, IDialog<T>> Block;
786  public CatchDialog(IDialog<T> antecedent, Func<IDialog<T>, E, IDialog<T>> block)
787  {
788  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
789  SetField.NotNull(out this.Block, nameof(block), block);
790  }
791  async Task IDialog<T>.StartAsync(IDialogContext context)
792  {
793  context.Call<T>(this.Antecedent, ResumeAsync);
794  }
795  private async Task ResumeAsync(IDialogContext context, IAwaitable<T> result)
796  {
797  try
798  {
799  context.Done(await result);
800  }
801  catch (E error)
802  {
803  context.Call(this.Block(this.Antecedent, error), ResumeAsync);
804  }
805  }
806  }
807 
808  [Serializable]
809  private sealed class DefaultIfExceptionDialog<T, E> : IDialog<T> where E : Exception
810  {
811  public readonly IDialog<T> Antecedent;
812  public DefaultIfExceptionDialog(IDialog<T> antecedent)
813  {
814  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
815  }
816  async Task IDialog<T>.StartAsync(IDialogContext context)
817  {
818  context.Call<T>(this.Antecedent, ResumeAsync);
819  }
820  private async Task ResumeAsync(IDialogContext context, IAwaitable<T> result)
821  {
822  try
823  {
824  context.Done(await result);
825  }
826  catch (E)
827  {
828  context.Done(default(T));
829  }
830  }
831  }
832 
833  [Serializable]
834  private sealed class SwitchDialog<T, R> : IDialog<R>
835  {
836  public readonly IDialog<T> Antecedent;
837  public readonly IReadOnlyList<ICase<T, R>> Cases;
838  public SwitchDialog(IDialog<T> antecedent, IReadOnlyList<ICase<T, R>> cases)
839  {
840  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
841  SetField.NotNull(out this.Cases, nameof(cases), cases);
842  }
843 
844  async Task IDialog<R>.StartAsync(IDialogContext context)
845  {
846  context.Call<T>(Antecedent, AfterAntecedent);
847  }
848 
849  private async Task AfterAntecedent(IDialogContext context, IAwaitable<T> result)
850  {
851  var itemT = await result;
852  R itemR = default(R);
853  foreach (var condition in this.Cases)
854  {
855  if (condition.Condition(itemT))
856  {
857  itemR = condition.Selector(context, itemT);
858  break;
859  }
860  }
861  context.Done(itemR);
862  }
863  }
864 
872  [Serializable]
873  private sealed class ReturnDialog<T> : IDialog<T>
874  {
875  public readonly T Value;
876 
877  public ReturnDialog(T value)
878  {
879  this.Value = value;
880  }
881 
882  public async Task StartAsync(IDialogContext context)
883  {
884  context.Done(Value);
885  }
886  }
887 
888  [Serializable]
889  private sealed class WhileDialog<T> : IDialog<T>
890  {
891  public readonly IDialog<T> Zero;
892  public readonly Func<T, IDialog<bool>> Test;
893  public readonly Func<T, IDialog<T>> Body;
894  public WhileDialog(IDialog<T> zero, Func<T, IDialog<bool>> test, Func<T, IDialog<T>> body)
895  {
896  SetField.NotNull(out this.Zero, nameof(Zero), zero);
897  SetField.NotNull(out this.Test, nameof(Test), test);
898  SetField.NotNull(out this.Body, nameof(Body), body);
899  }
900 
901  async Task IDialog<T>.StartAsync(IDialogContext context)
902  {
903  context.Call(this.Zero, this.AfterZeroOrBody);
904  }
905 
906  private T item = default(T);
907  private async Task AfterZeroOrBody(IDialogContext context, IAwaitable<T> result)
908  {
909  this.item = await result;
910  var test = this.Test(this.item);
911  context.Call(test, this.AfterTest);
912  }
913 
914  private async Task AfterTest(IDialogContext context, IAwaitable<bool> result)
915  {
916  if (await result)
917  {
918  var body = this.Body(this.item);
919  context.Call(body, this.AfterZeroOrBody);
920  }
921  else
922  {
923  context.Done(this.item);
924  }
925  }
926  }
927 
928  [Serializable]
929  private sealed class FoldDialog<T> : IDialog<T>
930  {
931  public readonly IDialog<IEnumerable<IDialog<T>>> Antecedent;
932  public readonly Func<T, T, T> Folder;
933  public FoldDialog(IDialog<IEnumerable<IDialog<T>>> antecedent, Func<T, T, T> folder)
934  {
935  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
936  SetField.NotNull(out this.Folder, nameof(folder), folder);
937  }
938  async Task IDialog<T>.StartAsync(IDialogContext context)
939  {
940  context.Call(this.Antecedent, this.AfterAntecedent);
941  }
942  private IReadOnlyList<IDialog<T>> items;
943  private async Task AfterAntecedent(IDialogContext context, IAwaitable<IEnumerable<IDialog<T>>> result)
944  {
945  this.items = (await result).ToArray();
946  await Iterate(context);
947  }
948  private int index = 0;
949  private T folded = default(T);
950  private async Task Iterate(IDialogContext context)
951  {
952  if (this.index < this.items.Count)
953  {
954  var child = this.items[this.index];
955  context.Call(child, AfterItem);
956  }
957  else
958  {
959  context.Done(this.folded);
960  }
961  }
962  private async Task AfterItem(IDialogContext context, IAwaitable<T> result)
963  {
964  var itemT = await result;
965  if (this.index == 0)
966  {
967  this.folded = itemT;
968  }
969  else
970  {
971  this.folded = this.Folder(this.folded, itemT);
972  }
973 
974  ++this.index;
975 
976  await this.Iterate(context);
977  }
978  }
979 
980  [Serializable]
981  private sealed class WithScorableDialog<T, Item, Score> : DelegatingScorable<Item, Score>, IDialog<T>
982  {
983  public readonly IDialog<T> Antecedent;
984  public WithScorableDialog(IDialog<T> antecedent, IScorable<Item, Score> scorable)
985  : base(scorable)
986  {
987  SetField.NotNull(out this.Antecedent, nameof(antecedent), antecedent);
988  }
989  async Task IDialog<T>.StartAsync(IDialogContext context)
990  {
991  context.Call<T>(this.Antecedent, ResumeAsync);
992  }
993  private async Task ResumeAsync(IDialogContext context, IAwaitable<T> result)
994  {
995  context.Done(await result);
996  }
997  }
998  }
999 
1008  public delegate R ContextualSelector<in T, R>(IBotContext context, T item);
1009 
1015  public interface ICase<in T, R>
1016  {
1020  Func<T, bool> Condition { get; }
1024  ContextualSelector<T, R> Selector { get; }
1025  }
1026 
1030  [Serializable]
1031  public class Case<T, R> : ICase<T, R>
1032  {
1033  public Func<T, bool> Condition { get; protected set; }
1034  public ContextualSelector<T, R> Selector { get; protected set; }
1035 
1036  protected Case()
1037  {
1038  }
1039 
1045  public Case(Func<T, bool> condition, ContextualSelector<T, R> selector)
1046  {
1047  SetField.CheckNull(nameof(condition), condition);
1048  this.Condition = condition;
1049  SetField.CheckNull(nameof(selector), selector);
1050  this.Selector = selector;
1051  }
1052  }
1053 
1060  [Serializable]
1061  public sealed class RegexCase<R> : Case<string, R>
1062  {
1063  private readonly Regex Regex;
1064 
1070  public RegexCase(Regex regex, ContextualSelector<string, R> selector)
1071  {
1072  SetField.CheckNull(nameof(selector), selector);
1073  this.Selector = selector;
1074  SetField.NotNull(out this.Regex, nameof(regex), regex);
1075  this.Condition = this.IsMatch;
1076  }
1077 
1078  private bool IsMatch(string text)
1079  {
1080  return this.Regex.Match(text).Success;
1081  }
1082  }
1083 
1087  [Serializable]
1088  public sealed class DefaultCase<T, R> : Case<T, R>
1089  {
1094  public DefaultCase(ContextualSelector<T, R> selector)
1095  : base(obj => true, selector)
1096  {
1097  }
1098  }
1099 }
Task StartAsync(IDialogContext context)
The start of the code that represents the conversational dialog.
The context for the execution of a dialog&#39;s conversational process.
The default implementation of ICase<T, R>.
Definition: Chain.cs:1031
DefaultCase(ContextualSelector< T, R > selector)
Constructs the default case for switch.
Definition: Chain.cs:1094
Namespace for the Microsoft Bot Connector SDK.
The exception that is thrown when the where is canceled.
Definition: Chain.cs:633
The default case for switch. ICase<T, R>
Definition: Chain.cs:1088
The regex case for switch.
Definition: Chain.cs:1061
Namespace for internal scorable implementation that is not useful for most developers and may change ...
Namespace for scorable interfaces, classes and compositions.
Namespace for internal machinery that is not useful for most developers and may change in the future...
Task PostAsync(IMessageActivity message, CancellationToken cancellationToken=default(CancellationToken))
Post a message to be sent to the user.
delegate Task StartAsync(IDialogContext context)
Encapsulate a method that represents the code to start a dialog.
Namespace for internal Dialogs machinery that is not useful for most developers and may change in the...
A fluent, chainable interface for IDialogs.
Definition: Chain.cs:52
RegexCase(Regex regex, ContextualSelector< string, R > selector)
Constructs a case based on a regular expression.
Definition: Chain.cs:1070
Case(Func< T, bool > condition, ContextualSelector< T, R > selector)
Constructs a case.
Definition: Chain.cs:1045
static IDialog< IMessageActivity > PostToChain()
Post the message from the user to Chain.
Definition: Chain.cs:129
Namespace for the internal fibers machinery that is not useful for most developers and may change in ...
Core namespace for Dialogs and associated infrastructure.
The stack of dialogs in the conversational process.
Definition: IDialogTask.cs:46
Root namespace for the Microsoft Bot Builder SDK.