CSharp - Other

Common Attributes

[Obsolete] Attribute

Marks a program element as obsolete or deprecated, generating a warning or error when used.

[Obsolete("This method is deprecated, use NewMethod instead.")]
public void OldMethod() { }


[Serializable] Attribute

Indicates that an object can be serialized for storage or transmission.

[Serializable]
public class SerializableObject { }


[DataContract] and [DataMember] Attributes

Used in Windows Communication Foundation (WCF) for specifying serializable classes and members.

[DataContract]
public class Person
{
    [DataMember]
    public string FirstName { get; set; }
}


[DllImport] Attribute

Used for calling functions from unmanaged code.

class Program
{
    [DllImport("user32.dll")]
    public static extern int MessageBox(int hWnd, string text, string caption, int type);
}


[Conditional] Attribute

Specifies that a method should be called conditionally based on compilation symbols.

class Program
{
    [Conditional("DEBUG")]
    public static void DebugMethod()
    {
        // This method will only be included if DEBUG is defined.
    }
}


[ThreadStatic] Attribute

Declares thread-local storage for a field, giving each thread its own copy.

public class ThreadLocalExample
{
    [ThreadStatic]
    public static int threadLocalValue;
}


[Serializable] Attribute

Marks a class as serializable for binary serialization.

[Serializable]
public class SerializableClass { }


[DefaultValue] Attribute

Specifies the default value for a property or field.

public class Example
{
    [DefaultValue(42)]
    public int MyProperty { get; set; }
}


[Description] Attribute

Provides a description for a property, event, or component.

public class Example
{
    [Description("This is a description.")]
    public int MyProperty { get; set; }
}


[DisplayName] Attribute

Specifies a display name for a property or event.

public class Example
{
    [DisplayName("Display Name")]
    public int MyProperty { get; set; }
}


[Browsable] Attribute

Indicates whether a property or event should be displayed in a designer.

public class Example
{
    [Browsable(false)]
    public int MyProperty { get; set; }
}


[NonSerialized] Attribute

Prevents a field from being serialized.

[Serializable]
public class SerializableObject
{
    [NonSerialized]
    public int NonSerializableField;
}


[XmlIgnore] Attribute

Excludes a property from XML serialization.

public class Example
{
    [XmlIgnore]
    public int MyProperty { get; set; }
}


[XmlAttribute] Attribute

Specifies that a property should be serialized as an XML attribute.

public class Person
{
    [XmlAttribute]
    public string FirstName { get; set; }
}


[XmlElement] Attribute

Specifies that a property should be serialized as an XML element.

public class Person
{
    [XmlElement]
    public string FirstName { get; set; }
}


[Serializable] Attribute

Marks a method as a web method that can be called from a web service.

[WebService]
public class MyWebService
{
    [WebMethod]
    public string HelloWorld()
    {
        return "Hello, world!";
    }
}


[Authorize] Attribute

Restricts access to a controller or action method to authorized users.

[Authorize]
public class SecureController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}


[Route] Attribute

Specifies the route template for an action method in ASP.NET Core.

[Route("api/[controller]")]
public class MyController : Controller
{
    [HttpGet("{id}")]
    public IActionResult Get(int id)
    {
        // Handle GET request
    }
}


[Required] Attribute

Indicates that a property is required for model validation.

public class Person
{
    [Required]
    public string FirstName { get; set; }
}


[MaxLength] Attribute

Specifies the maximum length for a string property.

public class Person
{
    [MaxLength(50)]
    public string FirstName { get; set; }
}


[MinLength] Attribute

Specifies the minimum length for a string property.

public class Person
{
    [MinLength(2)]
    public string FirstName { get; set; }
}


[RegularExpression] Attribute

Defines a regular expression pattern for string validation.

public class Person
{
    [RegularExpression(@"^[A-Za-z]+$")]
    public string FirstName { get; set; }
}


[Key] Attribute

Specifies a primary key for an entity in Entity Framework.

public class Person
{
    [Key]
    public int Id { get; set; }
}


[ForeignKey] Attribute

Indicates a foreign key relationship in Entity Framework.

public class Order
{
    public int CustomerId { get; set; }
    [ForeignKey("CustomerId")]
    public Customer Customer { get; set; }
}


[Table] Attribute

Specifies the table name for an entity in Entity Framework.

[Table("ProductTable")]
public class Product
{
    public int Id { get; set; }
}


[Column] Attribute

Maps a property to a specific database column in Entity Framework.

public class Product
{
    [Column("ProductName")]
    public string Name { get; set; }
}


[StringLength] Attribute

Specifies the maximum and minimum string length for validation.

public class Person
{
    [StringLength(50, MinimumLength = 2)]
    public string FirstName { get; set; }
}


[JsonProperty] Attribute

Maps a property to a specific JSON property name during serialization and deserialization.

public class Person
{
    [JsonProperty("first_name")]
    public string FirstName { get; set; }
}


[JsonIgnore] Attribute

Excludes a property from JSON serialization.

public class Person
{
    [JsonIgnore]
    public string SecretData { get; set; }
}


[AllowedValues] and [DeniedValues] Attributes

AllowedValues restricts a property to specific values, while DeniedValues blocks certain values

public class Product
{
    [AllowedValues("Chocolate", "Candy", "Fruit")]
    [DeniedValues("Vegetable", "Meat")]
    public required string Title { get; set; }
}


[Base64String] Attributes

Ensures that the property contains a valid Base64-encoded string

public class Document
{
    [Base64String]
    public required string EncodedContent { get; set; }
}


[Length] Attributes

Restricts the length of the string between 5 and 50 characters

public class UserProfile
{
    [Length(5, 50)]
    public required string Username { get; set; }
}


[Range] Attributes

Ensures that the date is within the specified range (for DateOnly and TimeOnly types)

public class Event
{
    [Range(typeof(DateOnly), "2024-01-01", "2025-12-31")]
    public required DateOnly EventDate { get; set; }
  
    [Range(typeof(TimeOnly), "08:00:00", "18:00:00")]
    public required TimeOnly EventTime { get; set; }
}


[RequiredIf] Attributes

Makes the property required only if a condition is met (e.g., TrackingNumber is required if IsExpressShipping is true)

public class Order
{
    [RequiredIf(nameof(IsExpressShipping), true)]
    public string? TrackingNumber { get; set; }
  
    public bool IsExpressShipping { get; set; }
}


[UnicodeCharacters] Attributes

Ensures that the string contains only Unicode characters

public class Message
{
    [UnicodeCharacters]
    public required string Content { get; set; }
}


[DisallowNull] Attributes

Prevents the property from being set to null

public class Settings
{
    [DisallowNull]
    public required string ConfigName { get; set; }
}


[DisallowDefault] Attributes

Prevents the property from being set to its default value (e.g., 0 for numeric types)

public class Transaction
{
    [DisallowDefault]
    public required decimal Amount { get; set; }
}


[DataType] Attributes

Specifies that the property should be treated as a specific data type (e.g., Currency and Duration)

public class Payment
{
    [DataType(DataType.Currency)]
    public required decimal TotalPrice { get; set; }
  
    [DataType(DataType.Duration)]
    public required TimeSpan ProcessingTime { get; set; }
}


[Display] Attributes

Enhances display attributes with localization support

public class Product
{
    [Display(Name = "Product Name", Description = "The name of the product", ResourceType = typeof(Resources))]
    public required string Title { get; set; }
}

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