August 24, 2026

Fake Logger Factory and Fake Loggers

When using Microsoft Logging this code allows you to create a FakeLoggerFactory that creates named FakeLoggers that all log to one Combined logger. This is useful for unit testing.


// You can define other methods, fields, classes and namespaces here
// What does this do? https://www.tutorialsteacher.com/core/fundamentals-of-logging-in-dotnet-core
/// <summary>
/// FakeLoggerFactory used in Unit testing. It creates loggers that log to a <see cref="CombinedLogging"/> object
/// </summary>
/// <inheritdoc />
public sealed class FakeLoggerFactory : ILoggerFactory
{
    readonly CombinedLogging _combinedLogging = new();

    /// <summary>
    /// Creates a new <see cref="FakeLoggerFactory"/> instance.
    /// </summary>
    public FakeLoggerFactory() { }

    /// <inheritdoc />
    /// <remarks>
    /// This returns a <see cref="FakeLogger2"/> instance of the given category.
    /// </remarks>
    public ILogger CreateLogger(string categoryName)
    {
        string tmp = $"CreateLogger {categoryName}";
        Console.WriteLine(tmp);
        return new FakeLogger2(categoryName, _combinedLogging);
    }

    /// <inheritdoc />
    public void AddProvider(ILoggerProvider provider)
    {
    }

    /// <inheritdoc />
    public void Dispose()
    {
    }

    /// <summary>
    /// Enumerate the combined log entries
    /// </summary>
    /// <returns></returns>
    public IEnumerable<LogEntry> Logs()
    {
        return _combinedLogging.Logs();
    }
}

[ExcludeFromCodeCoverage]
public class FakeLogger2 : ILogger
{
    private CombinedLogging _combinedLogging;
    private readonly string _category;

    /// <summary>
    /// Create a Fake Logger
    /// </summary>
    /// <param name="category">Specifying the category of the logging</param>
    /// <param name="combinedLogging">Combines the logging from all FakeLogger2s</param>
    public FakeLogger2(string category, CombinedLogging combinedLogging)
    {
        _category = category;
        _combinedLogging = combinedLogging;
    }

    public IDisposable? BeginScope<TState>(TState state) where TState : notnull
    {
        return new NoOpDisposable();
    }

    private sealed class NoOpDisposable : IDisposable
    {
        public void Dispose()
        {
        }
    }

    public bool IsEnabled(LogLevel logLevel)
    {
        return true;
    }

    public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
    {
        _combinedLogging.Log(_category, logLevel, eventId, state, exception, formatter);
    }
}

/// <summary>
/// To record a LogEntry
/// </summary>
/// <param name="Level">The LogLevel of the entry</param>
/// <param name="Category">The category of the entry, so an ILogger<T> will
/// log to a category of T where T is the type as a string</param>
/// <param name="Text">Logged text</param>
[ExcludeFromCodeCoverage]
public sealed record LogEntry(LogLevel Level, string Category, string Text)
{
    /// <summary>
    /// For Logger logging type T which has as a fully qualified type name of X.Y.Z, 
    /// ie. ILogger<T> we usually are only interested in the last part (class name), "Z"
    /// </summary>
    /// <returns>Class name part of the Type</returns>
    public string MinimalCategory()
    {
        Debug.Assert(Category is not null, $"Field {nameof(Category)} is null");

        var span = Category.AsSpan();
        int ix = span.LastIndexOf('.'); // Look for last period/full stop character
        if (ix > 0)
        {
            span = span[ix..];
        }
        return span.ToString();
    }

    public override string ToString()
    {
        return $"{DateTime.UtcNow.ToRoundTripFormatString()} - {Level.ToString()[0..3].ToUpperInvariant()} - {MinimalCategory()} - {Text}";
    }
}

/// <summary>
/// Used to combine the logging of all FakeLogger2 loggers
/// </summary>
[ExcludeFromCodeCoverage]
public class CombinedLogging
{
    /// <summary>
    /// Store the logged lines by logging level here in logged order
    /// </summary>
    private readonly List<LogEntry> _allLogs = new();

    public CombinedLogging()
    {
    }

