7

我正在开发一个需要小型本地数据库的客户端系统。我想避免安装 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);
}

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

不支持关键字:“提供者”。

提供程序未返回 ProviderManifestToken 字符串。

4

4 回答 4

10

啊,我终于猜对了!

如果其他人有同样的问题,我会包含调整后的代码。诀窍是在 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();
    }
于 2012-09-19T08:46:37.717 回答
3

在 EF 6 中,这可以使用 DbConfiguration 来完成:

应用程序配置:

<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));
    }
}  
于 2013-12-29T22:54:50.907 回答
0

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);
}
于 2014-08-26T06:18:13.267 回答
0

For EF 6

I arrived at this answer thanks to Matthias's post, but included some worthwhile info from Entity Framework Code-Based Configuration (EF6 onwards).

To specify a custom database location you will need to do some configuration. Configuration for an Entity Framework application can be specified in a config file (app.config/web.config) or through code. The latter is known as code-based configuration. Given that my project required the database location to be set dynamically, I went with the code-based configuration, which is described below.

(Note that the config file takes precedence over code-based configuration. In other words, if a configuration option is set in both code and in the config file, then the setting in the config file is used.)

According to EF documentation (above link) there are 4 approaches to implement your custom configuration,which include: Using DbConfiguration, Moving DbConfiguration, Setting DbConfiguration explicitly, and Overriding DbConfiguration.

Using DbConfiguration

Code-based configuration in EF6 and above is achieved by creating a subclass of System.Data.Entity.Config.DbConfiguration. The following guidelines should be followed when subclassing DbConfiguration:

  • Create only one DbConfiguration class for your application. This class specifies app-domain wide settings.

  • Place your DbConfiguration class in the same assembly as your DbContext class. (See the Moving DbConfiguration section if you want to change this.)

  • Give your DbConfiguration class a public parameterless constructor.

  • Set configuration options by calling protected DbConfiguration methods from within this constructor.

Following these guidelines allows EF to discover and use your configuration automatically by both tooling that needs to access your model and when your application is run.

Example (modified from Matthias's answer):

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

Note that the config file does not need to be altered unless there are existing configuration settings that override your custom configuration. Also, I changed the directory to illustrate that While EF is capable of creating a new database if it doesn't already exist, it will not create parent directories, which is why I included this line: Directory.CreateDirectory(directory). Given that this approach worked for my project I didn't explore the remaining 3 configuration methods, but you can find info on them in the link provided above and I will include the documentation here as a reference in case the link breaks.

Moving DbConfiguration

There are cases where it is not possible to place your DbConfiguration class in the same assembly as your DbContext class. For example, you may have two DbContext classes each in different assemblies. There are two options for handling this.

The first option is to use the config file to specify the DbConfiguration instance to use. To do this, set the codeConfigurationType attribute of the entityFramework section. For example:

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

The value of codeConfigurationType must be the assembly and namespace qualified name of your DbConfiguration class.

The second option is to place DbConfigurationTypeAttribute on your context class. For example:

[DbConfigurationType(typeof(MyDbConfiguration))] 
public class MyContextContext : DbContext 
{ 
}

The value passed to the attribute can either be your DbConfiguration type - as shown above - or the assembly and namespace qualified type name string. For example:

[DbConfigurationType("MyNamespace.MyDbConfiguration, MyAssembly")] 
public class MyContextContext : DbContext 
{ 
}

Setting DbConfiguration explicitly

There are some situations where configuration may be needed before any DbContext type has been used. Examples of this include:

  • Using DbModelBuilder to build a model without a context
  • Using some other framework/utility code that utilizes a DbContext where that context is used before your application context is used

In such situations EF is unable to discover the configuration automatically and you must instead do one of the following:

  • Set the DbConfiguration type in the config file, as described in the Moving DbConfiguration section above
  • Call the static DbConfiguration.SetConfiguration method during application startup

Overriding DbConfiguration

There are some situations where you need to override the configuration set in the DbConfiguration. This is not typically done by application developers but rather by thrid party providers and plug-ins that cannot use a derived DbConfiguration class.

For this, EntityFramework allows an event handler to be registered that can modify existing configuration just before it is locked down. It also provides a sugar method specifically for replacing any service returned by the EF service locator. This is how it is intended to be used:

  • At app startup (before EF is used) the plug-in or provider should register the event handler method for this event. (Note that this must happen before the application uses EF.)
  • The event handler makes a call to ReplaceService for every service that needs to be replaced.

For example, to repalce IDbConnectionFactory and DbProviderService you would register a handler something like this:

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

In the code above MyProviderServices and MyConnectionFactory represent your implementations of the service.

You can also add additional dependency handlers to get the same effect.

Note that you could also wrap DbProviderFactory in this way, but doing so will only effect EF and not uses of the DbProviderFactory outside of EF. For this reason you’ll probably want to continue to wrap DbProviderFactory as you have before.

You should also keep in mind the services that you run externally to your application - e.g. running migrations from Package Manager console. When you run migrate from the console it will attempt to find your DbConfiguration. However, whether or not it will get the wrapped service depends on where the event handler it registered. If it is registered as part of the construction of your DbConfiguration then the code should execute and the service should get wrapped. Usually this won’t be the case and this means that tooling won’t get the wrapped service.

于 2016-11-04T17:26:09.333 回答