同时将实体框架与 SQL Server 和 SQLite 数据库一起使用

Posted

技术标签:

【中文标题】同时将实体框架与 SQL Server 和 SQLite 数据库一起使用【英文标题】:Using entity framework with both SQL Server and SQLite databases simultaneously 【发布时间】:2013-11-03 07:08:03 【问题描述】:

我有一个用于测试目的的 C# .Net 4.0 控制台应用程序(使用 VS 2012)。我的目标是能够创建一个可以在 MS SQL Server 数据库和 SQLite 数据库上使用的单一实体框架 .edmx 文件。基本上,我想使用相同的实体模型类和集合进行查询,但可以很容易地在两个不同的数据库之间随意切换。

到目前为止,我已经通过连接到 MS Server 数据库并添加了我的单个测试表(称为联系人)来创建我的 .edmx 文件。有了这个,我可以使用以下代码从我的表中获取数据:

var db = new DataAccess.ContactTestEntities();
foreach (var contact in db.Contacts)
    Console.WriteLine("" + contact.ID + ". " + contact.FirstName + " " + contact.LastName);

现在,我希望能够使用相同的代码,但改为连接到 SQLite 数据库。我编写了一个部分类,允许我在构造时更改连接字符串,如下所示:

var db = new DataAccess.ContactTestEntities("MY SQLITE CONNECTION STRING");

在这方面它工作正常,除非在尝试查询数据库时出现此错误:

无法将“System.Data.SQLite.SQLiteConnection”类型的对象转换为“System.Data.SqlClient.SqlConnection”类型。

我试图找到解决方案,但遇到了死胡同,我正在努力寻找下一步要采取的措施。

这就是我的问题:我怎样才能解决这个问题?或者我可以采取其他方法来获得相同的预期结果吗?


上述异常的堆栈跟踪:

在 System.Data.SqlClient.SqlCommand.set_DbConnection(DbConnection 值)在 System.Data.Common.Utils.CommandHelper.SetStoreProviderCommandState(EntityCommand entityCommand, EntityTransaction entityTransaction, DbCommand storeProviderCommand) 在 System.Data.EntityClient.EntityCommandDefinition.ExecuteStoreCommands(EntityCommand entityCommand,CommandBehavior 行为)在 System.Data.Objects.Internal.ObjectQueryExecutionPlan.Execute[TResultType](ObjectContext 上下文,ObjectParameterCollection 参数值)在 System.Data.Objects.ObjectQuery1.GetResults(Nullable1 forMergeOption) 在 System.Data.Objects.ObjectQuery1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() at System.Data.Entity.Internal.Linq.InternalQuery1.GetEnumerator() 在 System.Data.Entity.Internal.Linq.InternalSet1.GetEnumerator() at System.Data.Entity.Infrastructure.DbQuery1.System.Collections.Generic.IEnumerable.GetEnumerator() 在 c:\Development\Projects\Test 中的 SQLiteTest.Program.ReadFromSqlite() Applications\SQLiteTest\SQLiteTest\Program.cs:第 82 行 c:\Development\Projects\Test 中的 SQLiteTest.Program.ReadTests() Applications\SQLiteTest\SQLiteTest\Program.cs:第 63 行 c:\Development\Projects\Test 中的 SQLiteTest.Program.ProcessMenu() Applications\SQLiteTest\SQLiteTest\Program.cs:第 36 行 c:\Development\Projects\Test 中的 SQLiteTest.Program.Main(String[] args) Applications\SQLiteTest\SQLiteTest\Program.cs:第 14 行 System.AppDomain._nExecuteAssembly(RuntimeAssembly 程序集,字符串 [] 参数)在 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext、ContextCallback 回调、对象状态、布尔值 preserveSyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext、ContextCallback 回调、对象状态、布尔值 preserveSyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext、ContextCallback 回调、对象状态)在 System.Threading.ThreadHelper.ThreadStart()

【问题讨论】:

你考虑过localdb吗? blogs.msdn.com/b/sqlexpress/archive/2011/07/12/… @jenson-button-event:刚刚看了看,似乎 localdb 需要在客户端上安装。我目前正在研究 SQLite 的原因是因为我需要一个可以轻松地在机器之间移动的单个文件 我认为您需要在这里展示更多部分类的内部工作原理。显然,有一个明确的类型被表达,而不是 sqlconnection 的接口 @jimtollan:这不会有帮助。部分是纯粹的单个构造函数重载,它允许将连接字符串传递给基 DbContext 构造函数。默认情况下没有允许这样做的自动生成重载,这就是我创建部分的原因 @musefan 老实说,这里完全是个猜测,但我想整个应用程序域将与特定的数据库实现相关联,并且由于元数据而无法在进程中进行交换和更改EF 静态存储在 AppDomain 中的等。也许……如果不阅读大量源代码就无法确定! 【参考方案1】:

您可以使用Entity Framework Code First 轻松实现此目的。 Code First 方法不使用 .edmx 文件。您的实体(类)会自动映射到数据库表。

您可以在 app.config 文件中同时定义 connectionstrings 并配置它们的提供者。现在您可以通过将所需的connectionstring 传递给 EF 在 DB 之间切换。

/* connection string for SQLite */
<add name="SQLiteConnection" connectionString="Data Source=MyDBName.sqlite" providerName="System.Data.SQLite" />

/* connection string for SQL Server */
<add name="SQLServerConnection" connectionString="Data Source=MyDB; Integrated Security=True" providerName="System.Data.SqlClient" />

请参阅SQLite Code First 和EF 6 Code First 了解更多信息。

【讨论】:

感谢您的回答。我有两点需要澄清。第一点是谁可以项目有两个 2 应用程序文件,这对我来说是新的。第二点是在从数据库中插入或读取数据时,实体框架将如何决定使用哪个数据库(sqlite 或 sql server)。我认为如果您创建一个包含此类解决方案的简单 winfrom 应用程序会很有帮助 假设您想在开发环境中使用 SQLite,在测试环境中使用 SQL Server,那么您需要将 SQLite 配置放入 app.dev.config 并将 SQL Server 配置放入 app.test.config... 您需要了解app.con 文件... connectionstring 有一个ProviderName... 所以在上面的示例中,如果您将SQLiteConnection 传递给EF,它将知道它必须使用System.Data.SQLite Provider .【参考方案2】:

使用 sqlite dbcontext 为 dbcontext 对象创建另一个类并使用相同的模型类。简单的原因是默认实体与 sqlserver db 一起使用。要使用 sqlite,您必须使用 sqlite dbcontext。

【讨论】:

【参考方案3】:

您是在&lt;connectionStrings&gt; 应用设置部分中传递连接字符串还是连接字符串的名称?我相信问题正如你所描述的那样。它将提供程序默认为 System.Data.SqlClient。如果您想要 Sql Lite,您必须在 &lt;connectionString&gt; 上设置 providerName,然后将 &lt;connectionString&gt; 的名称(属性)发送到 DbContext(它知道如何自动查找)。然后它应该使用新的 providerName 属性而不是 SqlClient。

【讨论】:

以上是关于同时将实体框架与 SQL Server 和 SQLite 数据库一起使用的主要内容,如果未能解决你的问题,请参考以下文章

优化实体框架生成的 SQL Server 执行计划

实体框架代码优先与 SQL Server 同义词

实体框架和SQL Server Compact Edition DDEX提供程序

SQL Server 和实体框架性能改进

具有 SQL Server 连接的实体框架 6 尝试使用 MySqlClient 并崩溃

实体框架将 DateTimeOffset 映射到 SQL Server DateTime