C# Extension Methods that come in handy for an ASP.Net Project
Tags: csharp, aspdotnet, extensions
Published: 2024-02-04 08:50:13

In the realm of C# and ASP.NET development, extension methods serve as invaluable tools, empowering developers to enhance the expressiveness and efficiency of their code. In this blog post, we will delve into some of the basic extension methods that any ASP.Net Core project needs and can elevate your web development projects.

## HTTPContext Extensions

### Get Base Path
```csharp
public static string GetBasePath(this HttpContext httpContext)
{
    return $"{httpContext.Request.Scheme}://{httpContext.Request.Host}";
}
```

### Check if the Request Header has a particular key
```csharp
public static bool HasRequestHeader(this HttpContext httpContext, string headerName)
{
    return httpContext.Request.Headers.ContainsKey(headerName);
}
```

### Check if the Response Header has a particular key
```csharp
public static bool HasResponseHeader(this HttpContext httpContext, string headerName)
{
    return httpContext.Response.Headers.ContainsKey(headerName);
}
```

### Check if the Request Header key has the required value
```csharp
public static bool HasRequestHeaderValue(this HttpContext httpContext, string headerName, string headerValue)
{
    return httpContext.Request.Headers.TryGetValue(headerName, out var header)
        && header.Any(a => a.Equals(headerValue, StringComparison.OrdinalIgnoreCase));
}
```

### Check if the Response Header key has the required value
```csharp
public static bool HasResponseHeaderValue(this HttpContext httpContext, string headerName, string headerValue)
{
    return httpContext.Response.Headers.TryGetValue(headerName, out var header)
        && header.Any(a => a.Equals(headerValue, StringComparison.OrdinalIgnoreCase));
}
```

## IEnumerable Extensions

### Convert any IEnumerable to CSV String
```csharp
public static string ConvertToCSVString<T>(this IEnumerable<T> items)
{
    if (items.Any())
    {
        var lines = new List<string>();
        var header = string.Empty;

        if (items.FirstOrDefault().GetType() == typeof(JObject))
        {
            var jObject = (JObject)Convert.ChangeType(items.FirstOrDefault(), typeof(JObject));

            header = string.Join(",", jObject.Properties().Select(x => x.Name));

            lines.Add(header);

            ////DO NOT include the starting and ending quotes inside the $ string interpolation.
            var valueLines = items.Select(row =>
                                    string.Join(",", header.Split(',')
                                        .Select(a => "\"" + $"{((JObject)Convert.ChangeType(row, typeof(JObject))).GetValue(a)}"?
                                                        .Replace(Environment.NewLine, string.Empty, StringComparison.OrdinalIgnoreCase)
                                                        .Replace("\"", "\"\"", StringComparison.OrdinalIgnoreCase)
                                                        + "\""
                                                        )));

            lines.AddRange(valueLines);
        }
        else
        {
            var props = items.FirstOrDefault().GetType().GetProperties();

            header = string.Join(",", props.Select(x => x.Name));

            lines.Add(header);

            //DO NOT include the starting and ending quotes inside the $ string interpolation.
            var valueLines = items.Select(row =>
                                    string.Join(",", header.Split(',')
                                        .Select(a => "\"" + $"{row.GetType().GetProperty(a).GetValue(row, null)}"?
                                                        .Replace(Environment.NewLine, string.Empty, StringComparison.OrdinalIgnoreCase)
                                                        .Replace("\"", "\"\"", StringComparison.OrdinalIgnoreCase)
                                                        + "\""
                                                        )));

            lines.AddRange(valueLines);
        }

        var csvString = string.Join("\r\n", lines.ToArray());

        return csvString;
    }
    else
    {
        return string.Empty;
    }
}
```

### Convert any IEnumerable to DataTable
```csharp
public static DataTable ToDataTable<T>(this IEnumerable<T> items)
{
    DataTable dataTable = new DataTable(typeof(T).Name);

    PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
    foreach (PropertyInfo prop in Props)
    {
        dataTable.Columns.Add(prop.Name);
    }
    foreach (T item in items)
    {
        var values = new object[Props.Length];
        for (int i = 0; i < Props.Length; i++)
        {
            values[i] = Props[i].GetValue(item, null);
        }
        dataTable.Rows.Add(values);
    }

    return dataTable;
}
```

## Session Extensions

### Replace a value in a Session key
```csharp
public static void Replace(this ISession session, string key, object data)
{
    if (session.Keys.Any(x => x.Equals(key, StringComparison.OrdinalIgnoreCase)))
    {
        session.Remove(key);
    }

    session.SetString(key, JsonConvert.SerializeObject(data));
}
```

### Get value from a Session key
```csharp
public static bool TryGet<T>(this ISession session, string key, out T data, bool removeKey = false)
{
    if (session.Keys.Any(x => x.Equals(key, StringComparison.OrdinalIgnoreCase)))
    {
        var objString = session.GetString(key);

        data = JsonConvert.DeserializeObject<T>(objString);

        if (removeKey)
        {
            session.Remove(key);
        }

        return true;
    }

    data = default;
    return false;
}
```

## ClaimsPrincipal Extensions