    public void Log<TState>(string category, LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
    {
        string message = "";
        if (formatter != null)
        {
            message += formatter(state, exception);
        }
        var logLeveChar = logLevel.ToString()[0];
        Console.WriteLine($"{logLeveChar} - {category} - {message}");
        _allLogs.Add(new LogEntry(logLevel, category, $"{message}"));
    }

    /// <summary>
    /// Retrieve All Logging
    /// </summary>
    /// <returns>All logging</returns>
    public IEnumerable<LogEntry> Logs()
    {
        foreach (var line in _allLogs)
        {
            yield return line;
        }
    }
}

August 23, 2026

Corrupted State Exceptions

Critical exceptions are exceptions that should not be caught. In C#, exceptions that indicate a catastrophic failure of the runtime or the process are generally known as Corrupted State Exceptions (CSEs). You should avoid catching these because the state of the application is no longer predictable, and attempting to execute code—especially code that allocates memory for logging—can lead to secondary crashes or hang the process.

public static class ExceptionExtender
{
    /*
    The primary exceptions you should avoid catching in a general exception handler are:
    Exception	Reason
    OutOfMemoryException	Logging/documenting the exception may itself fail due to lack of available memory.
    StackOverflowException	The stack is already exhausted; attempting to execute logging code will fail.
    ExecutionEngineException	Represents a critical CLR failure; recovery is not guaranteed. OBSOLETE now.
    FatalExecutionEngineError	Represents a critical CLR failure; recovery is not guaranteed. OBSOLETE now.
    AccessViolationException	Indicates unsafe memory access; logging may be unsafe or unreliable.
    ThreadAbortException	Thrown by the runtime to abort a thread; catching and rethrowing can interfere with CLR cleanup.
    AppDomainUnloadedException	The application domain is being unloaded; recovery is not possible.
    BadImageFormatException	Indicates a corrupted or invalid assembly; recovery is not feasible.
    
    In modern .NET (.NET Core, .NET 5+), most of these are treated as fatal and will not be caught by a standard 
    catch (Exception ex) block unless you specifically decorate your method with the 
    HandleProcessCorruptedStateExceptions attribute. For a general-purpose handler, it is safest to let these 
    propagate and allow the operating system to terminate the process.
    */


    public static bool IsCriticalException(this Exception ex)
    {
        return ex is OutOfMemoryException or
               StackOverflowException or
               // ExecutionEngineException or // OBSOLETE: ExecutionEngineException is obsolete and should not be used in 
               // new code. It was used to indicate a severe error in the execution engine of the .NET runtime, but it has
               // been deprecated and is no longer relevant in modern .NET applications.
               AccessViolationException or
               ThreadAbortException or
               AppDomainUnloadedException or
               BadImageFormatException;
    }
}

Here is an example for using it:


public T? LoadFromJsonFile(string jsonFilePath) where T : class, new()
{
    Debug.Assert(jsonFilePath != null, $"Parameter {nameof(jsonFilePath)} is null");
    T? reconstituted = null;
    try
    {
        using (var stream = _fileSystem.File.OpenRead(jsonFilePath))
        {
            reconstituted = JsonSerializer.Deserialize(stream, _serializerOptions);
        }
    }
    catch (Exception ex) when (!ex.IsCriticalException())
    {
        _logger.Log(LogLevel.Error, CallerContext.Create(), "Exception deserializing JSON from file '{JsonFilePath}' of: {Exception}",
            jsonFilePath, ex);
    }
    return reconstituted;
}

Partially AI Generated

Parsing Extensions

An idea from Nick Chapsis as shown here https://www.youtube.com/watch?v=lqbYURwM0bw, althought I added the TryParse with default value. These extensions that allow you to call the Parse function on a string or span. This will work with all the Basic types because they will have the static ISpanParsable interface defined for them.

public static class ParsableExtensions
{
	// SPAN Parsing
	
    /// <summary>
    /// Parse a span of characters for a given type <typeparamref name="T"/>
    /// </summary>
    /// <typeparam name="T">Type that supports an ISpanParsable<typeparamref name="T"/> interface</typeparam>
    /// <param name="input">span of characters to parse</param>
    /// <param name="fp">format provider</param>
    /// <returns>The parsed value, will throw an exception if the parsing faile</returns>
    public static T Parse<T>(this ReadOnlySpan<char> input, IFormatProvider? fp = null)
      where T : ISpanParsable<T>
    {
        return T.Parse(input, fp);
    }

    /// <summary>
    /// Parse a span of characters for a given type <typeparamref name="T"/>, the parsed value is returned as an out parameter
    /// </summary>
    /// <typeparam name="T">Type that supports an ISpanParsable<typeparamref name="T"/> interface</typeparam>
    /// <param name="input">span of characters to parse</param>
    /// <param name="fp">format provider</param>
    /// <returns>whether the parse succeeded or not</returns>
    public static bool TryParse<T>(this ReadOnlySpan<char> input, IFormatProvider fp, out T? value)
      where T : ISpanParsable<T>
    {
        return T.TryParse(input, fp, out value);
    }

    /// <summary>
    /// Parse a span of characters for a given type <typeparamref name="T"/>
    /// </summary>
    /// <typeparam name="T">Type that supports an ISpanParsable<typeparamref name="T"/> interface</typeparam>
    /// <param name="input">span of characters to parse</param>
    /// <param name="defaultValue">The value to return if the parsing failed</param>
    /// <returns>The parsed value if the span was successfully parsed, the default value otherwise</returns>
    public static T Parse<T>(this ReadOnlySpan<char> input, T defaultValue)
      where T : ISpanParsable<T>
    {
        T result = T.TryParse(input, null, out T? value) ? value : defaultValue;
        return result;
    }

