EF 5 + SQL Server CE 4:如何为数据库文件指定自定义位置?
我正在开发一个需要一个小型本地数据库的客户端系统。 我想避免安装SQL Server Express,并决定使用SQL Server 4。
我使用Entity Framework 5进行数据访问,并创建了自定义上下文。 在开发中一切正常,我可以使用app.config来设置特定的文件位置或动态Data Source=|DataDirectory|\MyDatabase.sdf
。
但在部署时,我希望数据库位于users documents文件夹中:
\My Documents\ApplicationName\MyDatabase.sdf
我怎样才能做到这一点?
我只需要能够在代码中设置自定义连接字符串!
这是我到目前为止所尝试的:
private MyApplicationDataContext(string connectionString) : base(connectionString) { } public static MyApplicationDataContext CreateInstance() { var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); var path = Path.Combine(directory, @"ApplicationName\MyDatabase.sdf"); //var connectionString = string.Format("provider=System.Data.SqlServerCe.4.0;provider connection string=\"Data Source={0}\"", path); var connectionString = string.Format("Data Source={0}", path); return new MyApplicationDataContext(connectionString); }
正如您所看到的,我尝试了两种连接字符串,但都导致exception。
不支持关键字:’provider’。
和
提供程序未返回ProviderManifestToken字符串。
啊,我终于做对了!
如果其他人有同样的问题,我会包含调整后的代码。 诀窍是在Database.DefaultConnectionFactory上设置连接字符串
private MyApplicationDataContext() { } public static MyApplicationDataContext CreateInstance() { var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); var path = Path.Combine(directory, @"ApplicationName\MyDatabase.sdf"); // Set connection string var connectionString = string.Format("Data Source={0}", path); Database.DefaultConnectionFactory = new SqlCeConnectionFactory("System.Data.SqlServerCe.4.0", "", connectionString); return new MyApplicationDataContext(); }
在EF 6中,可以使用DbConfiguration完成:
App.config中:
在程序集内部创建一个类,如:
public class MyDbConfiguration : DbConfiguration { public MyDbConfiguration() { SetProviderServices(SqlCeProviderServices.ProviderInvariantName, SqlCeProviderServices.Instance); var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); var path = Path.Combine(directory, @"ApplicationName\MyDatabase.sdf"); var connectionString = string.Format(@"Data Source={0}",path); SetDefaultConnectionFactory(new SqlCeConnectionFactory(SqlCeProviderServices.ProviderInvariantName, "", connectionString)); } }
SqlCeConnection
实例用于连接到Sql Ce
数据库文件。 如果我想连接到MSSQL
数据库,我将使用SqlConnectionStringBuilder
和SqlConnection
实例来构建我的DbConnection
实例。
// The ctor for `DbConnection`. private MyApplicationDataContext(DbConnection conn) : base(conn, true) {} public static MyApplicationDataContext CreateInstance() { var directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); var path = Path.Combine(directory, @"ApplicationName\MyDatabase.sdf"); // Connection string builder for `Sql ce` SqlCeConnectionStringBuilder sb = new SqlCeConnectionStringBuilder(); sb.DataSource = path; // DbConnection for `Sql ce` SqlCeConnection dbConn = new SqlCeConnection(sb.ToString()); return new MyApplicationDataContext(dbConn); }
对于EF 6
由于Matthias的post,我得到了这个答案,但是包含了一些来自Entity Framework Code-Based Configuration(EF6以上版本)的有价值的信息。
要指定自定义数据库位置,您需要进行一些配置。 可以在配置文件(app.config / web.config)中或通过代码指定entity framework应用程序的配置。 后者称为基于代码的配置。 鉴于我的项目需要动态设置数据库位置,我使用了基于代码的配置,如下所述。
(请注意,配置文件优先于基于代码的配置。换句话说,如果在代码和配置文件中都设置了配置选项,则使用配置文件中的设置。)
根据EF文档(上面的链接),有4种方法可以实现自定义配置,包括:使用DbConfiguration,移动DbConfiguration,显式设置DbConfiguration和覆盖DbConfiguration。
使用DbConfiguration
EF6及更高版本中基于代码的配置是通过创建System.Data.Entity.Config.DbConfiguration的子类来实现的。 在对DbConfiguration进行子类化时,应遵循以下准则:
-
仅为您的应用程序创建一个DbConfiguration类。 此类指定应用程序域范围的设置。
-
将DbConfiguration类放在与DbContext类相同的程序集中。 (如果要更改此选项,请参阅Moving DbConfiguration部分。)
-
为DbConfiguration类提供一个公共无参数构造函数。
-
通过从此构造函数中调用受保护的DbConfiguration方法来设置配置选项。
遵循这些准则允许EF通过需要访问模型的工具和运行应用程序的时间自动发现和使用您的配置。
示例(根据Matthias的回答修改):
public class MyDbConfiguration : DbConfiguration { public MyDbConfiguration() { SetProviderServices(SqlCeProviderServices.ProviderInvariantName, SqlCeProviderServices.Instance); //var directory Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); var directory = @"C:\Users\Evan\Desktop\TestFolder"; // Directory may or may not already exist Directory.CreateDirectory(directory); // create directory if not exists var path = Path.Combine(directory, @"ApplicationName\MyDatabase.sdf"); var connectionString = string.Format(@"Data Source={0}",path); SetDefaultConnectionFactory(new SqlCeConnectionFactory(SqlCeProviderServices.ProviderInvariantName, "", connectionString)); } }
请注意,除非存在覆盖自定义配置的现有配置设置,否则不需要更改配置文件。 此外,我更改了目录以说明虽然EF能够创建新数据库(如果它尚不存在),但它不会创建父目录,这就是我包含此行的原因:Directory.CreateDirectory(目录)。 鉴于这种方法适用于我的项目,我没有探索剩余的3种配置方法,但您可以在上面提供的链接中找到有关它们的信息,我将在此处包含文档作为参考,以防链接中断。
移动DbConfiguration
在某些情况下,无法将DbConfiguration类放在与DbContext类相同的程序集中。 例如,您可能在不同的程序集中有两个DbContext类。 处理此问题有两种选择。
第一个选项是使用配置文件指定要使用的DbConfiguration实例。 为此,请设置entityFramework部分的codeConfigurationType属性。 例如:
...Your EF config...
codeConfigurationType的值必须是DbConfiguration类的程序集和名称空间限定名称。
第二个选项是将DbConfigurationTypeAttribute放在上下文类中。 例如:
[DbConfigurationType(typeof(MyDbConfiguration))] public class MyContextContext : DbContext { }
传递给属性的值可以是DbConfiguration类型(如上所示),也可以是程序集和名称空间限定类型名称字符串。 例如:
[DbConfigurationType("MyNamespace.MyDbConfiguration, MyAssembly")] public class MyContextContext : DbContext { }
明确设置DbConfiguration
在某些情况下,在使用任何DbContext类型之前可能需要进行配置。 这方面的例子包括:
- 使用DbModelBuilder构建没有上下文的模型
- 使用一些其他框架/实用程序代码,这些代码利用DbContext,在使用应用程序上下文之前使用该上下文
在这种情况下,EF无法自动发现配置,您必须执行以下操作之一:
- 在配置文件中设置DbConfiguration类型,如上面的Moving DbConfiguration部分所述
- 在应用程序启动期间调用静态DbConfiguration.SetConfiguration方法
覆盖DbConfiguration
在某些情况下,您需要覆盖DbConfiguration中的配置集。 这通常不是由应用程序开发人员完成的,而是由不能使用派生的DbConfiguration类的第三方提供者和插件完成的。
为此,EntityFramework允许注册事件处理程序,可以在锁定之前修改现有配置。 它还提供了一种专门用于替换EF服务定位器返回的任何服务的糖方法。 这是它的用途:
- 在app启动时(使用EF之前),插件或提供程序应该为此事件注册事件处理程序方法。 (请注意,这必须在应用程序使用EF之前发生。)
- 事件处理程序为每个需要替换的服务调用ReplaceService。
例如,要重新生成IDbConnectionFactory和DbProviderService,您将注册一个类似于下面的处理程序:
DbConfiguration.Loaded += (_, a) => { a.ReplaceService((s, k) => new MyProviderServices(s)); a.ReplaceService((s, k) => new MyConnectionFactory(s)); };
在上面的代码中,MyProviderServices和MyConnectionFactory表示您的服务实现。
您还可以添加其他依赖关系处理程序以获得相同的效果。
请注意,您也可以以这种方式包装DbProviderFactory,但这样做只会影响EF而不会使用EF之外的DbProviderFactory。 因此,您可能希望继续像以前一样包装DbProviderFactory。
您还应该记住在应用程序外部运行的服务 – 例如,从Package Manager控制台运行迁移。 从控制台运行迁移时,它将尝试查找您的DbConfiguration。 但是,它是否将获得包装服务取决于它注册的事件处理程序的位置。 如果它被注册为构建DbConfiguration的一部分,那么代码应该执行并且服务应该被包装。 通常情况并非如此,这意味着工具不会获得包装服务。