CSharp - Other

Empty IQueryable Class

Enumerable.Empty<ClassName>().AsQueryable();

Enable Cross-Origin Requests (CORS) in ASP.NET Core

Store Allowed Domains in appsettings.json: Instead of hardcoding the allowed domains in the code, we can define them in the configuration file:

{
  "AllowedOrigins": [
    "https://app1.example.com",
    "https://app2.example.com",
    "https://mobile.example.com"
  ]
}


Retrieved the array of allowed origins from the app settings and used them to register CORS services :

string allowedSpecificOrigins = "AllowSpecificOrigins";
WebApplicationBuilder? builder = WebApplication.CreateBuilder(args);
string[]? allowedOrigins = builder
                 .Configuration
                 .GetSection("AllowedOrigins")
                 .Get<string[]>();
builder.Services.AddCors(options =>
{
    options.AddPolicy(allowedSpecificOrigins,
        policy =>
           {
               policy.WithOrigins(allowedOrigins)
                     .AllowAnyMethod()
                     .AllowAnyHeader();
            });
});
builder.Services.AddControllers();


Enable CORS middleware

app.UseCors(allowedSpecificOrigins);

Enable debugging access for my website

Set debug to true in Web.config file.


<compilation debug="true" strict="false" explicit="true">

</compilation>

Enable Global Split Queries in .Net Core

This configuration ensures that all LINQ queries using Include() in Entity Framework Core are executed using split queries by default. This helps prevent performance issues caused by Cartesian explosion when loading multiple related collections. It is enabled by setting UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery) in the DbContext configuration.


builder.Services.AddDbContext<YourDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))
           .UseQuerySplittingBehavior(QuerySplittingBehavior.SplitQuery)
);

Enable Sensitive Data Logging in .Net Core

if (env.IsDevelopment())
{
    options.EnableSensitiveDataLogging();
    options.EnableDetailedErrors();
}

Enable Server GC in .NET Core

To enable Server GC, modify the runtimeconfig.json file or add the following to your app settings:

{
  "runtimeOptions": {
    "configProperties": {
      "System.GC.Server": true
    }
  }
}


Alternatively, you can configure GC in ASP.NET Core apps by editing Program.cs:

public static void Main(string[] args)
{
    var host = Host.CreateDefaultBuilder(args)
        .UseServerGC()  // Enables Server GC mode
        .Build();

    host.Run();
}

Enhancing File Upload Security in .NET Core with File Signature Validation

Why Use File Signature Validation? —


  • Prevents Spoofing Attacks — Attackers may rename a file (e.g., .exe to .jpg) to bypass extension-based filtering.
  • Stops Malware Uploads — Ensures that only legitimate file types are processed and prevents execution of malicious scripts.
  • Improves Data Integrity — Helps detect tampered files before they are stored or executed.
  • Enhances Compliance — Many regulatory standards (e.g., PCI DSS, HIPAA) require secure file handling.


How File Signature Validation Works —


Extract File Signature —

  • Every file format has a unique binary signature (also called "magic number") at the beginning of the file.
  • This signature is compared against a list of allowed file types.


Validate Against Expected Format: —

  • Check if the extracted signature matches the expected format.
  • If the signature doesn't match the extension, reject the upload.


Perform Additional Security Checks

  • Virus Scanning: Use an antivirus engine to scan files.
  • Size Restrictions: Set limits to prevent DoS attacks via large file uploads.
  • Content-Type Verification: Ensure that the MIME type matches the file's actual content.


Steps to Implement File Signature Validation in .NET Core


Define Allowed File Signatures: -

Each file type has a unique signature (byte sequence) at the beginning of the file. Below are some common file signatures:

None


Define a dictionary to map file types to their signatures:

public static class FileSignatures
{
    public static readonly Dictionary<string, List<byte[]>> Signatures = new()
    {
        { ".jpg", new List<byte[]> { new byte[] { 0xFF, 0xD8, 0xFF } } },
        { ".png", new List<byte[]> { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A } } },
        { ".pdf", new List<byte[]> { new byte[] { 0x25, 0x50, 0x44, 0x46 } } },
        { ".docx", new List<byte[]> { new byte[] { 0x50, 0x4B, 0x03, 0x04 } } } // DOCX is a ZIP file format
    };
}


Validate File Signature During Upload: —

Use the file stream to read the first few bytes and compare them against the allowed signatures.

public static bool IsValidFileSignature(Stream fileStream, string fileExtension)
{
    if (!FileSignatures.Signatures.ContainsKey(fileExtension))
        return false; // File type is not allowed

    var allowedSignatures = FileSignatures.Signatures[fileExtension];

    using (var reader = new BinaryReader(fileStream))
    {
        foreach (var signature in allowedSignatures)
        {
            fileStream.Position = 0; // Reset stream position before reading
            var headerBytes = reader.ReadBytes(signature.Length);

            if (headerBytes.SequenceEqual(signature))
                return true; // Valid file signature
        }
    }

    return false; // Invalid file signature
}


