Created
March 21, 2018 19:40
-
-
Save randyburden/90d79ff8082fedfc50d898d128e7f417 to your computer and use it in GitHub Desktop.
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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