我有一个 WPF 应用程序,其中包含一个采用数据库优先方法的 EF6.0 项目。用户应该能够以不同的名称在不同的位置打开不同的数据库。所以我的 ConnectionString 不是来自 App.config 像这里:
<connectionStrings>
<add name="SQLiteDatabaseEntities" connectionString="metadata=res://*/SQLiteModel.csdl|res://*/SQLiteModel.ssdl|res://*/SQLiteModel.msl;provider=System.Data.SQLite.EF6;provider connection string="data source=C:\SomePathThatMayChange\Database.sqlite;useutf16encoding=True;synchronous=Full"" providerName="System.Data.EntityClient" />
我宁愿自己在代码中创建它并将数据源设置到正确的位置:
if (false == File.Exists(fullPathAndFilename))
{
return string.Empty;
}
EntityConnectionStringBuilder entityConnectionStringBuilder = new EntityConnectionStringBuilder();
entityConnectionStringBuilder.Name = "SQLiteConnection";
entityConnectionStringBuilder.Provider = "System.Data.EntityClient";
entityConnectionStringBuilder.ConnectionString = "metadata=res://*/SQLiteModel.csdl|res://*/SQLiteModel.ssdl|res://*/SQLiteModel.msl;provider=System.Data.SQLite.EF6;provider connection string=';data source=" + fullPathAndFilename + "';";
return entityConnectionStringBuilder.ToString();
现在我通过添加其他项目并将 EF 事物放入仅包含数据库事物的单独项目(与主项目不同)来更改我的应用程序的结构。调试以下代码时,我遇到了异常
try
{
using (Entities context = new Entities(ConnectionString))
{
context.Database.Connection.Open();
using (System.Data.Entity.DbContextTransaction dbTran = context.Database.BeginTransaction())
{
try
{
// Do things:
// ...
return true;
}
catch
{
dbTran.Rollback();
}
}
}
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.Message);
}
异常消息是:指定的架构无效。错误:SQLiteModel.ssdl(2,2):错误 0152:没有为具有不变名称“System.Data.SQLite.EF6”的 ADO.NET 提供程序找到实体框架提供程序。确保提供程序已在应用程序配置文件的“entityFramework”部分注册。
我发现有些文件不在它们应该在的位置。即 SQLiteModel.csdl、SQLiteModel.msl 和 SQLiteModel.ssdl。它们位于 ...\obj\Debug\edmxResourcesToEmbed\ 目录中。我还发现在 *.ssdl 文件中还有一个在 ProviderManifestToken="data source= 下指定的数据源
<Schema Namespace="RohlingModel.Store" Provider="System.Data.SQLite.EF6" ProviderManifestToken="data source=C:\SomePathThatMayChange\Database.sqlite" Alias="Self" xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" xmlns:customannotation="http://schemas.microsoft.com/ado/2013/11/edm/customannotation" xmlns="http://schemas.microsoft.com/ado/2009/11/edm/ssdl">
我认为我的程序第一次只是意外地工作。现在我有一些问题: 1. 我真的必须嵌入这个包含数据源固定位置的 *ssdl 文件吗?2. 以后是否可以通过在 ConnectionString 中设置数据源来更改数据库位置?3. 在这种情况下,数据库优先方法是否可行?4. 在我的多项目解决方案中,我必须在哪里复制/嵌入这些文件?
提前致谢!
更新: 我发现我的问题很愚蠢。问题似乎是在我的主项目中 app.config 不包含所有需要的信息。由于我想将数据库内容与其他内容分开,因此在主项目的 app.config 中根本不应该有与 EF 相关的内容。
我通过编程 app.config 解决了这个问题。我的 app.config 看起来像这样:
<? xml version="1.0" encoding="utf-8"?> <configuration> <configSections> <section name = "entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" /> </configSections> <entityFramework> <defaultConnectionFactory type = "System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework" > < parameters > < parameter value="mssqllocaldb" /> </parameters> </defaultConnectionFactory> <providers> <provider invariantName = "System.Data.SQLite" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" /> <provider invariantName = "System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> <provider invariantName = "System.Data.SQLite.EF6" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" /> </providers> </entityFramework> <system.data> <DbProviderFactories> <remove invariant = "System.Data.SQLite.EF6" /> < remove invariant="System.Data.SQLite" /> <add name = "SQLite Data Provider (Entity Framework 6)" invariant="System.Data.SQLite.EF6" description=".NET Framework Data Provider for SQLite (Entity Framework 6)" type="System.Data.SQLite.EF6.SQLiteProviderFactory, System.Data.SQLite.EF6" /> <add name = "SQLite Data Provider" invariant="System.Data.SQLite" description=".NET Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite, Version=1.0.104.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139" /> </DbProviderFactories> </system.data> <connectionStrings> <add name = "SQLiteDatabaseEntities" connectionString="metadata=res://*/SQLiteModel.csdl|res://*/SQLiteModel.ssdl|res://*/SQLiteModel.msl;provider=System.Data.SQLite.EF6;provider connection string="data source=C:\SomePath\SQLiteDatabase.Template;useutf16encoding=True;synchronous=Full"" providerName="System.Data.EntityClient" /> <add name = "Entities" connectionString="metadata=res://*/SQLiteModel.csdl|res://*/SQLiteModel.ssdl|res://*/SQLiteModel.msl;provider=System.Data.SQLite.EF6;provider connection string="data source=C:\SomePath\SQLiteDatabase.Template;useutf16encoding=True;synchronous=Full"" providerName="System.Data.EntityClient" /> </connectionStrings> </configuration>
我创建了一个类 MySQLiteConfiguration,如下所示:
public class MySQLiteConfiguration : DbConfiguration
{
public MySQLiteConfiguration()
{
SetDefaultConnectionFactory(new System.Data.Entity.Infrastructure.LocalDbConnectionFactory("mssqllocaldb"));
SetProviderFactory("System.Data.SQLite.EF6", SQLiteProviderFactory.Instance);
// this provider is part of the App.config. However, it seems to be needed only for updating the model from the database in this "database first approach" rather than reading from or writing to the database.
//SetProviderServices("System.Data.SqlClient", System.Data.Entity.SqlServer.SqlProviderServices.Instance);
DbProviderServices sqLiteProviderServices = (DbProviderServices)SQLiteProviderFactory.Instance.GetService(typeof(DbProviderServices));
SetProviderServices("System.Data.SQLite", sqLiteProviderServices);
SetProviderServices("System.Data.SQLite.EF6", sqLiteProviderServices);
// These providers have to be specified in detail. Since they are there already they must be exchanged:
List<Provider> providersToExchange = new List<Provider>();
providersToExchange.Add(new Provider("SQLite Data Provider (Entity Framework 6)", "System.Data.SQLite.EF6", ".NET Framework Data Provider for SQLite (Entity Framework 6)", "System.Data.SQLite.EF6.SQLiteProviderFactory, System.Data.SQLite.EF6"));
providersToExchange.Add(new Provider("SQLite Data Provider", "System.Data.SQLite", ".NET Framework Data Provider for SQLite", "System.Data.SQLite.SQLiteFactory, System.Data.SQLite, Version=1.0.104.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139"));
try
{
System.Data.DataRowCollection rows = (System.Configuration.ConfigurationManager.GetSection("system.data") as System.Data.DataSet).Tables?[0]?.Rows;
if (rows != null)
{
foreach (Provider provider in providersToExchange)
{
for (int i = 0; i < rows.Count; i++)
{
if (rows[i]["InvariantName"] as string == provider.InvariantName)
{
rows.RemoveAt(i);
break;
}
}
}
foreach (Provider provider in providersToExchange)
{
rows.Add(provider.InvariantName, provider.Description, provider.Name, provider.Type);
}
}
}
catch (System.Data.ConstraintException constraintException)
{
System.Diagnostics.Debug.Assert(false, constraintException.Message);
}
}
private class Provider
{
internal string Name { get; set; }
internal string InvariantName { get; set; }
internal string Description { get; set; }
internal string Type { get; set; }
internal Provider(string name, string invariantName, string description, string type)
{
Name = name;
InvariantName = invariantName;
Description = description;
Type = type;
}
}
}
这个类用于配置上下文:
[DbConfigurationType(typeof(MySQLiteConfiguration))]
public partial class Entities : DbContext
{
public Entities(string connectionString)
: base(connectionString)
{ }
}
在使用上下文时,我使用传递我自己的连接字符串的构造函数:
try
{
using (Entities context = new Entities(ConnectionString))
{
// Do things...
// ...
}
}
catch { }
我很抱歉我的愚蠢问题。我希望帮助一些在使用 Entity Framework 6.0 和 SQLite 迈出第一步时遇到困难的人。
问候