CSharp - Other

Common Debug Methods

Debug.WriteLine()

The WriteLine() method is used to output debugging information to the listener (usually the output window). It can accept strings or objects, and optionally attach a category.

Debug.WriteLine("This is a debugging message");
Debug.WriteLine("Debugging information", "Category");


Debug.Assert()

The Assert() method is used to verify that a conditional expression is true. If it is false, an assertion failure is thrown and the program is interrupted during debugging.

int value = 10;
Debug.Assert(value > 0, "value should be greater than 0");


Debug.Fail()

The Fail() method is used to display an error message during debugging, prompting developers about unforeseen circumstances.

Debug.Fail("An unforeseen error occurred");


Debug.Indent() and Debug.Unindent()

These methods are used to adjust the indentation level of the output information, making it easy to maintain a good format in complex debugging information.

Debug.Indent();
Debug.WriteLine("Indent one level");
Debug.Unindent();
Debug.WriteLine("Cancel indentation");


Configuring Listeners

By default, the output of Debug.WriteLine() is displayed in the "Output" window of Visual Studio. We can also configure other listeners, such as TextWriterTraceListener, to write output to a file.

TextWriterTraceListener myListener = new TextWriterTraceListener("debug_output.txt");
Debug.Listeners.Add(myListener);
Debug.WriteLine("Debugging information written to file");
Debug.Flush();

Compiled Queries: Improve Query Performance by Reusing Queries

Problem: Your application is getting slower because it's running the same queries repeatedly, leading to performance issues.


Solution: Compiled Queries are perfect for scenarios where you need to run a similar query multiple times. EF Core usually compiles each LINQ query every time it's used, which adds overhead. With Explicitly Compiled Queries, you can reuse a precompiled version of your query to boost performance.


Here's how to create a compiled query:

public static readonly Func<AppDbContext, int, Product> GetProductById =
    EF.CompileQuery((AppDbContext context, int id) =>
        context.Products.FirstOrDefault(p => p.Id == id));


Now, you can use it like this:

var product = GetProductById(context, 1); // This is efficient for repeated queries


By using compiled queries, you can make your application significantly faster when executing the same type of query repeatedly.


Keywords: EF Core Compiled Queries, optimize EF Core query performance, reuse queries in Entity Framework Core

ConditionalWeakTable - Prevents Memory Leaks

The ConditionalWeakTable<T, TData> prevents memory leaks by allowing garbage collection of keys when they are no longer referenced.


Before (Memory Leak Risk)

Without ConditionalWeakTable, extra data associated with an object keeps it in memory indefinitely.

class MyData
{
    public string Data { get; set; }
}


class Program
{
    static Dictionary<object, MyData> dict = new Dictionary<object, MyData>();


    static void Main()
    {
        var key = new object();
        dict[key] = new MyData { Data = "Some data" };


        key = null; // The object is unreachable, but still held in `dict`
        GC.Collect();
        Console.WriteLine(dict.Count); // Memory leak: `dict` still holds the object
    }
}


After (Memory Leak Fixed)

Using ConditionalWeakTable, the associated data is automatically released when the key is garbage collected.

using System.Runtime.CompilerServices;


class MyData
{
    public string Data { get; set; }
}


class Program
{
    static ConditionalWeakTable<object, MyData> table = new();


    static void Main()
    {
        var key = new object();
        table.Add(key, new MyData { Data = "Some data" });


        key = null; // Now `key` can be garbage collected
        GC.Collect();
        Console.WriteLine(table.Count); // Output: 0, no memory leak
    }
}


Configure Serilog in Program.cs

// Configure Serilog
Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug() // Set minimum log level
    .WriteTo.Console()    // Log to console
    .WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day) // Optional: Log to file
    .Enrich.FromLogContext() // Include contextual information
    .CreateLogger();

builder.Host.UseSerilog(); // Use Serilog as the logging provider

ConfigureAwait(false) Prevents Deadlocks

ConfigureAwait(false) avoids deadlocks in UI and ASP.NET synchronization contexts.


Before (Deadlock Risk)

Blocking the async operation in a UI application causes deadlocks.

public void DoSomething()
{
    var result = GetDataAsync().Result; // Deadlocks if GetDataAsync resumes on UI thread
}


