ericsink / SQLitePCL.raw

A Portable Class Library (PCL) for low-level (raw) access to SQLite
Apache License 2.0
512 stars 106 forks source link

How do I tell SQLitePCL where e_sqlite3 is? #484

Closed Ryan2065 closed 2 years ago

Ryan2065 commented 2 years ago

I'm working on a project where I run Entity Framework Core in PowerShell.

Because of this, all dll loads happen at runtime in a non-normal way. In general this isn't a huge deal, but for this library (well EntityFramework Core Sqlite), it cannot find e_sqlite3.

I've hooked up an assembly load context into AssemblyLoadContext.Default.Resolving and don't see this dll try to be loaded. I've also set a resolving method to AppDomain.CurrentDomain.AssemblyResolve but don't see e_sqlite3 come in there.

I can't just copy the .dll to base folder because this may run on Linux, Windows, or OSX. I know exactly where the right dll is programmatically (in base folder\runtimes\\native), I just need a way to tell SQLitePCL where it is.

How can I do that?

ericsink commented 2 years ago

You might look at NativeLibrary.SetDllImportResolver(). I think it would allow you to intercept DllImport and specify where the native library is.

Ryan2065 commented 2 years ago

That worked! If you don't have docs anywhere, this would be helpful to have documented for people. Here's what I did:

The build puts the sqlite files in /runtimes//native/e_sqlite3.dll. So to make Entity Framework Core figure it out,I hooked up the SetDllImportResolver in my classes constructor:

NativeLibrary.SetDllImportResolver(typeof(SQLitePCL.SQLite3Provider_e_sqlite3).Assembly, NativeAssemblyResolver);

Then NativeAssemblyResolver is just this (note, PoshContextInteractions is the class name I'm in):

        static IntPtr NativeAssemblyResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
        {
            if (!libraryName.Equals("e_sqlite3", StringComparison.OrdinalIgnoreCase))
                return IntPtr.Zero;
            var currentFolder = Path.GetDirectoryName(typeof(PoshContextInteractions).Assembly.Location);
            IntPtr libHandle = IntPtr.Zero;
            string dllPath = "";
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && RuntimeInformation.OSArchitecture == Architecture.X64)
            {
                dllPath = Path.Combine(currentFolder, "runtimes", "win-x64", "native", "e_sqlite3.dll");
            }
            else if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && RuntimeInformation.OSArchitecture == Architecture.X86)
            {
                dllPath = Path.Combine(currentFolder, "runtimes", "win-x86", "native", "e_sqlite3.dll");
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && RuntimeInformation.OSArchitecture == Architecture.X64)
            {
                dllPath = Path.Combine(currentFolder, "runtimes", "linux-x64", "native", "libe_sqlite3.so");
            }
            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
            {
                dllPath = Path.Combine(currentFolder, "runtimes", "osx", "native", "libe_sqlite3.dylib");
            }
            if (File.Exists(dllPath))
            {
                NativeLibrary.TryLoad(dllPath, assembly, searchPath, out libHandle);
            }
            return libHandle;
        }

Thanks for the help!