如何在控制台应用程序中为 ConnectionString 定义数据目录以使用 EntityFramework Code First 迁移

How to define DataDirectory for ConnectionString in console application to work with EntityFramework Code First Migrations

我尝试为 App.config 中的数据库设置位置 MyProject\App_Data\Cos.mdf:

 <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\Cos.mdf;Initial Catalog=Cos;Integrated Security=True;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
  </connectionStrings>

Program.cs中我写道:

 static void Main(string[] args) {

        string relative = @"..\..\App_Data\Cos.mdf";
        string absolute = Path.GetFullPath(relative);

        AppDomain.CurrentDomain.SetData("DataDirectory", absolute); 
        Console.WriteLine(absolute);
        Console.ReadKey();
 }

显示的路径是(我贴出来证明我没弄错):

但是当我在程序包管理器控制台中输入时 enable-migrationsAutomaticMigrations 更改为 true,然后输入 update-database 我得到错误:

Cannot attach the file 'C:\Users\s8359_000\Documents\Visual Studio 2013\Projects\Projekt5 — kopia\Projekt5\bin\Debug\Cos.mdf' as database 'Cos'.

为什么 .NET 试图在 Debug 目录中创建我的数据库?!我在 Whosebug 上浏览了 15 个关于这个主题的主题,看起来每个人都只是重复了不起作用的答案。

在回答 SRUTZKY 后编辑 是的,你是对的,有错误。在你回答后我尝试了更多的组合,不幸的是 none 成功了。

  <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\baza.mdf;Initial Catalog=baza;Integrated Security=True;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
  </connectionStrings>

和主要

  static void Main(string[] args) {

        Console.WriteLine("BEFORE:" + AppDomain.CurrentDomain.GetData("DataDirectory"));
        string relative = @"..\..\App_Data\Cos.mdf";
        string absolute = Path.GetFullPath(relative);
        absolute = Path.GetDirectoryName(@absolute);
        AppDomain.CurrentDomain.SetData("DataDirectory", @absolute);
        Console.WriteLine(@absolute);
        Console.WriteLine(AppDomain.CurrentDomain.GetData("DataDirectory"));
        Console.ReadKey();
}

然后我进入控制台:

删除 Migrations 目录和 enable-migrations 后,自动迁移到 true,update-database 我得到:

PM> update-database Specify the '-Verbose' flag to view the SQL statements being applied to the target database. System.Data.SqlClient.SqlException (0x80131904): A file activation error occurred. The physical file name '\baza.mdf' may be incorrect. Diagnose and correct additional errors, and retry the operation. CREATE DATABASE failed. Some file names listed could not be created. Check related errors. at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction) at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async, Int32 timeout, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.ExecuteNonQuery() at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.<NonQuery>b__0(DbCommand t, DbCommandInterceptionContext1 c) at System.Data.Entity.Infrastructure.Interception.InternalDispatcher1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func3 operation, TInterceptionContext interceptionContext, Action3 executing, Action3 executed) at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.NonQuery(DbCommand command, DbCommandInterceptionContext interceptionContext) at System.Data.Entity.SqlServer.SqlProviderServices.<>c__DisplayClass1a.b__19(DbConnection conn) at System.Data.Entity.SqlServer.SqlProviderServices.<>c__DisplayClass33.b__32() at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.<>c__DisplayClass1.b__0() at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func1 operation) at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute(Action operation) at System.Data.Entity.SqlServer.SqlProviderServices.UsingConnection(DbConnection sqlConnection, Action1 act) at System.Data.Entity.SqlServer.SqlProviderServices.UsingMasterConnection(DbConnection sqlConnection, Action1 act) at System.Data.Entity.SqlServer.SqlProviderServices.CreateDatabaseFromScript(Nullable1 commandTimeout, DbConnection sqlConnection, String createDatabaseScript) at System.Data.Entity.SqlServer.SqlProviderServices.DbCreateDatabase(DbConnection connection, Nullable1 commandTimeout, StoreItemCollection storeItemCollection) at System.Data.Entity.Core.Common.DbProviderServices.CreateDatabase(DbConnection connection, Nullable1 commandTimeout, StoreItemCollection storeItemCollection) at System.Data.Entity.Core.Objects.ObjectContext.CreateDatabase() at System.Data.Entity.Migrations.Utilities.DatabaseCreator.Create(DbConnection connection) at System.Data.Entity.Migrations.DbMigrator.EnsureDatabaseExists(Action mustSucceedToKeepDatabase) at System.Data.Entity.Migrations.Infrastructure.MigratorBase.EnsureDatabaseExists(Action mustSucceedToKeepDatabase) at System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration) at System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(String targetMigration) at System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.Run() at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate) at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
at System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner) at System.Data.Entity.Migrations.Design.ToolingFacade.Update(String targetMigration, Boolean force) at System.Data.Entity.Migrations.UpdateDatabaseCommand.<>c__DisplayClass2.<.ctor>b__0() at System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command) ClientConnectionId:23ca49c1-4797-4bc3-8f16-f34fd77f2cbe A file activation error occurred. The physical file name '\baza.mdf' may be incorrect. Diagnose and correct additional errors, and retry the operation. CREATE DATABASE failed. Some file names listed could not be created. Check related errors. PM>