### Check if the claims has a particular Role
```csharp
public static bool HasRole(this ClaimsPrincipal principal, string role)
{
    return principal.HasClaim(c =>
                                    c.Type == ClaimTypes.Role &&
                                    c.Value.Equals(role, StringComparison.OrdinalIgnoreCase));
}
```

### Get Roles from the claims
```csharp
public static IEnumerable<string> GetRoles(this ClaimsPrincipal principal)
{
    return principal.Claims.Where(c => c.Type == ClaimTypes.Role).Select(s => s.Value);
}
```

### Get a particular claim from the claims
```csharp
 public static T GetClaim<T>(this ClaimsPrincipal principal, string claim)
 {
     var result = principal?.Claims?.FirstOrDefault(f => f.Type.Equals(claim, StringComparison.OrdinalIgnoreCase))?.Value;

     return (T)Convert.ChangeType(result, typeof(T));
 }
```

## DateTime Extensions

### Convert to a different TimeZone
```csharp
public static DateTime ConvertToTimeZone(this DateTime dateTime, string timeZoneId)
{
    return TimeZoneInfo.ConvertTimeFromUtc(dateTime, TimeZoneInfo.FindSystemTimeZoneById(timeZoneId));
}
```

### Check if a given DateTime is between a time range
```csharp
public static bool IsBetweenTimeRange(this DateTime comparisonTime, DateTime? startDate, DateTime? endDate)
{
    return (startDate.HasValue ? comparisonTime >= startDate.Value : true) && (endDate.HasValue ? comparisonTime <= endDate.Value : true);
}
```

### Check if a give DateTimeOffset is between a time range
```csharp
public static bool IsBetweenTimeRange(this DateTime comparisonTime, DateTimeOffset? startDate, DateTimeOffset? endDate)
{
    return comparisonTime.IsBetweenTimeRange(startDate.HasValue ? startDate.Value.DateTime : null, endDate.HasValue ? endDate.Value.DateTime : null);
}
```

### Convert to a DateTimeOffset based on a TimeZone
```csharp
public static DateTimeOffset? ToDateTimeOffset(this DateTime? dateTime, string timeZoneId)
{
    return dateTime.HasValue ? new DateTimeOffset(dateTime.Value, TimeZoneInfo.FindSystemTimeZoneById(timeZoneId).BaseUtcOffset) : null;
}
```

## Enum Extensions

### Get display name of an Enum
```csharp
public static string GetDisplayName(this Enum enumValue)
{
    var displayName = enumValue.GetType().GetMember(enumValue.ToString()).FirstOrDefault().GetCustomAttribute<DisplayAttribute>()?.GetName();

    return string.IsNullOrWhiteSpace(displayName) ? enumValue.ToString() : displayName;
}
```

## HTTPResponseMessage Extensions

### Ensure the HTTPResponseMessage has a Success Status code and log failures

```csharp
public static async Task EnsureSuccessStatusCodeAndLogFailures(this HttpResponseMessage httpResponseMessage, ILogger logger)
{
    if (httpResponseMessage == null)
    {
        throw new ArgumentNullException(nameof(httpResponseMessage));
    }
    if (logger == null)
    {
        throw new ArgumentNullException(nameof(logger));
    }

    if (!httpResponseMessage.IsSuccessStatusCode)
    {
        var response = await httpResponseMessage.Content?.ReadAsStringAsync();

        var requestUri = httpResponseMessage.RequestMessage?.RequestUri;

        logger.LogError($"HTTP call to {requestUri} returned status code {httpResponseMessage.StatusCode} with response {response}");
    }

    httpResponseMessage.EnsureSuccessStatusCode();
}
```

## TokenResponse Extensions

### Ensure the TokenResponse has a Success Status and log failures
```csharp
public static void CheckResponseAndLogFailures(this TokenResponse tokenResponse, ILogger logger)
{
    if (tokenResponse == null)
    {
        throw new ArgumentNullException(nameof(tokenResponse));
    }
    if (logger == null)
    {
        throw new ArgumentNullException(nameof(logger));
    }

    if (tokenResponse.IsError)
    {
        var response = JsonConvert.SerializeObject(tokenResponse);

        logger.LogError("Access Token call to returned with error and data: {data}", response);
    }
}
```

## String Extensions

### Slugify
```csharp
public static string Slugify(this string phrase)
{
    if (string.IsNullOrWhiteSpace(phrase))
    {
        return string.Empty;
    }

    var output = phrase.RemoveAccents().ToLower();

    output = Regex.Replace(output, @"[^A-Za-z0-9\s-]", "");

    output = Regex.Replace(output, @"\s+", " ").Trim();

    output = Regex.Replace(output, @"\s", "-");

    return output;
}

public static string RemoveAccents(this string text)
{
    if (string.IsNullOrWhiteSpace(text))
        return text;

    text = text.Normalize(NormalizationForm.FormD);
    char[] chars = text
        .Where(c => CharUnicodeInfo.GetUnicodeCategory(c)
        != UnicodeCategory.NonSpacingMark).ToArray();

    return new string(chars).Normalize(NormalizationForm.FormC);
}
```

### Check if a email is valid
```csharp
public static bool IsValidEmail(this string email)
{
    var isValid = true;

    try
    {
        var emailAddress = new MailAddress(email.Trim());
    }
    catch
    {
        isValid = false;
    }

    return isValid;
}
```