-
-
Notifications
You must be signed in to change notification settings - Fork 8
Description
SQL Server is made up of a vast number of components, many managed by different teams.
Some components are being updated while others are not which leads us to DLL heck.
The way that .NET handles this is through app.config files but I don't know how to beautifully load an app.config, which looks like this
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0"/>
</dependentAssembly>
That basically says "if any components call for old versions of this dll, just give them the new version instead".
You can read more here, which I found right after running into my first error after mixing and matching SMO, SqlClient and DacFx: https://stackoverflow.com/questions/62764744/could-not-load-file-or-assembly-system-runtime-compilerservices-unsafe
Right now, I solve it with this code, which is working. It had to be written in C# otherwise, I'd get a StackOverflow error in Appveyor.
if ($PSVersionTable.PSEdition -ne "Core") {
$dir = (Join-Path $script:libraryroot "lib\net462\").Replace('\','\\')
if (-not ("Redirector" -as [type])) {
$source = @"
using System;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
public class Redirector
{
public Redirector()
{
this.EventHandler = new ResolveEventHandler(AssemblyResolve);
}
public readonly ResolveEventHandler EventHandler;
protected Assembly AssemblyResolve(object sender, ResolveEventArgs e)
{
string[] dlls = {
"System.Runtime.CompilerServices.Unsafe",
"System.Resources.Extensions",
"Microsoft.SqlServer.ConnectionInfo",
"Microsoft.SqlServer.Smo",
"Microsoft.Identity.Client",
"System.Diagnostics.DiagnosticSource",
"Microsoft.IdentityModel.Abstractions",
"Microsoft.Data.SqlClient",
"System.Configuration.ConfigurationManager",
"Microsoft.SqlServer.Replication",
"Microsoft.SqlServer.Rmo"
};
var name = new AssemblyName(e.Name);
var assemblyName = name.Name.ToString();
foreach (string dll in dlls)
{
if (assemblyName == dll)
{
string filelocation = "$dir" + dll + ".dll";
//Console.WriteLine(filelocation);
return Assembly.LoadFrom(filelocation);
}
}
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
// maybe this needs to change?
var info = assembly.GetName();
if (info.FullName == e.Name) {
return assembly;
}
}
return null;
}
}
"@
$null = Add-Type -TypeDefinition $source
}
try {
$redirector = New-Object Redirector
[System.AppDomain]::CurrentDomain.add_AssemblyResolve($redirector.EventHandler)
} catch {
# unsure
}
}
$dll =
if ($PSVersionTable.PSVersion.Major -ge 6) {
Join-Path $script:libraryroot "lib\net6.0\dbatools.dll"
} else {
Join-Path $script:libraryroot "lib\net462\dbatools.dll"
}
try {
Import-Module "$dll"
} catch {
throw "Couldn't import dbatools.dll | $PSItem"
}
if ($PSVersionTable.PSEdition -ne "Core") {
[System.AppDomain]::CurrentDomain.remove_AssemblyResolve($onAssemblyResolveEventHandler)
}
I would also like a way to run commands (such as a disconnect) before and after specific commands without actually having to code that into there. When we talked, you mentioned this syntax which I liked.
[Reflection.AssemblyMetadata("TypeConflict", "t")]
[Reflection.AssemblyMetadata("TypeConflict", "t2")]
[Reflection.AssemblyMetadata("TypeConflict", "t3")]
param()
Thank you!