Implement File Validation in Controller : —

Modify the file upload controller to validate the file signature before saving.

[HttpPost("upload")]
public async Task<IActionResult> UploadFile(IFormFile file)
{
    if (file == null || file.Length == 0)
        return BadRequest("File is empty.");

    var extension = Path.GetExtension(file.FileName).ToLowerInvariant();
    
    using var fileStream = file.OpenReadStream();
    if (!IsValidFileSignature(fileStream, extension))
        return BadRequest("Invalid file format.");

    var filePath = Path.Combine("Uploads", file.FileName);
    using (var stream = new FileStream(filePath, FileMode.Create))
    {
        await file.CopyToAsync(stream);
    }

    return Ok("File uploaded successfully.");
}

Enum to List

enumClass[] enumList = (enumClass[])Enum.GetValues(typeof(enumClass));

Evaluate Expressions in Switch Statements

switch (mark)
{
    case int n when n >= 80:
        Console.WriteLine("Grade is A");
        break;
    case int n when n >= 60:
        Console.WriteLine("Grade is B");
        break;
    case int n when n >= 40:
        Console.WriteLine("Grade is C");
        break;
    default:
        Console.WriteLine("Grade is D");
        break;
}

Exception Logging In MVC Using Elmah

Install Elmah Package from Nuget.


Now open web config file and add the following code inside <system.web> </system.web>.

<!--add this-->  
    < httpHandlers >  
    < add verb = "POST,GET,HEAD"path = "elmah.axd"type = "Elmah.ErrorLogPageFactory, Elmah" / >  
    < /httpHandlers>  
<!--add this-->  


Also, add the following code inside <system.webServer></system.webServer>.

<!--add this-->  
    < handlers >  
    < add name = "Elmah"verb = "POST,GET,HEAD"path = "elmah.axd"type = "Elmah.ErrorLogPageFactory, Elmah" / >  
    < /handlers>  
<!--add this-->  


Create ELMAH_Error table.

CREATE TABLE[dbo].[ELMAH_Error]  
(  
  
    [ErrorId][uniqueidentifier] NOT NULL,  
  
    [Application][nvarchar](60) NOT NULL,  
  
    [Host][nvarchar](50) NOT NULL,  
  
    [Type][nvarchar](100) NOT NULL,  
  
    [Source][nvarchar](60) NOT NULL,  
  
    [Message][nvarchar](500) NOT NULL,  
  
    [User][nvarchar](50) NOT NULL,  
  
    [StatusCode][int] NOT NULL,  
  
    [TimeUtc][datetime] NOT NULL,  
  
    [Sequence][int] IDENTITY(1, 1) NOT NULL,  
  
    [AllXml][ntext] NOT NULL  
)  


Create stored procedures.

ELMAH_GetErrorsXml

Create PROCEDURE[dbo].[ELMAH_GetErrorsXml]  
  
(  
    @Application NVARCHAR(60),  
    @PageIndex INT = 0,  
    @PageSize INT = 15,  
    @TotalCount INT OUTPUT  
  
)  
  
AS  
  
SET NOCOUNT ON  
  
DECLARE @FirstTimeUTC DATETIME  
DECLARE @FirstSequence INT  
DECLARE @StartRow INT  
DECLARE @StartRowIndex INT  
SELECT  
  
@TotalCount = COUNT(1)  
  
FROM  
  
    [ELMAH_Error]  
  
WHERE  
  
    [Application] = @Application  
SET @StartRowIndex = @PageIndex * @PageSize + 1  
IF @StartRowIndex <= @TotalCount  
  
BEGIN  
  
SET ROWCOUNT @StartRowIndex  
  
SELECT  
  
@FirstTimeUTC = [TimeUtc],  
  
    @FirstSequence = [Sequence]  
  
FROM  
  
    [ELMAH_Error]  
  
WHERE  
  
    [Application] = @Application  
  
ORDER BY  
  
    [TimeUtc] DESC,  
    [Sequence] DESC  
  
END  
  
ELSE  
  
BEGIN  
  
SET @PageSize = 0  
  
END  
  
SET ROWCOUNT @PageSize  
  
SELECT  
  
errorId = [ErrorId],  
  
    application = [Application],  
    host = [Host],  
    type = [Type],  
    source = [Source],  
    message = [Message],  
    [user] = [User],  
    statusCode = [StatusCode],  
    time = CONVERT(VARCHAR(50), [TimeUtc], 126) + 'Z'  
  
