IIS Shadow Copied DLL accessing dependency DLLs from bin location

599 Views Asked by At

I am trying to resolve an issue with ASP.Net Framework 4.8 site using EFCore 3.1.16 in IIS. Microsoft.Data.SqlClient has a process lock on SNI.dll which causes issues with xcopy deployment in IIS.

I have tried a strategy of copying the SNI.dll to the same shadow copy location as Microsoft.Data.SqlClient so it doesn't have to try and access the DLL in the bin folder as outlined in https://github.com/lscorcia/sqlclient.snishadowcopy.

// Look for the main Microsoft.Data.SqlClient assembly in the 
// shadow copy path
var sqlClientShadowAssembly = Directory.GetFiles(
currentShadowPath, "Microsoft.Data.SqlClient.dll",
SearchOption.AllDirectories).FirstOrDefault();

// Extract the directory information from the shadow assembly path
var sqlClientShadowPath = 
Path.GetDirectoryName(sqlClientShadowAssembly);

// Find out the process bitness and choose the appropriate native 
// assembly
var moduleName = Environment.Is64BitProcess ? "x86\\SNI.dll"
        : "x64\\SNI.dll";
// Compute the source and target paths for the native assembly
var sourceFile = Path.Combine(currentPrivatePath, moduleName);
var targetFile = Path.Combine(sqlClientShadowPath, "SNI.dll");
File.Copy(sourceFile, targetFile);

However, it still tries to access the bin location first instead of the sni.dll that is in the same folder location.

I have checked that the Microsoft.Data.SqlClient in the shadow location is being used correctly by deleting the DLL and confirming that a FileNotFound exception is thrown.I have also tried copying directly into the same folder and also copying into an x64 sub folder in the shadow location.

1

There are 1 best solutions below

0
Uwe Keim On

In my case, the error occured only when my IIS application is located on an UNC path (e.g. "\\myserver\myshare\myapplication"). Here is a workaround that worked in my scenario.

Use P/Invoke to SetDllDirectory:

[DllImport(@"kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool SetDllDirectory(string lpPathName);

(See also this MSDN article about DLL load order)

Then, early in my Global.asax.cs Application_Start method (before any DB calls), call it like this:

SetDllDirectory(Server.MapPath(@"~/bin"))

After that, everything works as expected.


I still to consider this to be kind of a hack/workaround, but at least, it is working now in my scenario.

As I do understand, you can call SetDllDirectory multiple times to add additional directories (i.e. not overwrite the existing one).

So in case someone reading this might have other assemblies that refer to native DLLs in "x86" or "x64" folders, one might do something like this:

var bitness = Environment.Is64BitProcess ? @"x64" : @"x86";
SetDllDirectory(Server.MapPath($@"~/bin/{bitness}"));

I've also tried serving my test application from a local path (like "C:\Inetpub\wwwroot") and here, the error does not occur, even when not calling SetDllDirectory.

I'm still not sure why the error occurs for UNC paths only, and not for local paths, as I would expect that the shadow copied managed assemblies to fail the DllImports, too.


(I've also posted the above in this GitHub issue)