< Summary

Information
Class: AmbientServices.ContextMutator
Assembly: AmbientServices
File(s): /home/runner/work/AmbientServices/AmbientServices/AmbientServices/Helpers/ContextMutator.cs
Tag: 332_35464845198
Line coverage
100%
Covered lines: 5
Uncovered lines: 0
Coverable lines: 5
Total lines: 114
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
ApplyContextChanges()100%11100%

File(s)

/home/runner/work/AmbientServices/AmbientServices/AmbientServices/Helpers/ContextMutator.cs

#LineLine coverage
 1using System;
 2
 3namespace AmbientServices;
 4
 5/// <summary>
 6/// A class that edits the caller context *after* an asynchronous operation completes so that subsequent calls inherit t
 7/// which is not the case if the context is edited in the asynchronous operation itself.
 8/// Have the asynchronous function return this type (or a <see cref="ContextMutator{T}"/>) and call <see cref="ApplyCont
 9/// It is *not* possible to wrap this logic and thereby simplify calling the asynchronous function and then calling <see
 10/// </summary>
 11/// <remarks>
 12/// <pitch>Solves the async context-mutation problem: an <see cref="System.Threading.AsyncLocal{T}"/> write made <em>ins
 13/// <pledge>The mutation happens only when the <em>caller</em> invokes <see cref="ApplyContextChanges"/>, and it applies
 14/// <plan>Wraps a single caller-supplied <see cref="Action"/>; the class adds no state or logic beyond signaling the cal
 15/// <priority>
 16/// 1. Solving the problem at all over an API that cannot be misused: the calling convention is genuinely fragile — appl
 17/// 2. Naming the convention in the type over hiding it: the class carries no state or logic and exists mainly so the co
 18/// </priority>
 19/// </remarks>
 20public sealed class ContextMutator
 21{
 22    private readonly Action _applyContextChanges;
 23
 24    /// <summary>
 25    /// Constructs the context editor with actions to be executed on return.
 26    /// </summary>
 27    /// <param name="applyContextChanges">The action to call after returning from the asynchronous function.</param>
 228    public ContextMutator(Action applyContextChanges)
 29    {
 230        _applyContextChanges = applyContextChanges;
 231    }
 32    /// <summary>
 33    /// Calls the context editing action to be executed after returning from the asynchronous function.
 34    /// </summary>
 35    public void ApplyContextChanges()
 36    {
 237        _applyContextChanges.Invoke();
 238    }
 39}
 40
 41/// <summary>
 42/// A class that temporarily changes the caller context *after* an asynchronous operation completes so that subsequent c
 43/// It is *not* possible to wrap this logic and thereby simplify calling the asynchronous function and then calling <see
 44/// </summary>
 45/// <remarks>
 46/// <pitch>The scoped variant of <see cref="ContextMutator"/>: applies a context change in the caller's frame and undoes
 47/// <pledge>Follows the <see cref="ContextMutator"/> calling convention — apply must be invoked by the frame that should
 48/// <plan>Wraps a caller-supplied apply <see cref="Action"/> and revert <see cref="Action"/>; no other state.</plan>
 49/// </remarks>
 50public sealed class TemporaryContextMutator: IDisposable
 51{
 52    private readonly Action _applyContextChanges;
 53    private readonly Action _revertContextChanges;
 54
 55    /// <summary>
 56    /// Constructs the context editor with actions to be executed on return.
 57    /// </summary>
 58    /// <param name="applyContextChanges">The action to call after returning from the asynchronous function.</param>
 59    /// <param name="revertContextChanges">The action to call when the temporary changes are no longer desired.</param>
 60    public TemporaryContextMutator(Action applyContextChanges, Action revertContextChanges)
 61    {
 62        _applyContextChanges = applyContextChanges;
 63        _revertContextChanges = revertContextChanges;
 64    }
 65    /// <summary>
 66    /// Calls the context editing action to be executed after returning from the asynchronous function.
 67    /// </summary>
 68    /// <returns>The <see cref="TemporaryContextMutator"/> instance, in case the caller wants to chain the call to this 
 69    public TemporaryContextMutator ApplyContextChanges()
 70    {
 71        _applyContextChanges.Invoke();
 72        return this;
 73    }
 74    /// <summary>
 75    /// Reverts the context changes applied by <see cref="ApplyContextChanges"/>.
 76    /// </summary>
 77    public void Dispose()
 78    {
 79        _revertContextChanges.Invoke();
 80    }
 81}
 82
 83/// <summary>
 84/// A class that edits the caller context *after* an asynchronous operation completes so that subsequent calls inherit t
 85/// which is not the case if the context is edited in the asynchronous operation itself.
 86/// Have the asynchronous function return this type (or a <see cref="ContextMutator"/>) and call <see cref="ApplyContext
 87/// It is *not* possible to wrap this logic and thereby simplify calling the asynchronous function and then calling <see
 88/// </summary>
 89/// <remarks>
 90/// <pitch>The value-returning variant of <see cref="ContextMutator"/>, for asynchronous functions that need to both mut
 91/// <pledge>Identical to the <see cref="ContextMutator"/> contract — the caller's frame must invoke <see cref="ApplyCont
 92/// <plan>Wraps a single caller-supplied <see cref="Func{T}"/>; no other state.</plan>
 93/// </remarks>
 94public sealed class ContextMutator<T>
 95{
 96    private readonly Func<T> _applyContextChanges;
 97
 98    /// <summary>
 99    /// Constructs the context editor with actions to be executed on return (and possibly dispose).
 100    /// </summary>
 101    /// <param name="applyContextChanges">The action to call after returning from the asynchronous function.</param>
 102    public ContextMutator(Func<T> applyContextChanges)
 103    {
 104        _applyContextChanges = applyContextChanges;
 105    }
 106    /// <summary>
 107    /// Calls the context editing action to be executed after returning from the asynchronous function.
 108    /// </summary>
 109    /// <returns>The result of the function.</returns>
 110    public T ApplyContextChanges()
 111    {
 112        return _applyContextChanges.Invoke();
 113    }
 114}