5

It is necessary to use the SQLite Entity Framework Database-first approach for the 3d-party application plugin. I searched all the Internet, including Add a DbProviderFactory without an App.Config, Problems using Entity Framework 6 and SQLite and many other. I have tried to use them all in different ways and combinations, but nothing helps:

"An unhandled exception of type 'System.Data.Entity.Core.MetadataException' occurred in mscorlib.dll. Additional information: Schema specified is not valid. Errors: AutosuggestModel.ssdl (2,2): error 0152: No Entity Framework provider found for the ADO.NET provider with invariant name 'System.Data.SQLite.EF6'. Make sure the provider is registered in the 'entityFramework' section of the application config file."

There is a test console application in the solution. With this minimal App.config it works:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <entityFramework>
    <providers>
      <provider invariantName="System.Data.SQLite.EF6" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
    </providers>
  </entityFramework>
  <system.data>
    <DbProviderFactories>
      <add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".NET Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
    </DbProviderFactories>
  </system.data>
</configuration>

The connection string has already implemented in the code. Used packages are:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="EntityFramework" version="6.1.3" targetFramework="net452" />
  <package id="System.Data.SQLite" version="1.0.98.1" targetFramework="net452" />
  <package id="System.Data.SQLite.Core" version="1.0.98.1" targetFramework="net452" />
  <package id="System.Data.SQLite.EF6" version="1.0.98.1" targetFramework="net452" />
  <package id="System.Data.SQLite.Linq" version="1.0.98.1" targetFramework="net452" />
</packages>

Please, give all the required code and specify where to insert it. Thanks in advance.

Community
  • 1
  • 1
costashu
  • 127
  • 1
  • 2
  • 10
  • the problem with sqlite and EF is: the database initializers are not working correctly, because Create database and Create table are not supported in the package (at least the versions I worked with). Your code does seem pretty weird. first you define the invariant System.Data.SQLite.EF6, and then you don't use it anymore. your problem is not really provider related, but code related. try renaming the invariant in the DbProviderFactories to "System.Data.SQLite.EF6", but maybe there are other errors I am missing here. – DevilSuichiro Sep 01 '15 at 18:37
  • Thanks, but with such App.config all works correctly, and I already have database. I need the application to run without App.сonfig. With the help of answers by the links above, the section "system.data" can be deleted, but other sections still necessary. – costashu Sep 01 '15 at 19:36
  • ahh, you meant it like this. well you need your app.config in your case, because you let your DbContext know that way that EF has to use this connection type. There is an overload to the constructor of your DbContext that actually takes a SqlConnection, I think with this it might work, call like :base(new SQLiteConnection([connection string]),true) – DevilSuichiro Sep 01 '15 at 21:11
  • Thanks again, but "the connection string has already implemented in the code". – costashu Sep 01 '15 at 21:39

1 Answers1

5

Here is a sample code that illustrates how to achieve the goal.

namespace SqliteEFNoConfig
{
    using System.Configuration;
    using System.Data;
    using System.Data.Common;
    using System.Data.Entity;
    using System.Data.Entity.Core.Common;
    using System.Data.SQLite;
    using System.Data.SQLite.EF6;
    using System.Linq;

    internal class Program
    {
        private static void Main()
        {
            // EF manages the connection via the DbContext instantiation
            // connection string is set in config
            // Use this code if you want to use a config file
            // with only the connection string
            //using (var model = new Model1())
            //{
            //    var dbSetProperty = model.dbSetProperty.ToList();
            //}

            // Alternative method: 
            // Use this code if you don't want to use a config file
            // You will also need to use the override constructor shown below,
            // in your EF Model class
            var connectionString = @"data source = {PathToSqliteDB}";
            using (var connection = new SQLiteConnection(connectionString))
            {
                using (var model = new Model1(connection))
                {
                    var dbSetProperty = model.dbSetProperty.ToList();
                }
            }
        }
    }

    class SqliteDbConfiguration : DbConfiguration
    {
        public SqliteDbConfiguration()
        {
            string assemblyName = typeof (SQLiteProviderFactory).Assembly.GetName().Name;

            RegisterDbProviderFactories(assemblyName );
            SetProviderFactory(assemblyName, SQLiteFactory.Instance);
            SetProviderFactory(assemblyName, SQLiteProviderFactory.Instance);
            SetProviderServices(assemblyName,
                (DbProviderServices) SQLiteProviderFactory.Instance.GetService(
                    typeof (DbProviderServices)));
        }

