/// <summary> /// Select an applicable function without executing it /// </summary> /// <typeparam name="T">Generic type</typeparam> /// <param name="context">gate context</param> /// <param name="mode">mode to run the gated code in</param> /// <param name="conditionalPredicate">The conditional predicate.</param> /// <param name="functions">array of gated functions</param> /// <param name="keepContext">controls whether async operation should keep the synchronization context</param> /// <remarks> /// keep context should be set to true in code which is async end to end, /// and false in other cases to avoid deadlocking /// </remarks> /// <returns>a function to perform, never null</returns> public static Func <Task <T> > SelectFunction <T>(this IGateContext context, GatedCode.Modes mode, Func <bool> conditionalPredicate, GatedAsyncFunc <T>[] functions, bool keepContext) { Tuple <GatedAsyncFunc <T>, IGate[]> action = SelectCode(context, conditionalPredicate, functions); if (action?.Item1 == null) { return(() => Task.FromResult(default(T))); } return(async() => { context.EnterScopes(mode, action.Item2); try { if (action.Item1.Func != null) { return await action.Item1.Func().ConfigureAwait(keepContext); } } finally { context.ExitScopes(mode, action.Item2); } return default(T); }); }
/// <summary> /// Select an applicable function without executing it /// </summary> /// <param name="context">gate context</param> /// <param name="mode">mode to run the gated code in</param> /// <param name="functions">array of gated functions</param> /// <param name="keepContext">controls whether async operation should keep the synchronization context</param> /// <remarks> /// keep context should be set to true in code which is async end to end, /// and false in other cases to avoid deadlocking /// </remarks> /// <returns>a function to perform, never null</returns> public static Func <T, Task <T> > SelectFunction <T>(this IGateContext context, GatedCode.Modes mode, GatedAsyncFunc <T, T>[] functions, bool keepContext) { Tuple <GatedAsyncFunc <T, T>, IGate[]> action = SelectCode(context, null, functions); if (action != null && action.Item1 != null) { return(async t => { context.EnterScopes(mode, action.Item2); try { if (action.Item1.Func != null) { return await action.Item1.Func(t).ConfigureAwait(keepContext); } } finally { context.ExitScopes(mode, action.Item2); } return t; }); } return(Task.FromResult); }
/// <summary> /// Exit a scope for gated code /// </summary> /// <param name="context">the gate context</param> /// <param name="mode">mode that code should be performed (e.g. scoped or not)</param> /// <param name="gate">the gate to exit scope for</param> public static void ExitScope(this IGateContext context, GatedCode.Modes mode, IGate gate) { if (context.ShouldBeScoped((mode & GatedCode.Modes.ExitScope), gate)) { context.ExitScope(gate); } }
/// <summary> /// Should the context scope the code /// </summary> /// <param name="context">gate context</param> /// <param name="mode">preferred mode for code</param> /// <param name="gate">gate to run</param> /// <returns>true if the context should scope the code, false otherwise</returns> private static bool ShouldBeScoped(this IGateContext context, GatedCode.Modes mode, IGate gate) { if (gate == null) { return(false); } if (context == null) { return(false); } if ((mode & GatedCode.Modes.EnterScope) == GatedCode.Modes.EnterScope || (mode & GatedCode.Modes.ExitScope) == GatedCode.Modes.ExitScope) { return(true); } if (context.Request != null && context.Request.RequestedGateIds != null && context.Request.RequestedGateIds.Contains(gate.Name)) { return(true); } return(false); }
/// <summary> /// Select an applicable function without executing it /// </summary> /// <param name="context">gate context</param> /// <param name="mode">mode to run the gated code in</param> /// <param name="functions">array of gated functions</param> /// <returns>a function to perform, never null</returns> public static Func <T, T> SelectFunction <T>(this IGateContext context, GatedCode.Modes mode, params GatedFunc <T, T>[] functions) { Tuple <GatedFunc <T, T>, IGate[]> action = SelectCode(context, null, functions); if (action != null && action.Item1 != null) { return(new Func <T, T>( (t) => { context.EnterScopes(mode, action.Item2); try { if (action.Item1.Func != null) { return action.Item1.Func(t); } } finally { context.ExitScopes(mode, action.Item2); } return t; })); } return(new Func <T, T>((t) => t)); }
/// <summary> /// Select an applicable action without executing it /// </summary> /// <param name="context">gate context</param> /// <param name="mode">mode to run the gated code in</param> /// <param name="conditionalPredicate">The conditional predicate.</param> /// <param name="actions">array of gated actions</param> /// <param name="keepContext">controls whether async operation should keep the synchronization context</param> /// <remarks> /// keep context should be set to true in code which is async end to end, /// and false in other cases to avoid deadlocking /// </remarks> /// <returns>an action to perform, never null</returns> public static Func <Task> SelectAction(this IGateContext context, GatedCode.Modes mode, Func <bool> conditionalPredicate, GatedAsyncAction[] actions, bool keepContext = false) { Tuple <GatedAsyncAction, IGate[]> action = SelectCode(context, conditionalPredicate, actions); if (action?.Item1 == null) { return(s_noOpAction); } return(async() => { context.EnterScopes(mode, action.Item2); try { if (action.Item1?.Action != null) { await action.Item1.Action().ConfigureAwait(keepContext); } } finally { context.ExitScopes(mode, action.Item2); } }); }
/// <summary> /// Select an applicable function without executing it /// </summary> /// <typeparam name="T">Generic type</typeparam> /// <param name="context">gate context</param> /// <param name="mode">mode to run the gated code in</param> /// <param name="conditionalPredicate">The conditional predicate.</param> /// <param name="functions">array of gated functions</param> /// <returns>a function to perform, never null</returns> public static Func <T> SelectFunction <T>(this IGateContext context, GatedCode.Modes mode, Func <bool> conditionalPredicate, params GatedFunc <T>[] functions) { Tuple <GatedFunc <T>, IGate[]> action = SelectCode(context, conditionalPredicate, functions); if (action != null && action.Item1 != null) { return(new Func <T>( () => { context.EnterScopes(mode, action.Item2); try { if (action.Item1.Func != null) { return action.Item1.Func(); } } finally { context.ExitScopes(mode, action.Item2); } return default(T); })); } return(new Func <T>(() => default(T))); }
/// <summary> /// Is the gate combination applicable /// </summary> /// <param name="context">gate context</param> /// <param name="gate">applicable gate, always null</param> /// <returns>true if no gates are applicable, false otherwise</returns> public override bool IsApplicable(IGateContext context, out IGate[] gate) { bool isApplicable = !base.IsApplicable(context, out gate); gate = null; return(isApplicable); }
/// <summary> /// Perform each function that is applicable in the current gate context. The input parameter /// is passed to the first applicable function, and the result of that function is used as the input /// for the next applicable function, etc. This allows for chaining multiple functions, each having /// an opportunity to modify the input. /// </summary> /// <param name="context">gate context</param> /// <param name="input">the input object</param> /// <param name="mode">mode to run the gated code in</param> /// <param name="functions">array of gated functions</param> /// <typeparam name="T">the return type of the function</typeparam> /// <returns>instance of T (default if no applicable action found)</returns> public static T PerformEachFunction <T>(this IGateContext context, GatedCode.Modes mode, T input, params GatedFunc <T, T>[] functions) { Array.ForEach(functions, (function) => { input = SelectFunction(context, mode, function)(input); } ); return(input); }
/// <summary> /// Enters a collection of scopes in a nested way. /// </summary> /// <param name="context">The gate context.</param> /// <param name="mode">The mode that code should be performed.</param> /// <param name="gates">The array of gates for the scope.</param> public static void EnterScopes(this IGateContext context, GatedCode.Modes mode, IGate[] gates) { if (gates == null) { return; } foreach (IGate gate in gates) { EnterScope(context, mode, gate); } }
/// <summary> /// Exits a collection of scopes in a reverse order. /// </summary> /// <param name="context">The gate context.</param> /// <param name="mode">The mode that code should be performed.</param> /// <param name="gates">The array of gates for exiting the scope.</param> public static void ExitScopes(this IGateContext context, GatedCode.Modes mode, IGate[] gates) { if (gates == null) { return; } for (int i = gates.Length - 1; i >= 0; i--) { ExitScope(context, mode, gates[i]); } }
/// <summary> /// Is the gate combination applicable /// </summary> /// <param name="context">gate context</param> /// <param name="gates">applicable gate, always set if the gate combination is applicable</param> /// <returns>true if at least one gate is applicable</returns> public override bool IsApplicable(IGateContext context, out IGate[] gates) { gates = null; if (context == null) { return(false); } IGate gate = Array.Find(Gates, t => context.IsGateApplicable(t)); if (gate != null) { gates = new[] { gate }; return(true); } return(false); }
/// <summary> /// Executes the given function in the scope of the gates associated with it /// </summary> /// <remarks> /// All code invoked by the function will consider the gates active. /// </remarks> /// <typeparam name="T">type of the function's result</typeparam> /// <param name="gateContext">gate context</param> /// <param name="gatedFunction">gated function to execute</param> /// <returns>result returned by the function</returns> public static T PerformGatedFunctionInScope <T>(this IGateContext gateContext, GatedFunc <T> gatedFunction) { foreach (IGate gate in gatedFunction.Gates.Gates) { gateContext.EnterScope(gate); } try { return(gatedFunction.Func == null ? default(T) : gatedFunction.Func()); } finally { foreach (IGate gate in gatedFunction.Gates.Gates) { gateContext.ExitScope(gate); } } }
/// <summary> /// Runs the given action in the scope of the gates associated with it /// </summary> /// <remarks> /// All code invoked by the action will consider the gates active. /// </remarks> /// <param name="gateContext">gate context</param> /// <param name="gatedAction">gated action to run</param> public static void PerformGatedActionInScope(this IGateContext gateContext, GatedAction gatedAction) { foreach (IGate gate in gatedAction.Gates.Gates) { gateContext.EnterScope(gate); } try { gatedAction.Action?.Invoke(); } finally { foreach (IGate gate in gatedAction.Gates.Gates) { gateContext.ExitScope(gate); } } }
/// <summary> /// Select a code /// </summary> /// <typeparam name="T">type of gated code</typeparam> /// <param name="context">gate context</param> /// <param name="conditionalPredicate">The conditional predicate.</param> /// <param name="code">array of gated code</param> /// <returns>Tuple of gated code and applicaple gate.</returns> public static Tuple <T, IGate[]> SelectCode <T>(this IGateContext context, Func <bool> conditionalPredicate, params T[] code) where T : GatedCode { if (context == null || code == null) { return(null); } Lazy <bool> conditionalPredicateIsFalse = new Lazy <bool>( () => conditionalPredicate != null && !conditionalPredicate()); IGate[] gates = null; T gatedCode = Array.Find(code, t => { if (t != null) { if (t.IsBaselineCode) { return(true); } if (!conditionalPredicateIsFalse.Value) { IGate[] applicableGates; if (t.Gates.IsApplicable(context, out applicableGates)) { gates = applicableGates; return(true); } } } return(false); }); if (gatedCode == null) { return(null); } return(new Tuple <T, IGate[]>(gatedCode, gates)); }
/// <summary> /// Checks for gate applicability /// </summary> /// <param name="context">The current gate context.</param> /// <param name="gates">A collection containing the applicable gates.</param> /// <returns>True if all the gates are applicable, false otherwise.</returns> public override bool IsApplicable(IGateContext context, out IGate[] gates) { gates = null; if (context == null) { return(false); } if (Array.Exists(Gates, t => !context.IsGateApplicable(t))) { return(false); } if (Gates != null && Gates.Length != 0) { gates = Gates; return(true); } return(false); }
/// <summary> /// Select an applicable action without executing it /// </summary> /// <param name="context">gate context</param> /// <param name="mode">mode to run the gated code in</param> /// <param name="conditionalPredicate">The conditional predicate.</param> /// <param name="actions">array of gated actions</param> /// <returns>an action to perform, never null</returns> public static Action SelectAction(this IGateContext context, GatedCode.Modes mode, Func <bool> conditionalPredicate, params GatedAction[] actions) { Tuple <GatedAction, IGate[]> action = SelectCode(context, conditionalPredicate, actions); if (action != null && action.Item1 != null) { return(new Action( () => { context.EnterScopes(mode, action.Item2); try { action.Item1.Action?.Invoke(); } finally { context.ExitScopes(mode, action.Item2); } })); } return(new Action(() => { })); }
/// <summary> /// Perform an action that is applicable in the current gate context /// </summary> /// <param name="context">gate context</param> /// <param name="actions">array of gated actions</param> public static Task PerformAction(this IGateContext context, params GatedAsyncAction[] actions) => SelectAction(context, actions)();
/// <summary> /// Is the gate combination applicable /// </summary> /// <param name="context">gate context</param> /// <param name="gates">applicable gates, can be null even if gate combination is applicable</param> /// <returns>true if the gate combination is applicable</returns> public abstract bool IsApplicable(IGateContext context, out IGate[] gates);
/// <summary> /// Retrieve the requested gates as a string /// </summary> /// <param name="context">gate context</param> /// <param name="separator">optional separator</param> /// <returns>string representation</returns> public static string RequestedGatesAsString(this IGateContext context, string separator = null) { return(context != null && context.Request != null? GatesAsString(context.Request.RequestedGateIds, separator) : null); }
/// <summary> /// Perform a function that is applicable in the current gate context based on the passed predicate. /// </summary> /// <typeparam name="T">Generic type.</typeparam> /// <param name="context">The context.</param> /// <param name="conditionalPredicate">The conditional predicate.</param> /// <param name="functions">The functions.</param> /// <returns>Func to perform, (default if no applicable action found)</returns> public static T PerformConditionalFunction <T>(this IGateContext context, Func <bool> conditionalPredicate, params GatedFunc <T>[] functions) => SelectFunction(context, GatedCode.Modes.Scoped, conditionalPredicate, functions)();
/// <summary> /// Perform an action based on a condition that is specified using predicate. /// </summary> /// <param name="context">The context.</param> /// <param name="conditionalPredicate">The conditional predicate.</param> /// <param name="actions">The actions.</param> public static Task PerformConditionalAction(this IGateContext context, Func <bool> conditionalPredicate, params GatedAsyncAction[] actions) => SelectAction(context, GatedCode.Modes.Scoped, conditionalPredicate, actions)();
/// <summary> /// Retrieve the activated gates as a string /// </summary> /// <param name="context">gate context</param> /// <param name="separator">optional separator</param> /// <returns>string representation</returns> public static string ActivatedGatesAsString(this IGateContext context, string separator = null) { return(context != null && context.ActivatedGates != null? GatesAsString(context.ActivatedGates.Select(gate => gate.Name), separator) : null); }
/// <summary> /// Select an applicable function without executing it /// </summary> /// <param name="context">gate context</param> /// <param name="functions">array of gated functions</param> /// <returns>a function to perform, never null</returns> public static Func <T, T> SelectFunction <T>(this IGateContext context, params GatedFunc <T, T>[] functions) { return(SelectFunction(context, GatedCode.Modes.Scoped, functions)); }
/// <summary> /// Retrieve the current active gates as a string /// </summary> /// <param name="context">gate context</param> /// <param name="separator">optional separator</param> /// <returns>string representation</returns> public static string CurrentGatesAsString(this IGateContext context, string separator = null) { return(context != null? GatesAsString(context.CurrentGates, separator) : null); }
/// <summary> /// Select an applicable function without executing it /// </summary> /// <param name="context">gate context</param> /// <param name="functions">array of gated functions</param> /// <returns>a function to perform, never null</returns> public static Func <Task <T> > SelectFunction <T>(this IGateContext context, params GatedAsyncFunc <T>[] functions) { return(SelectFunction(context, GatedCode.Modes.Scoped, null, functions)); }
/// <summary> /// Select an applicable function without executing it /// </summary> /// <typeparam name="T">Generic type</typeparam> /// <param name="context">gate context</param> /// <param name="mode">mode to run the gated code in</param> /// <param name="conditionalPredicate">The conditional predicate.</param> /// <param name="functions">array of gated functions</param> /// <returns>a function to perform, never null</returns> public static Func <Task <T> > SelectFunction <T>(this IGateContext context, GatedCode.Modes mode, Func <bool> conditionalPredicate, params GatedAsyncFunc <T>[] functions) { return(SelectFunction(context, mode, conditionalPredicate, functions, false)); }
/// <summary> /// Perform an action that is applicable in the current gate context /// </summary> /// <param name="context">gate context</param> /// <param name="mode">mode to run the gated code in</param> /// <param name="actions">array of gated actions</param> public static Task PerformAction(this IGateContext context, GatedCode.Modes mode, params GatedAsyncAction[] actions) => SelectAction(context, mode, null, actions)();
/// <summary> /// Perform an action that is applicable in the current gate context /// </summary> /// <param name="context">gate context</param> /// <param name="gate">single target gate</param> /// <param name="action">action delegate</param> public static Task PerformAction(this IGateContext context, IGate gate, Func <Task> action) => SelectAction(context, new GatedAsyncAction(gate, action))();
/// <summary> /// Select an applicable function without executing it /// </summary> /// <param name="context">gate context</param> /// <param name="mode">mode to run the gated code in</param> /// <param name="functions">array of gated functions</param> /// <returns>a function to perform, never null</returns> public static Func <T, Task <T> > SelectFunction <T>(this IGateContext context, GatedCode.Modes mode, params GatedAsyncFunc <T, T>[] functions) { return(SelectFunction(context, mode, functions, false)); }