EF 5 + SQL Server CE 4:如何为数据库文件指定自定义位置?

Posted

技术标签:

【中文标题】EF 5 + SQL Server CE 4:如何为数据库文件指定自定义位置?【英文标题】:EF 5 + SQL Server CE 4: How to specify custom location for database file? 【发布时间】:2012-09-19 07:24:12 【问题描述】:

我正在开发一个需要小型本地数据库的客户端系统。 我想避免安装 SQL Server Express,并决定使用 SQL Server 4。

我使用 Entity Framework 5 进行数据访问并创建了我的自定义上下文。 在开发中一切正常,我可以使用 app.config 设置特定文件位置或动态Data Source=|DataDirectory|\MyDatabase.sdf

但在部署时,我希望数据库位于用户文档文件夹中:

\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);

如您所见,我尝试了两种连接字符串,但都导致异常。

不支持关键字:'provider'。

提供者未返回 ProviderManifestToken 字符串。

【问题讨论】:

【参考方案1】:

啊,我终于猜对了!

如果其他人有同样的问题,我会包含调整后的代码。 诀窍是在 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();
    

【讨论】:

【参考方案2】:

在 EF 6 中,这可以通过 DbConfiguration 完成:

App.config:

<entityFramework codeConfigurationType="MyDbConfiguration, MyAssembly">
</entityFramework>

在你的程序集中创建一个类,如:

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));
    
  

【讨论】:

【参考方案3】:

SqlCeConnection 实例用于连接到Sql Ce 数据库文件。如果我想连接到MSSQL 数据库,我将使用SqlConnectionStringBuilderSqlConnection 实例来构建我的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);

【讨论】:

【参考方案4】:

对于 EF 6

感谢 Matthias 的 帖子,我得到了这个答案,但包含了来自 Entity Framework Code-Based Configuration (EF6 onwards) 的一些有价值的信息。

要指定自定义数据库位置,您需要进行一些配置。可以在配置文件 (app.config/web.config) 中或通过代码指定实体框架应用程序的配置。后者称为基于代码的配置。鉴于我的项目需要动态设置数据库位置,我采用了基于代码的配置,如下所述。

(请注意,配置文件优先于基于代码的配置。换句话说,如果在代码和配置文件中都设置了配置选项,则使用配置文件中的设置。)

根据 EF 文档(以上链接),有 4 种方法可以实现自定义配置,包括:使用 DbConfiguration、移动 DbConfiguration、显式设置 DbConfiguration 和覆盖 DbConfiguration。

使用 DbConfiguration

EF6 及更高版本中基于代码的配置是通过创建 System.Data.Entity.Config.DbConfiguration 的子类来实现的。子类化 DbConfiguration 时应遵循以下准则:

只为您的应用程序创建一个 DbConfiguration 类。此类指定应用程序域范围的设置。

将 DbConfiguration 类放置在与 DbContext 类相同的程序集中。 (如果要更改此设置,请参阅移动 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(directory)。鉴于这种方法适用于我的项目,我没有探索其余 3 种配置方法,但您可以在上面提供的链接中找到有关它们的信息,我将在此处包含文档作为参考,以防链接中断。

移动 DbConfiguration

在某些情况下,无法将 DbConfiguration 类与 DbContext 类放在同一个程序集中。例如,您可能在不同的程序集中有两个 DbContext 类。有两种处理方法。

第一个选项是使用配置文件来指定要使用的 DbConfiguration 实例。为此,请设置 entityFramework 部分的 codeConfigurationType 属性。例如:

<entityFramework codeConfigurationType="MyNamespace.MyDbConfiguration, MyAssembly"> 
    ...Your EF config... 
</entityFramework>

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 类型,如 将 DbConfiguration 部分移到上面 期间调用静态 DbConfiguration.SetConfiguration 方法 应用启动

覆盖 DbConfiguration

在某些情况下,您需要覆盖 DbConfiguration 中的配置集。这通常不是由应用程序开发人员完成,而是由无法使用派生 DbConfiguration 类的第三方提供商和插件完成。

为此,EntityFramework 允许注册一个事件处理程序,该处理程序可以在现有配置被锁定之前对其进行修改。它还提供了一种糖方法,专门用于替换 EF 服务定位器返回的任何服务。这就是它的用途:

在应用程序启动时(使用 EF 之前)插件或提供程序应 为此事件注册事件处理程序方法。 (请注意,这 必须在应用程序使用 EF 之前发生。) 事件处理程序为每个服务调用 ReplaceService 需要更换。

例如,要替换 IDbConnectionFactory 和 DbProviderService,您可以注册一个类似这样的处理程序:

DbConfiguration.Loaded += (_, a) => 
    
       a.ReplaceService<DbProviderServices>((s, k) => new MyProviderServices(s)); 
       a.ReplaceService<IDbConnectionFactory>((s, k) => new MyConnectionFactory(s)); 
   ;

在上面的代码中,MyProviderServices 和 MyConnectionFactory 代表你的服务实现。

您还可以添加额外的依赖处理程序以获得相同的效果。

请注意,您也可以以这种方式包装 DbProviderFactory,但这样做只会影响 EF,而不会影响 EF 之外的 DbProviderFactory 的使用。出于这个原因,您可能希望像以前一样继续包装 DbProviderFactory。

您还应该牢记在应用程序外部运行的服务 - 例如。从包管理器控制台运行迁移。当您从控制台运行迁移时,它将尝试查找您的 DbConfiguration。但是,它是否会获得包装的服务取决于它注册的事件处理程序的位置。如果它被注册为 DbConfiguration 构造的一部分,那么代码应该执行并且服务应该被包装。通常情况并非如此,这意味着工具不会获得包装好的服务。

【讨论】:

我想知道为什么“SqlCeConnectionFactory(...)”中的第二个参数是一个空字符串,而参数的名称是“databaseDirectory”?我们可以只将字符串传递给该参数而不是构建连接字符串吗?

以上是关于EF 5 + SQL Server CE 4:如何为数据库文件指定自定义位置?的主要内容,如果未能解决你的问题,请参考以下文章

错误 175:在配置中找不到指定的存储提供程序,或者对 EF4 和 SQL Server CE 4 无效

EF4 Code First + SQL Server CE:以原子方式保存双向引用

使用 EF6 SQL Server CE Code First 创建数据库崩溃

ASP.NET EF5 代码优先不创建 SQL Server CE 数据库

Visual Studio 的 SQL Server CE 4.0 数据提供程序在哪里?

如何让实体数据模型与 SQL Server CE 4.0 一起使用?