dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.62k stars 3.15k forks source link

SpatialiteLoader.Load failed (Self-Contained deploy) EF Core 2.2 #14093

Closed sergey-messer closed 4 years ago

sergey-messer commented 5 years ago

Exception

System.InvalidOperationException: Sequence contains no matching element at System.Linq.Enumerable.First[TSource](IEnumerable1 source, Func2 predicate) at Microsoft.EntityFrameworkCore.Infrastructure.SpatialiteLoader.FindExtension() at Microsoft.EntityFrameworkCore.Infrastructure.SpatialiteLoader.Load(DbConnection connection) at Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal.SqliteRelationalConnection.LoadSpatialite() at Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal.SqliteRelationalConnection.OpenAsync(CancellationToken cancellationToken, Boolean errorsExpected) at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable1.AsyncEnumerator.BufferlessMoveNext(DbContext _, Boolean buffer, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable1.AsyncEnumerator.MoveNext(CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.AsyncSelectEnumerable2.AsyncSelectEnumerator.MoveNext(CancellationToken cancellationToken) at System.Linq.AsyncEnumerable.FirstOrDefault_[TSource](IAsyncEnumerable1 source, CancellationToken cancellationToken) in D:\a\1\s\Ix.NET\Source\System.Interactive.Async\First.cs:line 144

Workaround:

var c = (SqliteConnection)applicationSqliteContext.Database.GetDbConnection();
            c.Open();
            try
            {
                SpatialiteLoader.Load(c);
            }
            catch (Exception e)
            {
                var file = "mod_spatialite";
                if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
                {
                    file += ".dylib";
                }
                else if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    file += ".so";
                }
                var select = $"SELECT load_extension('{file}')";

                c.EnableExtensions();
                applicationSqliteContext.Database.ExecuteSqlCommand(select);
                c.EnableExtensions(false);
            }

Technical details

EF Core version: 2.2 Database Provider: Sqlite IDE: (e.g. Visual Studio 2017 15.9.3) Server Win 2016 ( Core 2.2 Run-time NOT installed ) Self-Contained deploy

cmiles commented 5 years ago

Adding this report since I believe I am seeing the same bug with later versions than in the original report Microsoft.EntityFrameworkCore.Sqlite 3.0.0-preview4.19216.3, Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite 3.0.0-preview4.19216.3 and .NET Core 3.0.100-preview4-011223. Perhaps that is expected with no commits linked to this issue but just-in-case leaving the report below...

A .NET Core Console Project using EF Core Sqlite configured with .UseNetTopologySuite() causes a crash in .exes built with dotnet publish --self-contained.

The program runs as expected from the commandline with dotnet run and in VS2019 Community 16.0.2 F5 in both debug and release configurations. If .UseNetTopologySuite() is eliminated (and no spatial data is used) the published versions (with --self-contained) run successfully.

Exception message:

Unhandled Exception: System.InvalidOperationException: Sequence contains no matching element
   at System.Linq.ThrowHelper.ThrowNoMatchException()
   at System.Linq.Enumerable.First[TSource](IEnumerable`1 source, Func`2 predicate)
   at Microsoft.EntityFrameworkCore.Infrastructure.SpatialiteLoader.FindExtension()
   at Microsoft.EntityFrameworkCore.Infrastructure.SpatialiteLoader.Load(DbConnection connection)
   at Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal.SqliteRelationalConnection.LoadSpatialite()
   at Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal.SqliteRelationalConnection.Open(Boolean errorsExpected)
   at Microsoft.EntityFrameworkCore.Sqlite.Storage.Internal.SqliteDatabaseCreator.Delete()
   at Microsoft.EntityFrameworkCore.Storage.RelationalDatabaseCreator.EnsureDeleted()
   at SpatialitePublishCrash.Program.Main(String[] args) in C:\Code\SpatialitePublishCrash\Program.cs:line 51

Steps to reproduce

With .NET Core 3.0.100-preview4-011223 dotnet new Console, dotnet add package Microsoft.EntityFrameworkCore.Sqlite and dotnet add package Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite to get a minimal console project.

The code below will build/run as expected for dotnet build/run and VS2019 Community 16.0.2.

However running from an .exe created with the variations of dotnet publish with --self-contained I tried all produced the exception above when .EnsureDeleted() is reached on the context with UseNetTopologySuite().

If you comment the code related to the context with UseNetTopologySuite() the program runs correctly in all cases (VS/dotnet build/dotnet run/publish).

The attached zip shows the problem using the code included below - the included test scripts show the problems and also show exactly what I ran (in case there is a code/build/usage error) - SpatialitePublishCrash03.zip

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;

namespace SpatialitePublishCrash
{
    class Program
    {
        static int Main(string[] args)
        {
            var entityList = new List<NoSpatialColumnsEntity>
            {
                new NoSpatialColumnsEntity {Content = "Item A"},
                new NoSpatialColumnsEntity {Content = "Item B"},
                new NoSpatialColumnsEntity {Content = "Item C"},
            };

            Console.WriteLine();
            Console.WriteLine("\tSqlite Db without NetTopologySuite/Spatialite Test Starting");
            Console.WriteLine("\t\tCreate Context.");

            var noOptionsContext = new NoOptionsContext();

            Console.WriteLine("\t\tEnsure Deleted/Created.");

            noOptionsContext.Database.EnsureDeleted();
            noOptionsContext.Database.EnsureCreated();

            entityList.ForEach(x =>
            {
                noOptionsContext.NoSpatialColumnsEntities.Add(x);
            });

            Console.WriteLine($"\t\tSaving Changes");

            noOptionsContext.SaveChanges(true);

            Console.WriteLine($"\t\t{noOptionsContext.NoSpatialColumnsEntities.Count()} Items found in db");

            Console.WriteLine($"\tSqlite Db without NetTopologySuite/Spatialite Test Ending");
            Console.WriteLine();

            Console.WriteLine("\tSqlite Db with UseNetTopologySuite Test Starting");
            Console.WriteLine("\t\tCreate Context.");

            var netTopologyContext = new UseNetTopologySuiteContext();

            Console.WriteLine("\t\tEnsure Deleted/Created.");

            netTopologyContext.Database.EnsureDeleted();
            netTopologyContext.Database.EnsureCreated();

            entityList.ForEach(x =>
            {
                netTopologyContext.NoSpatialColumnsEntities.Add(x);
            });

            Console.WriteLine($"\t\tSaving Changes");

            netTopologyContext.SaveChanges(true);

            Console.WriteLine($"\t\t{netTopologyContext.NoSpatialColumnsEntities.Count()} Items found in db");

            Console.WriteLine($"\tSqlite Db with UseNetTopologySuite Test Ending");
            Console.WriteLine();

            return 0;
        }

        public class NoOptionsContext : DbContext
        {
            public DbSet<NoSpatialColumnsEntity> NoSpatialColumnsEntities { get; set; }

            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                optionsBuilder
                    .EnableSensitiveDataLogging()
                    .UseSqlite($@"Data Source=nooptionstestdb.db");
            }
        }

        public class NoSpatialColumnsEntity
        {
            public int Id { get; set; }
            public string Content { get; set; }
        }

        public class UseNetTopologySuiteContext : DbContext
        {
            public DbSet<NoSpatialColumnsEntity> NoSpatialColumnsEntities { get; set; }

            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                optionsBuilder.UseSqlite($@"Data Source=nettopologysuitetest.db", x => { x.UseNetTopologySuite(); });
            }
        }
    }
}

Further technical details

EF Core version: 3.0.0-preview4.19216.3 Database Provider: Microsoft.EntityFrameworkCore.Sqlite.NetTopologySuite 3.0.0-preview4.19216.3 Operating system: Windows 10 Pro 10.0.17763 Build 17763 IDE: Commandline dotnet + vs2019 Community 16.0.2

avk2k commented 5 years ago

This happens because in the runtime enviroment dependencyContext.Default is not null. And in source code src/EFCore.Sqlite.Core/Infrastructure/SpatialiteLoader.cs in method FindExtension() local variable hasDependencyContext set true. So line var rids = DependencyContext.Default.RuntimeGraph.First(g => g.Runtime == rid).Fallbacks.ToList(); rising error (Sequence contains no matching element) because DependencyContext.Default.RuntimeGraph is null.