When calling APIs from your website, you may wish to send headers and make other HTTP request modifications. In my case, I’d always want to ensure I send a specific correlation ID with each downstream request to any microservice.
When I create an HttpClient for the HttpFactory in the Dependency Injection Service Container, I don’t yet have access to the HttpContext of the incoming request. This is a bigger problem in APIs, where another application may be passing an existing correlation ID that I may want to grab and forward to any downstream requests.
On a website, I usually use a user identifier such as the user’s IdP unique ID. If I need or want to keep it more private, I just use the session ID.
Rather than add the correlation ID to each request in the repository classes, I want to have all transactions automatically check for an incoming correlation ID and, if so, use it as the correlation ID for downstream requests to any other APIs. If one doesn’t exist, then I’ll either generate one or use a different value.
To do this, I’ll create a special helper class to retrieve the value and override the SendAsync method to add it to any downstream request during the life of the current request.
/// <summary>
/// Handler for HTTP requests to propagate specific headers from the incoming request to outgoing requests
/// </summary>
public class RequestHeaderPropagationHandler(IHttpContextAccessor CurrentContext) : DelegatingHandler
{
/// <summary>
/// Handle the SendAsync Event
/// </summary>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Microsoft.AspNetCore.Http.HttpContext? httpContext = CurrentContext.HttpContext;
if (httpContext != null)
{
string _corId = httpContext.GetCorrelationID();
request.Headers.TryAddWithoutValidation("Accept", "application/json");
request.Headers.TryAddWithoutValidation("X-CORRELATION-ID", _corId);
}
return base.SendAsync(request, cancellationToken);
}
}
You’ll notice I have an Extension method called GetCorrelationID() that I use to see if the incoming request has one I want to use.
/// <summary>
/// Get the CorrelationID for this HttpContext or replies with
/// </summary>
/// <param name="value"></param>
/// <param name="newCorrID"></param>
/// <returns></returns>
public static string GetCorrelationID(this HttpContext value, string newCorrID = "")
{
string _corId = string.Empty;
StringValues header = value.Request.Headers["X-CORRELATION-ID"];
if (header.Count > 0)
{
_corId = header.First() + string.Empty;
}
// Use the new correlation ID if provided
if (!string.IsNullOrWhiteSpace(newCorrID))
{
_corId = newCorrID;
}
// No X-CORRELATION-ID sent in headers
if (string.IsNullOrWhiteSpace(_corId))
{
_corId = Guid.NewGuid().ToString();
}
return _corId;
}
In the above extension method, if there is no existing correlation ID in the incoming request, it will replace it with the one I’ve passed in; if not, it will generate a new GUID.
Startup.cs
In the SendAsync override, IHttpContextAccessor is not normally available in the dependency injection service collection. In the Startup.cs, make sure to enable that functionality:
// REQUIRED: Give downstream services access to the current HTTP context
services.AddHttpContextAccessor();
This will allow access the the HttpContext in any method within the application.
Next in the Startup.cs, you’ll need to register the RequestHeaderPropagationHandler. I make sure that this is registered as a transient, so it doesn’t store values from requests from other callers/users.
// REQUIRED: This is used in to propogate headers to downstream requests
services.AddTransient<RequestHeaderPropagationHandler>();
The last step is to add an http message handler to the Http Client in the Statup.cs.
services.AddHttpClient("lookups", c =>
{
c.BaseAddress = new Uri(Configuration["AppSettings:LookupsBaseURL"]!);
})
.AddHttpMessageHandler<RequestHeaderPropagationHandler>();
Summary
So now you have the code that attempts to add an X-CORRELATION-ID header and value to any outgoing HTTP request made by this application.
String Extension Methods in C# .NET can make life so much easier.
Extension Method
String Extension Methods in C# .NET can make life so much easier. Everyday functions that used to require extended syntax with a return value are a thing of the past. Now with Microsoft Visual Studio, we can just add a using to our Extensions collection and have full use of them from our Share Library.What is a String Extension Method?
An extension method is simply an additional method. It is a way of attaching additional functionality to a type which is available to you throughout your code without need to instantiate another class.
There’s plenty of best practices for extension methods. A great article is here on Microsoft’s site.
ToBoolean() Extension Method
Probably my most used and handiest is a simple one. There are so many times when we receive text, whether in a JSON payload or in a view that we need to see if it is a legitimate boolean value.
Here is a simple implementation of ToBoolean()
/// <summary>
/// Convert a string to a boolean
/// Yasgar Technology Group, Inc.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool ToBoolean(this string value)
{
if (value == null) { return false; }
if (value.Trim().ToLower() == "true" || value == "1" || value == "yes")
{ return true; }
else
{ return false; }
}
This is a simple implementation that does a quick compare against a set of strings.
IsNumericInteger() Extension Method
Often, during a view post back, I need to determine if a particular value is numeric that was accepted in a text box. While I usually try to validate this type of input using javascript, there are many ways that people can bypass that validation. I use this specific one to validate that this value is indeed an integer and not a decimal.
/// <summary>
/// Return bool whether the value in the string is a numeric integer
/// Yasgar Technology Group, Inc. - www.ytgi.com
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsNumericInteger(this string value)
{
return long.TryParse(value, out long _tempvalue);
}
Here is a simple sample implementation that does a quick TryParse() to see if it is a pass or fail.
IsNumericDecimal() Extension Method
Often there are numeric fields that you’re receiving via a post back or JSON or XML payload. This is a quick way to determine if it’s a decimal fit or not. Remember, that integers will pass this test as well. So use the IsNumericInteger() extension method if you want to determine if the numeric value has a decimal in it.
/// <summary>
/// Return bool whether the value in the string is a numeric decimal
/// Yasgar Technology Group, Inc. - www.ytgi.com
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool IsNumericDecimal(this string value)
{
return decimal.TryParse(value, out decimal _tempvalue);
}
ToDateFromCCYYMMDD() Extension Method
There are often cases where dates are passed around in CCYYMMDD format, such as 20220329. This is a preferred method for me when I need to transfer a date as a query string parameter argument and don’t want the mess of a full DateTime. This extension method converts that string to a DateTime object.
/// <summary>
/// Convert a string in CCYYMMDD format to a valid date
/// Yasgar Technology Group, Inc. - www.ytgi.com
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static DateTime ToDateFromCCYYMMDD(this string value)
{
if (!string.IsNullOrWhiteSpace(value))
{
if (value == "99999999")
{
return DateTime.MaxValue;
}
else
{
string _value = value.Trim();
if (_value.IsNumericInteger() && _value.Trim().Length == 8)
{
int.TryParse(_value.Substring(0, 4), out int year);
int.TryParse(_value.Substring(4, 2), out int month);
int.TryParse(_value.Substring(6, 2), out int day);
DateTime datItem = new DateTime(year, month, day);
return datItem;
}
}
}
return DateTime.MinValue;
}
Notice that I check for the “99999999” string. This is a very popular marker for “no expiration” date, especially in mainframe data.
ToDateFromString() Extension Method
This is a variation on the ToDateFromCCYYMMDD() extension method. You might ask why I would have an extension method that probably does the same thing as DateTime.TryParse()? Well, simple, I’ve worked with lots of data where they have dates like “99999999” and “99/99/9999” which I want to handle properly.
/// <summary>
/// Convert a string in MM/DD/CCYY format to a valid date
/// Yasgar Technology Group, Inc. - www.ytgi.com
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static DateTime ToDateFromString(this string value)
{
if (!string.IsNullOrWhiteSpace(value))
{
if ((value == "99999999") || (value == "99/99/9999"))
{
return DateTime.MaxValue;
}
else
{
if (!string.IsNullOrWhiteSpace(value))
{
DateTime.TryParse(value, out DateTime _value);
return _value;
}
else
{
return DateTime.MinValue;
}
}
}
return DateTime.MinValue;
}
Notice it does use the standard DateTime.TryParse(), but only after it checks for funky dates. You may also want to put in checks for dates that are popular in your environment, such as the old SQL Server minimum date of “1/1/1753”
Trim(int MaxLength) (Accepting a maximum length)
This extension method accepts an integer specifying the maximum length of the returned string. I use this method all the time, especially when stuffing data into old data tables where data needs to be truncated. To be honest, I find it hard to believe that after all this time, it’s still not an overload in the framework.
/// <summary>
/// Trim a string down to a particular size
/// Yasgar Technology Group, Inc. - www.ytgi.com
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static string Trim(this string value, int p_MaxLength)
{
try
{
if (value != null)
{
return value.Substring(0, Math.Min(p_MaxLength, value.Length));
}
else
{
return string.Empty;
}
}
catch (Exception)
{
throw;
}
}
Remember, you should never just randomly trim data without assessing whether it is going to cause data corruption. Important data should not be truncated with a method like this unless you’re logging the activity somewhere.
This is one of my favorite extension methods, not only because I use it so often when validating data, but because it’s proven to be so versatile that I haven’t had to modify very much over the years. This method accepts two parameters to allow you to keep dashes and hash signs in the return if you want. They both default to false if you don’t set them.
/// <summary>
/// Remove special characters from a string with option to
/// retain Dashes and Hash signs
/// Yasgar Technology Group, Inc. - www.ytgi.com
/// </summary>
/// <param name="value"></param>
/// <param name="dashOkay"></param>
/// <param name="hashOkay"></param>
/// <returns></returns>
public static string RemoveSpecialChars(this string value,
bool dashOkay = false,
bool hashOkay = false)
{
try
{
StringBuilder sb = new StringBuilder();
if (value != null)
{
if (dashOkay && hashOkay)
{
foreach (char c in value)
{
if ((c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
c == '-' ||
c == '#' ||
c == ' ')
{
sb.Append(c);
}
}
}
else if (dashOkay && hashOkay == false)
{
foreach (char c in value)
{
if ((c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
c == '-' || c == ' ')
{
sb.Append(c);
}
}
}
else if (dashOkay == false && hashOkay)
{
foreach (char c in value)
{
if ((c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
c == '#' || c == ' ')
{
sb.Append(c);
}
}
}
else if (!dashOkay && !hashOkay)
{
foreach (char c in value)
{
if ((c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
c == ' ')
{
sb.Append(c);
}
}
}
}
return sb.ToString();
}
catch (Exception)
{
throw;
}
}
RemoveSpaces(bool StripInternal = false)
The standard Trim() extension method in the .NET framework will remove spaces from the beginning and end, but does it remove spaces inside the string? No, of course not. But there are times when that is needed and I have just the method read for it. It also trims the front and back as well, so no need to do an extra Trim() on it.
/// <summary>
/// Strip spaces from a string
/// Yasgar Technology Group, Inc. - www.ytgi.com
/// </summary>
/// <param name="value"></param>
/// <param name="StripInternal">strip spaces from within the string</param>
/// <returns></returns>
public static string RemoveSpaces(this string value, bool StripInternal = false)
{
if (!string.IsNullOrWhiteSpace(value))
if (StripInternal)
{
return new string(value.ToCharArray()
.Where(c => !Char.IsWhiteSpace(c))
.ToArray());
}
else
{
return value.Trim();
}
else
{
return string.Empty;
}
}
ToDecimal() Extension Method
If you need to retrieve a decimal value from a string, you can use this extension method. It will actually return a nullable decimal (decimal?). It will be null if the value could not be coerced into a decimal. This one could be used in place of the IsNumericDecimal() method if you need to retrieve the value and not simply pass it on if it validates. There is the extra step to check whether the return value is null though.
/// <summary>
/// Convert a string to a Decimal, return null if fails
/// Yasgar Technology Group, Inc. - www.ytgi.com
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static decimal? ToDecimal(this string value)
{
if (decimal.TryParse(value, out decimal decItem))
{ return decItem; }
else
{ return null; }
}
These are two powerful extension methods that I group together. They come in so handy for encrypting and decrypting values on the fly. While it’s probably not the greatest plan to use this for an encryption strategy, I often use them while data is in flight. For instance, I parse a text file and save it to a staging database table for later processing. If there is Protected Health Information (PHI), or even Personally Identifiable Information (PII), then I’ll use this method to protect it from prying eyes before it winds up in its final resting place.
Both of these extension methods make use of the CRijndael namespaces in the framework.
ToProperCase()
How often do we need to convert a standard text string to proper or title case. All the time! So this is a solution for your needs!
using System.Globalization;
using System.Threading;
/// <summary>
/// Convert a string to Proper case
/// based on the current thread culture
/// Yasgar Technology Group, Inc. - www.ytgi.com
/// </summary>
/// <param name="text"></param>
/// <returns></returns>
public static string ToProperCase(this string text)
{
if (text != null)
{
CultureInfo _cultureInfo = Thread.CurrentThread.CurrentCulture;
TextInfo textInfo = _cultureInfo.TextInfo;
// Send in the text as lower case to allow the method to
// make all the decisions
return textInfo.ToTitleCase(text.ToLower());
}
else
{
return text;
}
}
If you love Extension Methods, take a look at some other posts I have about others:
When writing more complicated Linq queries against an ORM, it is often desirable to be able to randomly pass in a field name as a string and sort order (direction) on the fly. This comes in especially handy for the paging project that you’ll also find on this Blog. I developed this Linq OrderBy Extension using a string and boolean value to determine if should sort ascending or descending.
Basically, with out of the box Linq, you can’t easily change the sort column dynamically. There is a Dynamic Linq Library (System.Linq.Dynamic) that you can install using a NuGet package, but that seemed overkill to me for this need. After looking that over, if you feel it’s a better solution for you, you won’t need to use this extension.
This extension method allows you to pass in the collection as IQueryable, so you can pass in Linq queries that haven’t been executed yet. Then it accepts your OrderByProperty and whether you want the query set to sort the results Descending as a boolean value.
You implement it like this:
using (EFModels.AWEntities _efEntities = new EFModels.AWEntities())
{
var _entityrows = (from sheader in _efEntities.SalesOrderHeaders
select sheader);
if (!string.IsNullOrWhiteSpace(SortExpression))
{
bool IsSortDESC = false;
if (SortOrder.ToLower() == "asc") { IsSortDESC = true; }
_entityrows = _entityrows.OrderBy(SortExpression, IsSortDESC);
}
return _entityrows.ToList();
}
Put the following Extension in your extensions class and make sure to put a using to that location at the top of the class where you’re going to use it.
/// <summary>
/// Implementation of OrderBy Extension Method for IQueryable Collections
/// </summary>
/// <typeparam name="TEntity">Generic Type Object</typeparam>
/// <param name="p_Source">The collection to order</param>
/// <param name="p_OrderByProperty">The property to order by</param>
/// <param name="p_Descending">True to sort Descending</param>
/// <returns></returns>
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> p_Source, string p_OrderByProperty, bool p_Descending)
{
try
{
string command = p_Descending ? "OrderByDescending" : "OrderBy";
var type = typeof(TEntity);
var property = type.GetProperty(p_OrderByProperty);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExpression = Expression.Lambda(propertyAccess, parameter);
var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
p_Source.Expression, Expression.Quote(orderByExpression));
return p_Source.Provider.CreateQuery<TEntity>(resultExpression);
}
catch (ArgumentNullException)
{
throw new Exception("The OrderByProperty value of: '" + p_OrderByProperty + "', was empty or is not a proper column name!");
}
catch (Exception)
{
throw;
}
}
If you like this, take a look at some other extension methods that I use all the time:
Back when I coded in Visual Studio 2003, we used to joke that if you wrote a small app that had 100 lines of code, 98 of them would be checking for nulls. It actually wasn’t that far off.
Because of that, I often force my DTO’s to default to DateTime.MinValue instead of nulls and have my code look for MinValue rather than checking for nulls all the time. This has one side effect that I didn’t like, when you use your model in an MVC view, it will display in the text box like “01/01/0001 00:00:00” etc. This is obviously not desirable.
The secondary issue is that when you use Date/Time pickers in the view, they don’t like being bound to DateTime properties, they work better on a string based text box. So what I’ve done is resolve both issues with one solution.
First part is in the ViewModel. I often just inherit my DTO and add or override properties with Attributes/Decorations that I need for the View. I’ll use my ProjectViewModel as an example:
public class ProjectViewModel : Project
{
/// <summary>
/// Gets or sets the Project Start Date Display
/// Use to get rid of 01/01/0001 displays
/// </summary>
[Display(Name = "Start Date: ")]
public string StartDateDisplay
{
get
{
return StartDate.ToShortDateDisplay();
}
set
{
StartDate = value.ToDateFromString();
}
}
}
In this case, my Project DTO has a StartDate property that is a DateTime. It could be null, or it could be DateTime.MinValue, or it could be a real date. In this implementation, I don’t care about the time, but you could build this out to include time very easily.
When I use this in a view, I use the StartDateDisplay property, rather than the StartDate property.
One piece of code that is very popular, besides checking for null, is being able to trim a string to a particular length, making sure a string is not too long to save in the database or display on your page or MVC View. In the olden days, we had lots of code like this:
In one form or another. Now, with extension methods, we can add this into our bag of tricks to have available everywhere:
public static class StringExtensions
{
public static string Trim(this string value, int MaxLength)
{
return value.Substring(0, Math.Min(MaxLength, value.Length));
}
}
Notice that I’m using the Math.Min function to tell the Substring whether to return the string as it is, or use the MaxLength argument. Now the code above will look like this:
dbTable.Property = myString.Trim(10);
And it will save the value of myString as it’s current value, unless it’s longer than 10, which will cause it to be trimmed.
One of the big advantages of using String Extension Methods like this is that they can be used in Linq queries very easily. If you want to see more about extension methods, check out my other posts below:
I was working on a project where I needed to compare values from two different systems. I needed a way to convert the data based on the type, which was known, from a string value that was pulled from XML or typed in by the user as an override value. Using LINQ to compare the values was problematic, as a decimal value could be input as “0” or “0.0” or “0.00” etc. Dates are an obvious issue. So I used an extension method to convert types.
Let’s take a look at the following code:
private void ConvertDecimals1()
{
try
{
string[] valsFromXML = new string[] {"0", "1", "2" };
string testVal = "0.00";
var myVals = from objVal in valsFromXML
where objVal == testVal
select objVal;
Console.WriteLine(myVals.Count().ToString());
}
catch (Exception)
{ throw; }
}
Running this code produces a count of zero, which you would expect for a comparison of this nature. But wait, System.String implements IComparable, so maybe we could use it to quickly resolve our issue:
var myVals = from objVal in valsFromXML
where (objVal.CompareTo(testVal) == 0)
select objVal;
But alas, no easy solution here, this also does not produce a match, unless the strings were formatted identically. There is no getting around converting the values. Fortunately, we have some conversion functions that we could use to accomplish this task:
var myVals = from objVal in valsFromXML
where Convert.ToDecimal(objVal) == Convert.ToDecimal(testVal)
select objVal;
Ah, perfect this produces the count of one that we were looking for. This code will work great, so long as it never has a code review or goes into production. Since the source values are strings, you don’t have any control over what is being passed in. In this case, the best bet is to use the TryParse method on the type to convert the values:
decimal decValue;
decimal.TryParse(testVal, out decValue);
Which would work great, except we still have the collection of values being passed in that would have to be handled in a foreach loop to accomplish this goal.
Extension methods to the rescue again. Let’s create a new static class and encapsulate the logic we need to convert the string to the decimal values:
public static class ExtMethods
{
public static decimal? ToDecimal(this string value)
{
Decimal decItem;
if (Decimal.TryParse(value, out decItem))
{ return decItem; }
else
{ return null; }
}
}
This effectively adds the ability to attempt to convert the string value to a decimal and return the value. Notice that I didn’t just return the decItem variable contents. This is because if the TryParse fails, it will fill the value with a decimal value of zero, which will pass a comparison to zero. This is not acceptable, so we still need to check that the conversion occurred successfully before we return the value. Returning null will not cause LINQ to raise an exception.
So now we can modify our LINQ query as follows:
var myVals = from objVal in valsFromXML
where objVal.ToDecimal() == testVal.ToDecimal()
select objVal;
This will give us a safe, encapsulated, reusable way to compare values that need to be converted from strings.
We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept All”, you consent to the use of ALL the cookies. However, you may visit "Cookie Settings" to provide a controlled consent.
This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.
Cookie
Duration
Description
cookielawinfo-checkbox-analytics
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics".
cookielawinfo-checkbox-functional
11 months
The cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".
cookielawinfo-checkbox-necessary
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary".
cookielawinfo-checkbox-others
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other.
cookielawinfo-checkbox-performance
11 months
This cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance".
viewed_cookie_policy
11 months
The cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.
Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.
Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.
Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.