	/////////////////////////////////////////////////////////////////////////////////
	// STRING parsing

	/// <summary>
	/// Parse a string for a given type <typeparamref name="T"/>
	/// </summary>
	/// <typeparam name="T">Type that supports an ISpanParsable<typeparamref name="T"/> interface</typeparam>
	/// <param name="input">string to parse</param>
	/// <param name="fp">format provider</param>
	/// <returns>The parsed value, will throw an exception if the parsing faile</returns>
	public static T Parse<T>(this string input, IFormatProvider? fp = null)
	  where T : ISpanParsable<T>
	{
		return T.Parse(input, fp);
	}

	/// <summary>
	/// Parse a string for a given type <typeparamref name="T"/>, the parsed value is returned as an out parameter
	/// </summary>
	/// <typeparam name="T">Type that supports an ISpanParsable<typeparamref name="T"/> interface</typeparam>
	/// <param name="input">string to parse</param>
	/// <param name="fp">format provider</param>
	/// <returns>whether the parse succeeded or not</returns>
	public static bool TryParse<T>(this string input, IFormatProvider fp, out T? value)
	  where T : ISpanParsable<T>
	{
		return T.TryParse(input, fp, out value);
	}

	/// <summary>
	/// Parse a string for a given type <typeparamref name="T"/>
	/// </summary>
	/// <typeparam name="T">Type that supports an ISpanParsable<typeparamref name="T"/> interface</typeparam>
	/// <param name="input">string to parse</param>
	/// <param name="defaultValue">The value to return if the parsing failed</param>
	/// <returns>The parsed value if the string was successfully parsed, the default value otherwise</returns>
	public static T Parse<T>(this string input, T defaultValue)
	  where T : ISpanParsable<T>
	{
		T result = T.TryParse(input, null, out T? value) ? value : defaultValue;
		return result;
	}
}

Here are some examples on how to call them. I have used the Shouldy package to perform the assertions.

int resSpan = "  54 ".AsSpan().Parse(); // Span Parse
resSpan.ShouldEqual(54);

int resStr = " 27  ".Parse();   // String parse
resStr.ShouldEqual(27);
	
var action = () => { int res2Str = "   ".Parse(); }; // String parse that throws a format exception
action.ShouldThrow(); 

int res2Span = "   ".AsSpan().Parse(-1); // Span parse with default value
res2Span.ShouldEqual(-1);

August 21, 2026

Carriage Return and Line Feed

I always forget these so here is a note so that I can look them up easily. You can get the default newline charaters for the current system using the "Environment.NewLine".
Carriage Return (CR, '\r', ASCII 13): A Carriage return, takes the paper carriage back to the beginning of the line. In computing this control character repositions the cursor to the beginning of the line without going down to the next line.
Line Feed (LF, '\n', ASCII 10): In a typewriter, the line feed will help advance the paper by one line. In computing, this tip moves the cursor down to the next line without going back to the start of the line again.
On Windows it is Carriage return, Line feed or "\r\n"
I believe Linux and Unix and Mac OS (?) use "\n"

June 9, 2026

Windows Extended Length Path

The \\?\ (pronounced “extended-length path” or “verbatim path”) syntax is a Windows path prefix that tells the system to treat the path literally and bypass normal Windows path processing (path normalization, MAX_PATH length limit, and some path parsing rules). Key points:

Purpose

  • Allows paths longer than MAX_PATH (260 characters).
  • Prevents the system from interpreting backslashes, relative segments (., ..), or expanding device names; the path is taken verbatim.

Syntax

  • For local paths: start with \\?\ followed immediately by a drive letter and a colon, e.g.: \\?\C:\very\long\path...
  • For UNC/network paths: use the \\?\UNC\ form instead of \\server\share, e.g.: \\?\UNC\server\share\path...
  • Do not mix forward slashes; use backslashes.

Limits and behavior

  • Removes the MAX_PATH ~260-character limit for most APIs that accept Win32 file paths when used with wide-char (UTF-16) APIs.
  • Some Win32 APIs, shells, and libraries do not accept \\?\ paths; some higher-level frameworks (older .NET, Windows Explorer) may not handle them.
  • Certain parse rules are disabled: the path is not normalized (so trailing dots/spaces are preserved) and long relative components won’t be collapsed.
  • You must use absolute paths; relative paths with \\?\ are not supported.
  • For device paths (NT namespace) a different prefix \\?\GLOBALROOT\ or \\.\ may be used for special device access.

Unicode

  • Typically used with wide-character (W) Win32 APIs (UTF-16). Passing UTF-8/ANSI APIs can be problematic.

Examples

