【问题标题】:Create sql server compact file in appdata folder在 appdata 文件夹中创建 sql server 压缩文件
【发布时间】:2011-10-03 19:25:19
【问题描述】:

我正在开发一个简单的软件,它首先使用实体​​框架代码和 sql server compact 4。目前此设置有效。如果 sql server 压缩文件不存在,实体框架会创建它。数据库的路径是从存储在 app.config 文件中的连接字符串中定义的。它是这样构建的:

<connectionStrings>
  <add name="DataContext" 
       connectionString="Data source=Database.sdf;"
       providerName="System.Data.SqlServerCe.4.0"/>
</connectionStrings>

但是,我想将数据库放在当前用户的 Application Data 文件夹(我的 win7 机器上的 C:\Users\User\AppData\Roaming 文件夹)内的文件夹中。我尝试将连接字符串的数据源设置为 %APPDATA%\Database.sdf 之类的,但这不起作用,我收到“路径中的非法字符”异常。

我想坚持使用 connectionstring 方法,因为我想为我的单元测试使用与实际应用程序不同的数据库。这样,通过将 app.config 文件放在项目的根目录中,就可以轻松修改数据库。

有人可以引导我走向正确的方向吗?

【问题讨论】:

  • 但是这样我必须在代码中构建连接字符串。这不会像我现在那样轻松切换数据库的位置。我的常规项目和测试项目都有一个单独的 app.config 文件,它们都指向不同的数据库。

标签: c# entity-framework-4.1 sql-server-ce


【解决方案1】:

在下面使用:

AppDomain.CurrentDomain.SetData("DataDirectory", Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData));

<connectionStrings>
  <add name="DataContext" 
       connectionString="Data source=|DataDirectory|Database.sdf;"
       providerName="System.Data.SqlServerCe.4.0"/>
</connectionStrings>

【讨论】:

  • 谢谢,这行得通。实际应用是一个wpf应用,我把AppDomain这一行放在了App.xaml.cs启动方法中。
  • 您需要在“DataDirectory”(|DataDirectory|)之前添加另一个管道。只是认为这可能会对某人有所帮助,因为我被困了一段时间! msdn.microsoft.com/en-us/library/cc716756(v=vs.100).aspx
【解决方案2】:
    connectionString="Data source=Database.sdf;"

这会告诉您的应用在应用的当前工作目录中查找 Database.sdf;它可能在任何地方,并且可能不可写。您需要查看您指定的位置:

    connectionString="Data source=|DataDirectory|Database.sdf;"

ADO.NET 在连接字符串中查找管道字符,并将它们扩展为应用程序域中该名称的属性值。那么DataDirectory 属性的值是多少?它应该由您部署的应用程序设置:

  • .MSI 安装程序将其设置为应用程序安装文件夹。如果您允许用户选择安装文件夹,他们也会选择 DataDirectory。这就是为什么您应该始终使用 |DataDirectory| 而永远不要使用硬编码路径的原因。
  • ClickOnce 在您的项目中定义了一个特殊的数据文件夹。
  • Web 应用程序使用 App_Data 文件夹。
  • Visual Studio 调试器使用调试文件夹。

项目中任何具有“复制到输出目录”属性的 Visual Studio 文件都将被复制到 DataDirectory。在大多数情况下,DataDirectory 将是一个只读文件夹。如果您的数据是只读的,这很好,但如果您想写入它,则必须将数据复制到可写位置。可能最好的地方是Environment.GetFolderPath( Environment.SpecialFolder.ApplicationData))。有几种方法可以做到这一点:

  • 如果您要创建一个空的新数据文件,只需使用您的 API 标准 CREATE DATABASEnew SqlCeConnection() 或其他。
  • 如果您想从预先填充的种子或起始数据库开始,请将种子数据库包含在您的项目中。在您的应用程序启动中,检查数据库是否存在于SpecialFolder.ApplicationData 文件夹中,如果不存在,请将其复制到那里。

如果您在网上搜索有关创建本地数据库的示例代码,您会遇到很多糟糕的建议。请勿执行以下操作:

new SqlCeConnection(@"Data source=c:\users\me\myApp\Database.sdf;");  // Do NOT do this!

我希望我不必解释为什么硬编码数据路径是错误的;但请注意,除非您在连接字符串中指定完整路径,否则该路径是相对于您当前的工作目录的。

using (var conn = new SqlCeConnection(@"Data source=|DataDirectory|Database.sdf;"))
{
    conn.Open();
    // No No No! This throws an Access Exception for Standard users,
    // and gets deleted when you repair the app!
    var cmd = conn.CreateCommand("INSERT INTO Table (column1, column2) VALUES (@p1, @p2)");
    ...
}

不要尝试修改DataDirectory 中的数据。用户不仅不能始终修改此目录,而且它归安装程序所有,而不归用户所有。修复或卸载应用程序将删除所有用户数据;用户不喜欢这样。而是将安装的数据复制到用户可写的文件夹中,并对副本进行所有更改。

AppDomain.CurrentDomain.SetData("DataDirectory",
    // Wrong, this overwrites where the user installed your app!
    Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData));

不要在代码中更改DataDirectory 的值,它是由安装程序设置的,如果您更改它,您将不知道您的数据安装在哪里。如果您要创建一个空数据库,只需在您的最终位置打开它。如果要制作副本,请打开已安装的数据库,将其保存到用户的位置,关闭已安装的数据库,然后打开副本。

我也不鼓励将数据保存到 Environment.SpecialFolder.CommonApplicationData。这可能不是用户可写的,除非有非常好的理由必须允许所有用户更改其他用户的数据,否则每个用户都应该有自己的数据库。

【讨论】: