EF6,如果没有App.config文件,SQLite无法工作

18

我正在尝试创建一个插件,它将使用EF6.1和SQLite为一个应用程序提供服务,但我无法更改App.config文件,所以所有的配置和连接字符串都需要在代码中设置。

我发现了这个看起来很有前途的答案:Problems using Entity Framework 6 and SQLite

现在我有一个像这样的配置类:

public class CollectionDbConfiguration : DbConfiguration
{
    public CollectionDbConfiguration()
    {
        SetProviderServices("System.Data.SQLite"(DbProviderServices)SQLiteProviderFactory.Instance.GetService(typeof(DbProviderServices)));
        SetProviderFactory("System.Data.SQLite.EF6", SQLiteProviderFactory.Instance);
        SetProviderFactory("System.Data.SQLite", SQLiteFactory.Instance);                 
    }
}

在第一次创建上下文并返回所有这些返回值之前,我已确认会执行此操作。

我的上下文看起来像这样

public class MyContext : DbContext
{
    public MyContext(string connectionString)
    :   base(connectionString) { }

    public DbSet<MyEntity> MyEntities { get; set; }      
}

我有一些调用它的代码,如下所示:

var context = new MyContext("Data Source = mytest.db; Version = 3;");
var entities = context.MyEntities.ToList();
当我尝试运行代码时,上下文似乎默认连接字符串是针对 SQL Server 的,因此它会给出:“关键字不受支持:'version'。” 如果我删除它,则会收到一个错误,指出无法连接并且明显尝试连接到 SQL Server 数据库。 我尝试通过添加构造函数传递 SQLite 连接:
public MyContext(DbConnection connection) 
    : base(connection, contextOwnsConnection: true)
{ }

使用此调用:

var context = new MyContext(new SQLiteConnection("Data Source = mytest.db; Version = 3;"));
var entities = context.MyEntities.ToList();

但是我遇到了这个错误:

无法确定类型为'System.Data.SQLite.SQLiteConnection'的连接所需的DbProviderFactory类型。请确保已在应用程序配置中安装或注册ADO.NET提供程序。

因此,我创建了一个工厂解析器并进行了注册。

public class FactoryResolver : IDbProviderFactoryResolver
{
    public DbProviderFactory ResolveProviderFactory(DbConnection connection)
    {
        if (connection.GetType() == typeof(SQLiteConnection))
        {
            return SQLiteFactory.Instance;
        }

        if (connection.GetType() == typeof(EntityConnection))
        {
            return SQLiteProviderFactory.Instance;
        }

        return null;
    }
}

并添加:

SetProviderFactoryResolver(new FactoryResolver());

但是现在我得到了这个错误:

找不到名称为'System.Data.SQLite.EF6'的ADO.NET提供程序的Entity Framework提供程序。请确保在应用程序配置文件的“entityFramework”部分中注册了提供程序。有关更多信息,请参见http://go.microsoft.com/fwlink/?LinkId=260882

我已经花了两天时间在这个问题上了,现在已经没有任何想法了。

1个回答

21

使具有连接字符串的构造函数工作所需的最小内容是自定义的IProviderInvariantNameIDbDependencyResolverDbConfiguration

public class SQLiteProviderInvariantName : IProviderInvariantName
{
    public static readonly SQLiteProviderInvariantName Instance = new SQLiteProviderInvariantName();
    private SQLiteProviderInvariantName() { }
    public const string ProviderName = "System.Data.SQLite.EF6";
    public string Name { get { return ProviderName; } }
}

class SQLiteDbDependencyResolver : IDbDependencyResolver
{
    public object GetService(Type type, object key)
    {
        if (type == typeof(IProviderInvariantName)) return SQLiteProviderInvariantName.Instance;
        if (type == typeof(DbProviderFactory)) return SQLiteProviderFactory.Instance;
        return SQLiteProviderFactory.Instance.GetService(type);
    }

    public IEnumerable<object> GetServices(Type type, object key)
    {
        var service = GetService(type, key);
        if (service != null) yield return service;
    }
}

class SQLiteDbConfiguration : DbConfiguration
{
    public SQLiteDbConfiguration()
    {
        AddDependencyResolver(new SQLiteDbDependencyResolver());
    }
}

现在应该可以工作了:

var context = new MyContext("Data Source = mytest.db; Version = 3;");
var entities = context.MyEntities.ToList();

更新: 对于.NET 4.0,您还需要一个自定义的IDbProviderFactoryResolver

class SQLiteDbProviderFactoryResolver : IDbProviderFactoryResolver
{
    public static readonly SQLiteDbProviderFactoryResolver Instance = new SQLiteDbProviderFactoryResolver();
    private SQLiteDbProviderFactoryResolver() { }
    public DbProviderFactory ResolveProviderFactory(DbConnection connection)
    {
        if (connection is SQLiteConnection) return SQLiteProviderFactory.Instance;
        if (connection is EntityConnection) return EntityProviderFactory.Instance;
        return null;
    }
}

并添加

if (type == typeof(IDbProviderFactoryResolver)) return SQLiteDbProviderFactoryResolver.Instance;

SQLiteDbDependencyResolver.GetService 方法的实现。


我尝试了一下,但现在出现错误信息:“无法确定类型为'System.Data.SQLite.SQLiteConnection'的连接的DbProviderFactory类型。请确保在应用程序配置中安装或注册了ADO.NET提供程序。” - Mant101
这很奇怪,因为我能够完全按照你描述的方式复制该问题,并且上述方法解决了它。请确保项目中没有其他DbConfiguration类(例如,你在发布的代码中使用的那个类)和解析器 - 只有答案中提到的类。还要确保你使用的是带有连接字符串的构造函数,而不是连接。正如你所见,上述的IDbDependencyResolver会强制解析DbProviderFactory,因此不应该出现这样的错误。 - Ivan Stoev
如果您能提供完整的可重现项目,我可以看一看,但是我相信这就是解决方法,因为我能够通过显式注释掉App.config中与SQLite相关的条目,并保留localdb默认值、SqlServer和MySQL来连接/操作SQLite数据库。 - Ivan Stoev
太棒了,成功了,谢谢。我之前尝试过自定义IDbProviderFactoryResolver,但是我没有为正确的DbConnection类型返回正确的类型,所以它失败了。 - Mant101
不客气。这个复现项目帮了很多忙 - 我的测试(NET4.6)使用的是 DefaultDbProviderFactoryResolver,而你的使用的是 Net40DefaultDbProviderFactoryResolver,它们似乎使用了不同的代码路径 - 前者仅使用 connection.ProviderFactory,而后者则使用配置文件 :( - Ivan Stoev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接