  • Long local path: \\?\C:\very\long\directory... ( > 260 chars )
  • Long UNC path: \\?\UNC\myserver\myshare\folder\file.txt

Common pitfalls

  • Some Windows APIs (including many shell APIs) reject \\?\ paths.
  • Don’t use trailing backslash immediately after \\?, e.g., \\?\C:\ is fine but be careful with extra slashes.
  • When interop with libraries/frameworks, strip or add the prefix only at the last moment when calling Win32 functions.

C# example — using the \\?\ (verbatim/extended-length) path prefix

      using System;
using System.IO;
using System.Text;

class Program
{
    // Helper: convert a normal absolute path to an extended-length path for local drive or UNC
    static string ToExtendedLengthPath(string path)
    {
        if (string.IsNullOrWhiteSpace(path))
            throw new ArgumentException(nameof(path));

        // Normalize separators
        path = Path.GetFullPath(path);

        // If UNC path (\\server\share\...), convert to \\?\UNC\server\share\...
        if (path.StartsWith(@"\\"))
        {
            return @"\\?\UNC\" + path.Substring(2);
        }

        // Otherwise (drive letter) prefix with \\?\
        return @"\\?\" + path;
    }

    static void Main()
    {
        // Example long folder path (ensure this exists or create it)
        string longFolder = @"C:\example\very\long\path\that\..."; // replace with real long path
        string extended = ToExtendedLengthPath(longFolder);

        Console.WriteLine("Extended path: " + extended);

        // Create directory using extended path
        Directory.CreateDirectory(extended);
        Console.WriteLine("Directory created.");

        // Create a file inside that directory
        string file = Path.Combine(extended, "test.txt");
        using (var fs = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None))
        using (var sw = new StreamWriter(fs, Encoding.UTF8))
        {
            sw.WriteLine("Hello from extended-length path!");
        }
        Console.WriteLine("File written.");

        // Read the file back
        using (var sr = new StreamReader(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read), Encoding.UTF8))
        {
            Console.WriteLine("File contents:");
            Console.WriteLine(sr.ReadToEnd());
        }
    }
}

    

Notes:

  • Use Path.GetFullPath to ensure an absolute path before adding the prefix.
  • For UNC paths (start with \\), convert to \\?\UNC... as shown.
  • Many .NET APIs work with \\?\ paths on modern runtimes, but some higher-level APIs or components (older frameworks, Windows shell) may not.

AI Generated

June 8, 2026

Datasets in Html and Javascript

This is a sample to show how to use datasets to put data into the html and how to access it

<button data-sandwich="tuna" data-topping="tomato" data-snack="cookies">Place Order</button>
Here is the Javascript to access the dataset on the button:

let btn = document.querySelector('button');

let dataset = btn.dataset; // Access "data-" attributes here
console.log("All dataset attributes: ");
console.log(dataset); 

let sandwich = btn.dataset.sandwich; // Access individually as properties of "dataset"
console.log("sandwich=" + sandwich);

let {topping, snack} = btn.dataset; // Access using tuples
console.log(sandwich, topping, snack);

let snack2 = btn.dataset["snack"]; // Access the properties dynamically
console.log("snack2=" + snack);

btn.dataset.side = 'chips'; // Add more dataset attributes
console.log(dataset); 
 
console.log("All done"); 
The code can be accessed here: https://codepen.io/capitanacerimmer/pen/JobbWYa?editors=1111
Note that the console output is not activated by the button itself

March 30, 2026

C# Compiler "CallerArgumentExpression"

Here is an example usage of the "CallerArgumentExpression". It allows you to capture a copy of some compiled code that evaluates to a value expression


void Main()
{
    var x = 34;
    var y = 23;
    Assert.That(x > y, $"{x} > {y}");
    x = 20;
    Assert.That(x > y, $"{x} > {y}");
}

// You can define other methods, fields, classes and namespaces here
public static class Assert
{
    public static void That(
         bool condition,
         string message = "",
         [CallerArgumentExpression("condition")] string expression = "",
         [CallerMemberName] string memberName = "",
         [CallerFilePath] string sourceFilePath = "",
         [CallerLineNumber] int sourceLineNumber = 0)
    {
        if (!condition)
        {
            var fullMsg = $"{sourceFilePath}:{sourceLineNumber} {memberName} - Assertion \"{expression}\" evaluated to FALSE: {message}";
            Trace.WriteLine(fullMsg);
            Console.WriteLine(fullMsg);
        }
    }
}

In this case the expression "x > y" that is passed in as the value of "bool condition" parameter is inserted by the compiler into the "string expression" parameter. In this example, the first Assert passes and outputs nothing but the second one outputs the following:

C:\...\Temp\LINQPad8\_hxysxhku\ewejlm\LINQPadQuery:7 Main - Assertion "x > y" evaluated to FALSE: 20 > 23