Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
C# Connection String helper that can inject a custom application name pattern into a connection string. Custom application format is: <AssemblyName>, Version=X.X.X.X, BuildDate=MM/DD/YYYY 12:00 PM
using System;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Utilities
{
/// <summary>
/// Connection string helper.
/// </summary>
public static class ConnectionStringHelper
{
private static readonly Lazy<string> ApplicationName = new Lazy<string>(GetApplicationName);
/// <summary>
/// Adds the application name to the given connection string.
/// </summary>
/// <param name="connectionString">Connection string.</param>
/// <returns>Connection string with application name.</returns>
public static string AddApplicationName(string connectionString)
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectionString);
builder.ApplicationName = ApplicationName.Value;
return builder.ConnectionString;
}
/// <summary>
/// Gets a custom application name string with a max length of 100 characters.
/// </summary>
/// <example>
/// Core.Utilities, Version=2.1.0.0, BuildDate=3/21/2018 2:23:41 PM
/// </example>
/// <returns>Application name string.</returns>
private static string GetApplicationName()
{
var assembly = GetInitialExecutedAssembly();
if (assembly == null)
{
assembly = Assembly.GetExecutingAssembly();
}
var buildDate = GetBuildDate(assembly);
var assemblyName = assembly.GetName();
var applicationName = $"{assemblyName.Name}, Version={assemblyName.Version}, BuildDate={buildDate}";
return applicationName.Length <= 100 ? applicationName : applicationName.Substring(0, 100);
}
/// <summary>
/// Walk the stack in reverse and return the first non-system assembly found.
/// </summary>
/// <returns>Initial executed assembly.</returns>
private static Assembly GetInitialExecutedAssembly()
{
var stackFrames = new StackTrace().GetFrames();
if (stackFrames != null)
{
var reversedStackFrames = stackFrames.Reverse();
foreach (var stackFrame in reversedStackFrames)
{
var assembly = stackFrame.GetMethod()?.ReflectedType?.Assembly;
if (assembly != null && !assembly.FullName.StartsWith("Microsoft")
&& !assembly.FullName.StartsWith("System")
&& !assembly.FullName.StartsWith("mscorlib")
&& !assembly.FullName.StartsWith("App_global")
&& !assembly.FullName.StartsWith("nunit"))
{
return assembly;
}
}
}
return null;
}
/// <summary>
/// Gets the build date.
/// </summary>
/// <remarks>
/// Source: https://stackoverflow.com/a/44511677/670028
/// </remarks>
/// <param name="assembly">Assembly.</param>
/// <param name="timeZoneInfo">Time zone info.</param>
/// <returns>Assembly build date.</returns>
private static DateTime GetBuildDate(Assembly assembly, TimeZoneInfo timeZoneInfo = null)
{
try
{
// Constants related to the Windows PE file format.
const int peHeaderOffset = 60;
const int linkerTimestampOffset = 8;
// Discover the base memory address where our assembly is loaded
var entryModule = assembly.ManifestModule;
var hMod = Marshal.GetHINSTANCE(entryModule);
if (hMod == IntPtr.Zero - 1) throw new Exception("Failed to get HINSTANCE.");
// Read the linker timestamp
var offset = Marshal.ReadInt32(hMod, peHeaderOffset);
var secondsSince1970 = Marshal.ReadInt32(hMod, offset + linkerTimestampOffset);
// Convert the timestamp to a DateTime
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var linkTimeUtc = epoch.AddSeconds(secondsSince1970);
var dt = TimeZoneInfo.ConvertTimeFromUtc(linkTimeUtc, timeZoneInfo ?? TimeZoneInfo.Local);
return dt;
}
catch (Exception)
{
// Gulp
}
return DateTime.MinValue;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.