Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native assembly loading outside of application (e_sqlite3.dll) #343

Open
yojagad opened this issue May 6, 2020 · 24 comments
Open

Native assembly loading outside of application (e_sqlite3.dll) #343

yojagad opened this issue May 6, 2020 · 24 comments

Comments

@yojagad
Copy link

yojagad commented May 6, 2020

We have a hosted service that uses an overridden AssemblyLoadContext to load native assemblies. One of our customers moved from Microsoft.EntityFrameworkCore.Sqlite 2.2.6 to 3.3.1. The former seemed to be using SQLPCLRaw.bundle_green and we were able to see our custom AssemblyLoadContext being invoked for probing and thereby loading e_sqlite3.dll.

This does not seem to be the case for the latter though (3.3.1 and the app was on .net core 3.1) which is using SQLPCLRaw.bundle_e_sqlite3 and it seems to keep throwing errors saying e_sqlite3.dll was not found because it did not use our custom AssemblyLoadContext to probe for native assemblies. Interestingly, targeting the app to use netstandard 2.0 seem to go back to using our custom AssemblyLoadContext.

Are there plans to improve the native dependency loading behavior by engaging the CLR probing
especially in case of assembly load failures like the above?

cc: @fabiocav

@bricelam
Copy link
Contributor

I think NativeLibrary.Load() bypasses AssemblyLoadContext. It's probably worth filing an issue on dotnet/runtime to see if this is a bug or by design.

@fabiocav
Copy link

That's correct. With the current implementation, that is the case, which causes problems in any environment providing isolation with ALC (including Azure Functions)

@aderderian
Copy link

My setup is Azure Functions V1 - .net framework 4.8
Middle layer is .net standard 2.0 with reference to Microsoft.Data.Sqlite
Exception thrown while debugging in VS2019 is it can't load e_sqlite3.dll
Any workarounds would be awesome so we can keep moving forward. Thanks!

@ericsink
Copy link
Owner

The workaround I would investigate would be to use the DllImport e_sqlite3 SQLitePCLRaw provider instead of the dynamic one.

This would involve manual steps to do the things that are usually done for you by the bundle packages.

So instead of SQLitePCLRaw.bundle_whatever, add a reference to SQLitePCLRaw.core (to get the core library) and SQLitePCLRaw.provider.e_sqlite3 (to get the provider implementation) and SQLitePCLRaw.lib.e_sqlite3 (to get the e_sqlite3 library itself).

Without the bundle, you'll need to initialize SQLitePCLRaw yourself by creating a provider and calling SetProvider.

SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_e_sqlite3());

I don't actually know if this will work. I'm just thinking that if the problem here is that NativeLibrary is failing because of AssemblyLoadContext things, then perhaps regular old DllImport would work.

Cc @bricelam in case he sees me saying something dumb here.

@fabiocav
Copy link

fabiocav commented Jul 30, 2020

The DllImport should work. We could validate this in Azure Functions.

For context (@aderderian), we're only tracking this for Functions 3.0 at this point.

FYI, @yojagad

@aderderian
Copy link

Thanks guys I will try now. I am using Azure Functions V1 right because I need to be running .net 4.8 for an old (eb) assembly to work. All seems to work fine in Functions V3 if I paste the e_sqlite3.dll into the bin in V3. No dice in V1.

Do I add these references and do the import in my Azure Function or in my .NET Standard layer?

Azure/Azure-Functions#1664

@ericsink
Copy link
Owner

I think those references and the init call go in your Azure Function project, but I should admit I have very little experience with Azure Functions.

@aderderian
Copy link

image

When I call it from Azure Functions V1 on app Startup. I added all those references.

@fabiocav
Copy link

That seems like a bitness mismatch. Are you running in a 32 or 64 bit app? Can you make sure it matches the native dependency you're deploying?

@aderderian
Copy link

aderderian commented Jul 30, 2020

Yes. I just switched it to force 64 bit and it now got past the : SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_e_sqlite3());

Same error though when I try and run a SqlCommand using Dapper and Microsoft.Data.Sqlite (5.0.0-preview.7.20365.15)

image

@ericsink
Copy link
Owner