public async Task<string> GetDataAsync()
{
    await Task.Delay(1000);
    return "Hello";
}


After (Deadlock Fixed)

Using ConfigureAwait(false), continuation runs on a thread pool instead of the UI thread.

public void DoSomething()
{
    var result = GetDataAsync().ConfigureAwait(false).GetAwaiter().GetResult(); // No deadlock
}


public async Task<string> GetDataAsync()
{
    await Task.Delay(1000).ConfigureAwait(false);
    return "Hello";
}


Connection Pool Exhaustion ASP.NET Core

services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
        sqlOptions =>
        {
            sqlOptions.MinPoolSize(10);
            sqlOptions.MaxPoolSize(100);
            sqlOptions.CommandTimeout(30);
            sqlOptions.EnableRetryOnFailure(
                maxRetryCount: 3,
                maxRetryDelay: TimeSpan.FromSeconds(30),
                errorNumbersToAdd: null);
            
            // Additional connection resiliency
            sqlOptions.ConnectRetryCount(5);
            sqlOptions.ConnectRetryInterval(TimeSpan.FromSeconds(10));
            sqlOptions.EnableRetryOnFailure();
        }));

// Add health checks
services.AddHealthChecks()
    .AddDbContextCheck<AppDbContext>();

Connection Resiliency in ASP.NET Core

Enable retry logic for transient failures by configuring connection resiliency.


services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
        sqlServerOptions => sqlServerOptions.EnableRetryOnFailure()));

Content-Security-Policy in ASP.NET MVC


Convert HTML to plain text

public static string HTMLToText(string HTMLCode)
{
    // Remove new lines since they are not visible in HTML
    HTMLCode = HTMLCode.Replace("\n", " ");

    // Remove tab spaces
    HTMLCode = HTMLCode.Replace("\t", " ");

    // Remove multiple white spaces from HTML
    HTMLCode = Regex.Replace(HTMLCode, "\\s+", " ");

    // Remove HEAD tag
    HTMLCode = Regex.Replace(HTMLCode, "<head.*?</head>", "", RegexOptions.IgnoreCase | RegexOptions.Singleline);

    // Remove any JavaScript
    HTMLCode = Regex.Replace(HTMLCode, "<script.*?</script>", "", RegexOptions.IgnoreCase | RegexOptions.Singleline);

    // Replace special characters like &, <, >, " etc.
    StringBuilder sbHTML = new StringBuilder(HTMLCode);

    // Note: There are many more special characters, these are just
    // most common. You can add new characters in this arrays if needed
    string[] OldWords = {"&nbsp;", "&amp;", "&quot;", "&lt;", "&gt;", "&reg;", "&copy;", "&bull;", "&trade;","&#39;"};
    string[] NewWords = { " ", "&", "\"", "<", ">", "®", "©", "•", "â„¢", "\'" };
    for (int i = 0; i < OldWords.Length; i++)
    {
        sbHTML.Replace(OldWords[i], NewWords[i]);
    }

    // Check if there are line breaks (<br>) or paragraph (<p>)
    sbHTML.Replace("<br>", "\n<br>");
    sbHTML.Replace("<br ", "\n<br ");
    sbHTML.Replace("<p ", "\n<p ");
       
    // Finally, remove all HTML tags and return plain text
    return System.Text.RegularExpressions.Regex.Replace(sbHTML.ToString(), "<[^>]*>", "");
}

Copying an Array

int[] original = { 1, 2, 3, 4, 5 };
int[] copy = new int[original.Length];
Array.Copy(original, copy, original.Length);
// Result: copy = { 1, 2, 3, 4, 5 }

Could not find a part of the path ... bin\roslyn\csc.exe

Run this in the Package Manager Console:

Update-Package Microsoft.CodeDom.Providers.DotNetCompilerPlatform -r

Count Words in a Sentence

Quickly determine the number of words in a sentence by splitting the string.

string sentence = "Count the number of words in this sentence.";
int wordCount = sentence.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length;
Console.WriteLine(wordCount);


Explanation: Using Split with a space delimiter and the RemoveEmptyEntries option ensures accurate word count even with extra spaces.