FROM  
  
    [ELMAH_Error] error  
  
WHERE  
  
    [Application] = @Application  
  
AND  
  
    [TimeUtc] <= @FirstTimeUTC  
  
AND  
  
    [Sequence] <= @FirstSequence  
  
ORDER BY  
  
    [TimeUtc] DESC,  
  
    [Sequence] DESC  
  
FOR  
  
XML AUTO  


ELMAH_GetErrorXml

Create PROCEDURE[dbo].[ELMAH_GetErrorXml]  
  
(  
  
    @Application NVARCHAR(60),  
    @ErrorId UNIQUEIDENTIFIER  
  
)  
  
AS  
  
SET NOCOUNT ON  
SELECT  
  
    [AllXml]  
FROM  
  
    [ELMAH_Error]  
WHERE  
  
    [ErrorId] = @ErrorId  
AND  
    [Application] = @Application  


ELMAH_LogError

Create PROCEDURE[dbo].[ELMAH_LogError]  
  
(  
  
    @ErrorId UNIQUEIDENTIFIER,    
    @Application NVARCHAR(60),    
    @Host NVARCHAR(30),    
    @Type NVARCHAR(100),  
    @Source NVARCHAR(60),    
    @Message NVARCHAR(500),  
    @User NVARCHAR(50),   
    @AllXml NTEXT,    
    @StatusCode INT,   
    @TimeUtc DATETIME  
  
)  
  
AS  
  
SET NOCOUNT ON  
  
INSERT  
  
INTO  
  
    [ELMAH_Error]
(  
  
    [ErrorId],   
    [Application],   
    [Host],  
    [Type],  
    [Source],  
    [Message],    
    [User],    
    [AllXml],    
    [StatusCode],    
    [TimeUtc]  
  
)  
  
VALUES  
  
    (  
  
    @ErrorId,  
    @Application,    
    @Host,    
    @Type,    
    @Source,   
    @Message,    
    @User,   
    @AllXml,   
    @StatusCode,   
    @TimeUtc  
  
)  


Now again open web config file, add the following code inside in <configuration>.

</configuration>  
<elmah>  
    <!--. If allowRemoteAccess value is set to 0, then the error log web page can only be viewed locally. If allowRemoteAccess attribute is set to 1 then the error log web page is enabled for both remote and local visitors.-->  
    <!--add this-->  
    <security allowRemoteAccess="0" />  
    <!--  DefaultConnection is the name of database connection string -->  
    <errorLog type="Elmah.SqlErrorLog, Elmah" connectionStringName="DefaultConnection" />  
    <!--add this-->  
</elmah>   


Note: You can view the error log web page through the url localhost:portno/elmah.axd like, localhost:55776/elmah.axd.

Expression in LINQ

Take the reference of System.Linq.Expressions namespace and use an Expression<TDelegate> class to define an Expression. Expression<TDelegate> requires delegate type Func or Action.


For example, you can assign lambda expression to the isTeenAger variable of Func type delegate, as shown below:


Define Func delegate for an expression in C#

public class Student 
{
  public int StudentID { get; set; }
  public string StudentName { get; set; }
  public int Age { get; set; }
}

Func<Student, bool> isTeenAger = s => s.Age > 12 && s.Age < 20;


And now, you can convert the above Func type delegate into an Expression by wrapping Func delegate with Expression, as below:


Define Expression in C#

Expression<Func<Student, bool>> isTeenAgerExpr = s => s.Age > 12 && s.Age < 20;


In the same way, you can also wrap an Action<t> type delegate with Expression if you don't return a value from the delegate.


Define Expression in C#

Expression<Action<Student>> printStudentName = s => Console.WriteLine(s.StudentName);


You can invoke the delegate wrapped by an Expression the same way as a delegate, but first you need to compile it using the Compile() method. Compile() returns delegateof Func or Action type so that you can invoke it like a delegate.


Invoke Expression in C#

Expression<Func<Student, bool>> isTeenAgerExpr = s => s.Age > 12 && s.Age < 20;

//compile Expression using Compile method to invoke it as Delegate
Func<Student, bool> isTeenAger = isTeenAgerExpr.Compile();
       
//Invoke
bool result = isTeenAger(new Student(){ StudentID = 1, StudentName = "Steve", Age = 20});

Output: false

Filter Even Numbers from an Array

Filtering out even numbers from an array is concise and efficient with LINQ.

int[] nums = { 1, 2, 3, 4, 5, 6 };
var evens = nums.Where(n => n % 2 == 0);
Console.WriteLine(string.Join(", ", evens));


Explanation: The Where method filters the array based on the condition n % 2 == 0, leaving only the even numbers, which are then printed.