You appear to still have a reference somewhere to a bundle package. I'm guessing it's probably coming in through Microsoft.Data.Sqlite?

Try Microsoft.Data.Sqlite.Core instead?

@aderderian
Copy link

Here is my .NET Standard data layer project :

image

And this is my Azure Function :

image

@ericsink
Copy link
Owner

Yeah, that's what I'm talking about. You have a reference to Microsoft.Data.Sqlite, which is bringing in (as a dependency) SQLitePCLRaw.bundle_e_sqlite3, and you don't want that.

Try changing it to Microsoft.Data.Sqlite.Core instead.

@aderderian
Copy link

Did that. Oddly now the WebApp that also uses this shared .NET Standard library will now not start with the error below. My Azure Function and the webapp share a data layer for some information. The error below is actually happening outside of all this, so no clue how it is event getting over to that now.

An unhandled exception occurred while processing the request.
TypeLoadException: Method 'sqlite3_stmt_isexplain' in type 'SQLitePCL.SQLite3Provider_dynamic_cdecl' from assembly 'SQLitePCLRaw.provider.dynamic_cdecl, Version=2.0.2.669, Culture=neutral, PublicKeyToken=b68184102cba0b3b' does not have an implementation.
SQLitePCL.Batteries_V2.DoDynamic_cdecl(string name, int flags)

TargetInvocationException: Exception has been thrown by the target of an invocation.
System.RuntimeMethodHandle.InvokeMethod(object target, object[] arguments, Signature sig, bool constructor, bool wrapExceptions)

TypeInitializationException: The type initializer for 'Microsoft.Data.Sqlite.SqliteConnection' threw an exception.
Microsoft.Data.Sqlite.SqliteConnection..ctor()

TargetInvocationException: Exception has been thrown by the target of an invocation.
System.RuntimeTypeHandle.CreateInstance(RuntimeType type, bool publicOnly, bool wrapExceptions, ref bool canBeCached, ref RuntimeMethodHandleInternal ctor, ref bool hasNoDefaultCtor)

@ericsink
Copy link
Owner

provider.dynamic_cdecl is not supposed to be around anymore. Somewhere it is still sneaking in. Maybe some other dependency. Maybe try a clean and full rebuild.

@aderderian
Copy link

So strange, so the WebApp has the following references. I use OrchardCore (which I love and have used for years). They use Sqlite as well. Could the bundle there be conflicting with this somehow?

image

@aderderian
Copy link

It's a tough predicament because at least in Functions V3, adding the e_sqlite3.dll to the bin solves it. And we only use SQlite for local dev anyways. But we need Functions V1 for legacy .NET 4.8 projects and everything works, except this. I also tried System.Data.Sqlite and the same thing happened but it was System.Data.Interop that was the missing dll.

@ericsink
Copy link
Owner

"Could the bundle there be conflicting with this somehow?"

The workaround we've been discussing requires calling SetProvider(). If a bundle_e_sqlite package is getting into the project where we are trying to use that workaround, then yes, that's a problem.

Another thing you could try is adding a call to FreezeProvider() just below the SetProvider() call:

SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_e_sqlite3());
SQLitePCL.raw.FreezeProvider();

If your SetProvider() call happens first, then the FreezeProvider() call should prevent others from messing it up.

@aderderian
Copy link

I can't really modify the core guts of OrchardCore and YesSQL, and my Azure functions layer shares a data layer with one of the custom modules I have built into the Orchard pipeline.

Not really sure how to resolve it. A lot of moving parts here.

@ericsink
Copy link
Owner

Did you try the FreezeProvider call?

@aderderian
Copy link

I did not. I am not sure where I would insert that in the pipeline of the web app starting. I can't modify the underlying assembly references. It is getting a little non-maintainable, but I really appreciate the help. I think the main issue is Functions V1 won't easily let me just add the dll in the bin.

@ericsink
Copy link
Owner

ericsink commented Aug 3, 2020

Link to related issue:

dotnet/runtime#13819

@TheCollegedude
Copy link

SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_e_sqlite3());
SQLitePCL.raw.FreezeProvider();

@ericsink I put your two lines in the constructor of my class and it fixed the problem immediately!

@ericsink
Copy link
Owner

This issue is likely related to #553

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants