praeclarum / sqlite-net

Simple, powerful, cross-platform SQLite client and ORM for .NET
MIT License
3.97k stars 1.42k forks source link

Path is not of a legal form when swapping from sqlite-net to sqlite-net-pcl #1115

Open agaertner opened 2 years ago

agaertner commented 2 years ago

So I have this code

        private async Task LoadDatabase()
        {
            var filePath = Path.Combine(this.CacheDir, "data.db");
            _db = new SQLiteAsyncConnection(filePath);
            await _db.CreateTableAsync<MusicSheetModel>();
        }

which basically is

"I:\\Repositories\\github.com\\redacted\\.debug.settings\\data.db"

or a folder under documents.

I previously used the sqlite-net package and it worked without a hitch. However, the service I am using needs the sqlite3 dll references and so I swapped to the pcl version. The pcl version however does not like the path I am giving it. The code itself did not change. I literally changed nothing but the nuget packages.

ericsink commented 2 years ago

Do you have an exception stack trace?

agaertner commented 2 years ago
System.AggregateException
  HResult=0x80131500
  Message=One or more errors occurred.
  Source=Blish HUD
  StackTrace:
   at Blish_HUD.Modules.Module.CheckForLoaded()
   at Blish_HUD.ModuleService.Update(GameTime gameTime)
   at Blish_HUD.BlishHud.Update(GameTime gameTime)
   at Microsoft.Xna.Framework.Game.DoUpdate(GameTime gameTime)
   at Microsoft.Xna.Framework.Game.Tick()
   at MonoGame.Framework.WinFormsGameWindow.TickOnIdle(Object sender, EventArgs e)
   at System.Windows.Forms.Application.ThreadContext.System.Windows.Forms.UnsafeNativeMethods.IMsoComponent.FDoIdle(Int32 grfidlef)
   at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
   at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
   at MonoGame.Framework.WinFormsGameWindow.RunLoop()
   at Microsoft.Xna.Framework.Game.Run(GameRunBehavior runBehavior)
   at Blish_HUD.Program.Main(String[] args)

  This exception was originally thrown at this call stack:
    [External Code]

Inner Exception 1:
TypeInitializationException: The type initializer for 'SQLite.SQLiteConnection' threw an exception.

Inner Exception 2:
ArgumentException: The path is not of a legal form.
ericsink commented 2 years ago

Hmmm. That wasn't as informative as I hoped. :-(

The main difference between sqlite-net and sqlite-net-pcl is that the latter uses SQLitePCLRaw (which I maintain) under the hood. The exception appears to be one thrown by somewhere in System.IO.Path, but I don't know if the offending call is in my code or in sqlite-net.

agaertner commented 2 years ago

Can't you compare it to the other package?

The sqlite-net package works without a hitch. Only the sqlite-net-pcl version does this.

Any ideas to work around this? I am thinking going back to the sqlite-net package and also referencing SQLite 3.0 from https://www.nuget.org/packages/SQLite/

ericsink commented 2 years ago

Both sqlite-net and sqlite-net-pcl are based on the same code, here in this repo, maintained by the same person, which is not me. I have never used either library. I only maintain the SQLitePCLRaw library. sqlite-net-pcl is simply a version of sqlite-net which uses my library instead of its own pinvoke layer.

I remain curious about what is causing the exception you see, which is why I asked for the stack trace. But the stack trace I need is the one from the inner exception, not the AggregateException. If you can provide that, I may be able to see what is going wrong.

agaertner commented 2 years ago

Ah misunderstood.

Here's the inner exception stacktrace:

   at System.IO.Path.NormalizePath(String path, Boolean fullCheck, Int32 maxPathLength, Boolean expandShortPaths)
   at System.IO.Path.InternalGetDirectoryName(String path)
   at SQLitePCL.NativeLibrary.MakePossibilitiesFor(String basename, Assembly assy, Int32 flags, LibSuffix suffix)
   at SQLitePCL.NativeLibrary.MyLoad(String basename, Assembly assy, Int32 flags, Action`1 log)
   at SQLitePCL.NativeLibrary.Load(String libraryName, Assembly assy, Int32 flags)
   at SQLitePCL.Batteries_V2.MakeDynamic(String name, Int32 flags)
   at SQLitePCL.Batteries_V2.DoDynamic_cdecl(String name, Int32 flags)
   at SQLite.SQLiteConnection..cctor()
   at SQLite.SQLiteConnection..ctor(SQLiteConnectionString connectionString)
   at SQLite.SQLiteConnectionPool.Entry..ctor(SQLiteConnectionString connectionString)
   at SQLite.SQLiteConnectionPool.GetConnectionAndTransactionLock(SQLiteConnectionString connectionString, Object& transactionLock)
   at SQLite.SQLiteAsyncConnection.<>c__DisplayClass33_0`1.<WriteAsync>b__0()
   at System.Threading.Tasks.Task`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()
--- End of stack trace from previous location ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at Nekres.Musician.UI.MusicSheetService.<LoadDatabase>d__13.MoveNext() in I:\Repositories\github.com\Blish-HUD-Musician\Musician Module\UI\MusicSheetService.cs:line 44
agaertner commented 2 years ago

image image

ericsink commented 2 years ago

OK, the stack trace is helpful, thanks. The exception occurs from a call in my code, but I don't yet see why. I'll take a closer look and see if I can figure it out. I've never seen this particular problem before.

agaertner commented 2 years ago

Some other info: The assembly and all the dependencies including sqlite-net-pcl of my project are being loaded into another app as a module.

ericsink commented 2 years ago

Your latest comment may be quite relevant. I was just looking at my code for MakePossibilitiesFor. It is trying to find the instance of the native SQLite library. The code it uses is basically equivalent to this:

var assembly = typeof(SQLitePCL.raw).Assembly;
var dir = System.IO.Path.GetDirectoryName(assembly .Location);

And apparently assembly .Location returns a string that GetDirectoryName() does not like. Which might happen if the assembly object is special because it was loaded differently?

You could verify this by putting the two lines above somewhere very early in your initialization, maybe just before you would be calling SQLite stuff, and see if you get the same exception. I wonder what assembly.Location is.

Note that if my guess is correct, then the path exception you saw has nothing to do with, the path of your sqlite database file.

TheHarimauMalaya commented 2 years ago

Hi @ericsink / @agaertner - I seem to have the same issue like yours. Have you guys got that issue solved? If so mind to brief how did you solve it?

`System.TypeInitializationException: The type initializer for 'SQLite.SQLiteConnection' threw an exception. ---> System.ArgumentException: The path is not of a legal form.
at System.IO.Path.LegacyNormalizePath(String path, Boolean fullCheck, Int32 maxPathLength, Boolean expandShortPaths)
at System.IO.Path.InternalGetDirectoryName(String path)
at SQLitePCL.NativeLibrary.MakePossibilitiesFor(String basename, Assembly assy, Int32 flags, LibSuffix suffix)
at SQLitePCL.NativeLibrary.MyLoad(String basename, Assembly assy, Int32 flags, Action`1 log)
at SQLitePCL.NativeLibrary.Load(String libraryName, Assembly assy, Int32 flags)
at SQLitePCL.Batteries_V2.MakeDynamic(String name, Int32 flags)
at SQLitePCL.Batteries_V2.DoDynamic_cdecl(String name, Int32 flags)
at SQLite.SQLiteConnection..cctor()
--- End of inner exception stack trace ---
`at SQLite.SQLiteConnection..ctor(SQLiteConnectionString connectionString)
at SQLite.SQLiteConnectionPool.Entry..ctor(SQLiteConnectionString connectionString)
at SQLite.SQLiteConnectionPool.GetConnectionAndTransactionLock(SQLiteConnectionString connectionString, Object& transactionLock)
at SQLite.SQLiteAsyncConnection.<>c__DisplayClass33_0`1.<WriteAsync>b__0()
at System.Threading.Tasks.Task`1.InnerInvoke()
at System.Threading.Tasks.Task.Execute()'
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at Code.General._abcdef.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()`
ericsink commented 2 years ago

I never got a reply to my previous comment, but I'd still be curious if something about the configuration is causing Assembly.Location to be something unexpected.

agaertner commented 2 years ago

Unfortunately I swapped over to LiteDB hence I couldn't further verify it. But I would assume since my stuff is bundled in an archive and loaded into another project as a module dll as well as its dependencies it might be that some directory identification issues are at fault.

PrincessLunaOfficial commented 1 year ago

Just wasted 2 hours to get it work. This package does not work with Costura.Fody or anything else that embeds references as resources.

mamift commented 1 year ago

Just wasted 2 hours to get it work. This package does not work with Costura.Fody or anything else that embeds references as resources.

Yes I can confirm you have to exclude all sqlite dlls from Costura.Fody build action. You can still use Costura.Fody in your project, but you just can't bundle the sqlite libraries:

In the FodyWeavers.xml

<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
  <Costura>
    <ExcludeAssemblies>
      sqlite*
    </ExcludeAssemblies>
  </Costura>
</Weavers>
ynzenu commented 4 months ago

@ericsink I'm facing the same issue and hope you can provide some insight. Assembly.Location returns an empty string when the assembly is loaded using Assembly.Load - that's what triggers the previously mentioned error: ArgumentException: The path is not of a legal form.

I've tried to use SQLitePCL.raw.SetProvider(new SQLitePCL.SQLite3Provider_e_sqlite3()); instead of a bundle and can't get it to work due to the error below, which seemed to be caused by using packages.config, but after migrating to PackageReference and trying numerous different solutions the issue still persists. Some kind compatibility issue perhaps? Unable to load DLL 'e_sqlite3': The specified module could not be found

It would be good to get a workaround for this. I did see that in the MakePossibilitiesFor method it will use Assembly.GetExecutingAssembly().CodeBase instead of Assembly.Location, but I don't understand when exactly this happens.

Details .NET Framework 4.7.2 Class Library sqlite-net-base - 1.8.116 SQLitePCLRaw.core - 2.1.8 SQLitePCLRaw.lib.e_sqlite3 - 2.1.8 SQLitePCLRaw.provider.e_sqlite3

ericsink commented 4 months ago

If you're using SetProvider with provider.e_sqlite3, the error indicates that the native e_sqlite3.dll didn't get copied into your build output directory. When using .NET Framework, modern versions of the .NET SDK handle this differently than how things worked in the old days. Try adding a RuntimeIdentifier property to your csproj:

<RuntimeIdentifier>win-x64</RuntimeIdentifier>
ynzenu commented 4 months ago

Managed to get it working by embedding the e_sqlite3.dll file using Costura. Hopefully this will help anyone facing the same issue.

This post describes how to embed the e_sqlite3.dll file using Costura.

laomms commented 6 days ago

Managed to get it working by embedding the e_sqlite3.dll file using Costura. Hopefully this will help anyone facing the same issue.

  • The sqlite-net-pcl package depends on a SQLitePCLRaw bundle that uses Assembly.Location to determine the location of the e_sqlite3.dll assembly, which can return an empty string if the assembly referencing the package is loaded by an MEF program like Blish HUD, or according to other people when using Costura.Fody.
  • The sqlite-net-base package does not depend on the bundle and can therefore still be used together with Costura. That does mean you have to install the SQLitePCLRaw's core, provider and lib packages yourself.

This post describes how to embed the e_sqlite3.dll file using Costura.

it doesn't work