        static void RegisterDbProviderFactories(string assemblyName)
        {
            var dataSet = ConfigurationManager.GetSection("system.data") as DataSet;
            if (dataSet != null)
            {
                var dbProviderFactoriesDataTable = dataSet.Tables.OfType<DataTable>()
                    .First(x => x.TableName == typeof (DbProviderFactories).Name);

                var dataRow = dbProviderFactoriesDataTable.Rows.OfType<DataRow>()
                    .FirstOrDefault(x => x.ItemArray[2].ToString() == assemblyName);

                if (dataRow != null)
                    dbProviderFactoriesDataTable.Rows.Remove(dataRow);

                dbProviderFactoriesDataTable.Rows.Add(
                    "SQLite Data Provider (Entity Framework 6)",
                    ".NET Framework Data Provider for SQLite (Entity Framework 6)",
                    assemblyName,
                    typeof (SQLiteProviderFactory).AssemblyQualifiedName
                    );
            }
        }
    }
}

In case you decide to not add a connection string in the config file then you need to add the following constructor in the EF model.

public Model1(DbConnection connection)
    : base(connection, true)
{
}

Notice: The above code is just a sample on how to achieve the goal and you will have to adjust it accordingly to your needs. The above code is provided assuming you are using EF Code First approach.

Jimi
  • 1,208
  • 1
  • 11
  • 16
  • It doesn't work out of the box. You need to adjust it to your code. I specifically added the 'Notice' section for that reason. – Jimi Sep 07 '15 at 15:30
  • I adjusted. But it's not working. Try it yourself. The goal is - console application must work after the **removal** of the App.config file. – costashu Sep 07 '15 at 20:12
  • It works. I just checked it again. You do realize that the first sample code in Main requires the config file but only the connection string to be present in it. And the 'Alternative method' doesn't need any config file at all. This is what I mean by 'you need to adjust it to your needs'. – Jimi Sep 08 '15 at 14:10
  • You wrote "It works. I just checked it again." Please upload your example and give a link. I specifically asked in the question to give a complete working example. – costashu Sep 08 '15 at 23:10
  • Use only the alternative method. Remove any other code from Main. Needless to say that you also need to replace 'PathToSqliteDB' with the path to the sqlite db. And just for your FYI, I use the exact same sample without a config file in my console app. That's why I know it works. – Jimi Sep 09 '15 at 11:18
  • Jimi, please, give me your full test example. – costashu Sep 09 '15 at 18:44
  • I edited my answer to make more clear. Other than that you are on your own to figure it out. – Jimi Sep 10 '15 at 08:39
  • [Here](http://github.com/costashu/ConsoleApplication1) complete working example that uses your code. The connection string is removed from the file App.config and implemented in the code. Example works **only** with the file App.config. – costashu Sep 10 '15 at 17:36
  • Oh, my God. First of all you aren't mentioning anywhere that you are using EF Model First approach. Do yourself a favor and get rid of that habit. Switch to Code First approach. After all the designer will be deprecated in EF 7. When you do that, you will see that the code I have provided works. – Jimi Sep 11 '15 at 15:22
  • OK. I take it back, you are mentioning it in the first sentence. My bad. Didn't noticed it until now. Still you should consider switching to Code First. – Jimi Sep 11 '15 at 15:41
  • [Here](https://github.com/costashu/ConsoleApplication2) is an example Code First. It does not work at all. Can you modify any example to make it work without the App.config or give your working example? – costashu Sep 11 '15 at 17:50
  • Yeah I noticed that the code complains about not finding provider SQLiteFactory. Just add SetProviderFactory(assemblyName, SQLiteFactory.Instance); in line 34. I'm working on a repo with sample code for both Database First and Code First approach. I'll let you know when I'm done. – Jimi Sep 11 '15 at 17:58
  • Submitted a PR to your repo. Code works with both approaches. – Jimi Sep 11 '15 at 18:28
  • A huge thank you. This solution works perfectly. But only for the test console application, unfortunately. When it is used in a DLL, an exception occurs. After all my manipulations with folders of assemblies I found out that the `(DbProviderServices)SQLiteProviderFactory.Instance.GetService(typeof(DbProviderServices))` returns null. – costashu Sep 12 '15 at 00:15
  • With conventional dll the solution works, but with the plug-ins, it seems, SQLite has a problem. – costashu Sep 14 '15 at 23:54