如何在控制台应用程序中为连接字符串定义数据目录以使用实体框架代码优先迁移

时间:2015-01-01 11:52:47

标签: c# entity-framework app-config localdb datadirectory

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

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

显示的路径是(我将其粘贴以显示我没有犯错):

enter image description here

但是当我输入程序包管理器控制台enable-migrations时将AutomaticMigrations更改为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目录中创建我的数据库?!我在StackOverflow上浏览了15个关于这个主题的主题,看起来每个人都只是重复了那些不能工作的答案。

在SRUTZKY之后编辑 是的,你是对的有错误。在你的答案之后,我尝试了更多的组合,不幸的是没有用。

  <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();
}
然后我进入控制台:

enter image description here

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

  

PM&GT; update-database指定&#39; -Verbose&#39;用于查看SQL的标志   语句被应用于目标数据库。   System.Data.SqlClient.SqlException(0x80131904):文件激活   发生了错误。物理文件名&#39; \ baza.mdf&#39;可能不正确。   诊断并更正其他错误,然后重试该操作。   CREATE DATABASE失败。无法创建列出的某些文件名。   检查相关错误。在   System.Data.SqlClient.SqlConnection.OnError(SqlException异常,   布尔值breakConnection,Action 1 wrapCloseInAction) at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action 1 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,布尔&amp; dataReady)at   System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(字符串   methodName,Boolean async,Int32 timeout,Boolean asyncWrite)at   System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource 1 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, DbCommandInterceptionContext 1 c)at   System.Data.Entity.Infrastructure.Interception.InternalDispatcher 1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func 3操作,TInterceptionContext interceptionContext,   行动3 executing, Action 3已执行)   System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.NonQuery(的DbCommand   命令,DbCommandInterceptionContext interceptionContext)at   System.Data.Entity.SqlServer.SqlProviderServices&LT;&GT; c__DisplayClass1a.b__19(的DbConnection   conn)at   System.Data.Entity.SqlServer.SqlProviderServices&LT;&GT; c__DisplayClass33.b__32()   在   System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy&LT;&GT; c__DisplayClass1.b__0()   在   System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute [TResult](Func 1 operation) at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute(Action operation) at System.Data.Entity.SqlServer.SqlProviderServices.UsingConnection(DbConnection sqlConnection, Action 1 act)at at   System.Data.Entity.SqlServer.SqlProviderServices.UsingMasterConnection(的DbConnection   sqlConnection,Action 1 act) at System.Data.Entity.SqlServer.SqlProviderServices.CreateDatabaseFromScript(Nullable 1   commandTimeout,DbConnection sqlConnection,String   createDatabaseScript)at   System.Data.Entity.SqlServer.SqlProviderServices.DbCreateDatabase(的DbConnection   connection,Nullable 1 commandTimeout, StoreItemCollection storeItemCollection) at System.Data.Entity.Core.Common.DbProviderServices.CreateDatabase(DbConnection connection, Nullable 1 commandTimeout,StoreItemCollection   storeItemCollection)at   System.Data.Entity.Core.Objects.ObjectContext.CreateDatabase()at   System.Data.Entity.Migrations.Utilities.DatabaseCreator.Create(的DbConnection   连接)   System.Data.Entity.Migrations.DbMigrator.EnsureDatabaseExists(动作   mustSucceedToKeepDatabase)at   System.Data.Entity.Migrations.Infrastructure.MigratorBase.EnsureDatabaseExists(动作   mustSucceedToKeepDatabase)at   System.Data.Entity.Migrations.DbMigrator.Update(字符串   targetMigration)   System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(字符串   targetMigration)   System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.Run()   在System.AppDomain.DoCallBack(CrossAppDomainDelegate   callBackDelegate)at   System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
  在System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner)   跑者)   System.Data.Entity.Migrations.Design.ToolingFacade.Update(字符串   targetMigration,Boolean force)at   System.Data.Entity.Migrations.UpdateDatabaseCommand&LT;&GT; c__DisplayClass2&LT; .ctor&GT; b__0()   在   System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(动作   命令)ClientConnectionId:23ca49c1-4797-4bc3-8f16-f34fd77f2cbe A   发生文件激活错误。物理文件名&#39; \ baza.mdf&#39;可以   不对。诊断并更正其他错误,然后重试   操作。 CREATE DATABASE失败。列出的某些文件名不能   创建。检查相关错误。 PM&GT;

3 个答案:

答案 0 :(得分:8)

您可以在Enable-Migrations命令创建的Configuration类中为Update-Database设置DataDirectory:

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

答案 1 :(得分:7)

问题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页面底部有一些额外的详细信息,标题为“支持| DataDirectory |替换字符串......”

问题2(2)

  1. “DataDirectory”在AppDomain中设置。
  2. 控制台应用程序有自己的AppDomain,它们在启动时创建,在退出时离开。
  3. 程序包管理器(您运行Update-Database的地方)无法访问您正在设置“DataDirectory”值的控制台应用程序的AppDomain。
  4. 您需要:
    1. 在程序包管理器中以编程方式设置“DataDirectory”,或
    2. 以编程方式在控制台应用程序的上下文中运行“update-database”
  5. 我不知道如何以编程方式与Package Manager进行交互,但我确实设法弄清楚如何以编程方式触发“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一起使用,它就会这样做:每次运行时(这就是为什么这是在控制台应用程序开始时完成的)它会同步“模型”(现在编译到程序集中)和数据库之间的任何更改,确保数据库具有最新版本。这假设任何新表都在DbContext类中表示。但是不需要运行其他的Package Manager命令。

答案 2 :(得分:3)

要将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
}