问题 1(共 2 个)

当你设置DataDirectory的值时,它需要是一个目录,而不是一个文件。您正在传递 absolute 变量的值,即:

C:\Users\s8359_000\Documents\Visual Studio 2013\Projects\Projekt5 — kopia\Projekt5\App_Data\Cos.mdf

其中包含文件名。那是无效的。 DataDirectory 是替换值,因此指定:

AttachDbFilename=|DataDirectory|\Cos.mdf

在连接字符串中会运行变成:

C:\Users\s8359_000\Documents\Visual Studio 2013\Projects\Projekt5 — kopia\Projekt5\App_Data\Cos.mdf\Cos.mdf

该路径无效。因此,.NET 似乎发现 DataDirectory 的值无效且未使用它,因此在当前工作目录中启动。

使用 Path.GetDirectoryName(relative) 而不是 Path.GetFullPath(relative) 来设置 absolute 的值,它应该可以工作,因为它将 DataDirectory 的值设置为:

C:\Users\s8359_000\Documents\Visual Studio 2013\Projects\Projekt5 — kopia\Projekt5\App_Data

Connection Strings 的 MSDN 页面底部有一些额外的详细信息,在标题为 "Support for the |DataDirectory| Substitution String..."

的部分中

问题 2(共 2 个)

  1. "DataDirectory" 在 AppDomain 中设置。
  2. 控制台应用程序有自己的 AppDomains,它们在启动时创建并在退出时消失。
  3. 包管理器(您所在的位置 运行ning Update-Database)无权访问您设置 "DataDirectory" 值的控制台应用程序的 AppDomain。
  4. 您需要:
    1. 在程序包管理器中以编程方式设置 "DataDirectory",或
    2. 在控制台应用程序的上下文中以编程方式运行"update-database"

我不知道如何以编程方式与程序包管理器交互,但我设法弄清楚了如何以编程方式触发 "update-database" 进程。只需在设置 "DataDirectory" 的值后添加以下行:

Database.SetInitializer(new
    MigrateDatabaseToLatestVersion<YourDataContextName, Configuration>()
 );

您还需要至少一个(如果不是两个)using 语句:

  • using System.Data.Entity;
  • using ProjectName.Migrations; // namespace of Migrations\Configuration.cs

请注意,仅此一项并不会创建数据库。当您首次通过 DbContext 访问 数据库时,将发布任何未决更改。

示例:

using System.Data.Entity;
using Projekt5.Migrations;

....

string relative = @"..\..\App_Data\Cos.mdf";
string absolute = Path.GetDirectoryName(absolute);
AppDomain.CurrentDomain.SetData("DataDirectory", absolute);
Database.SetInitializer(new
    MigrateDatabaseToLatestVersion<Projekt5Context, Configuration>()
 );
// database not created yet

using (var db = new Projekt5Context())
{
  db.Things.Add(new Thing { Name = "OMG This works!" });
  db.SaveChanges();
} 
// database CREATED!

此外,您可能需要通过包管理器调用以下一次(它不会立即对数据库执行任何操作,因此不会访问连接字符串) :

Add-Migration InitialMigration

有关详细信息,请参阅 Code First Migrations 的 MSDN 页面。

一旦调用 SetInitializer 的这行代码与 MigrateDatabaseToLatestVersion 一起出现,它就会这样做:每次 运行s(这就是为什么在开始时这样做的原因控制台应用程序)它同步 "model"(现在已编译到程序集中)和数据库之间的任何更改,确保数据库具有最新版本。这假定任何新表都在 DbContext class 中表示。但是不需要额外的包管理器命令 运行.

您可以在 Configuration class 中为 Update-Database 设置 DataDirectory,它是由 Enable-Migrations 命令创建的:

internal sealed class Configuration : DbMigrationsConfiguration<DataContext>
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
        var dataDirPath = "<YourPath>";
        AppDomain.CurrentDomain.SetData("DataDirectory", dataDirPath);
    }
}

要将 DataDirectory 指向控制台项目和 EF 代码优先迁移下名为 'AppData' 的文件夹,您必须执行以下操作:

public static class AppData
{
    public static void Set()
    {
        // Set the |DataDirectory| path used in connection strings to point to the correct directory for console app and migrations
        var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
        string relative = @"..\..\App_Data\";
        string absolute = Path.GetFullPath(Path.Combine(baseDirectory, relative));
        AppDomain.CurrentDomain.SetData("DataDirectory", absolute);
    }
}

然后从控制台应用程序和数据库迁移的入口点调用AppData.Set()

1) 控制台应用程序的 Main-method 或 Startup-class:

class Program
{
    static void Main()
    {
         AppData.Set();
    }
}

2) EF 代码首次迁移

public class DatabaseContext : DbContext
{
    // Add this constructor to your database context
    public DatabaseContext() : base()
    {
        AppData.Set();
    }

    // DbSet's are defined here
}