如何在控制台应用程序中为ConnectionString定义DataDirectory以使用EntityFramework Code First Migrations
我尝试在App.config
为数据库设置位置MyProject\App_Data\Cos.mdf
:
在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-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回答后编辑是的,你是对的,有错误。 你回答后我尝试了更多的组合,不幸的是没有一个工作。
和主要的
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指定’-Verbose’标志以查看应用于目标数据库的SQL语句。 System.Data.SqlClient.SqlException(0x80131904):发生文件激活错误。 物理文件名’\ baza.mdf’可能不正确。 诊断并更正其他错误,然后重试该操作。 CREATE DATABASE失败。 无法创建列出的某些文件名。 检查相关错误。 System.Data.SqlClient.TdsParser上System.Data.SqlClient.SqlInternalConnection.OnError(SqlExceptionexception,Boolean breakConnection,Action
1 wrapCloseInAction) at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action
1 wrapCloseInAction) System.Data.SqlClient.SqlCommand的System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior,SqlCommand cmdHandler,SqlDataReader dataStream,BulkCopySimpleResultSet bulkCopyHandler,TdsParserStateObject stateObj,Boolean&dataReady)中的.ThrowExceptionAndWarning(TdsParserStateObject stateObj,Boolean callerHasConnectionLock,Boolean asyncClose)。 System.Data.SqlClient.SqlCommand的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.b__0(DbCommand t, DbCommandInterceptionContext
RunExecuteNonQueryTds(String methodName,Boolean async,Int32 timeout,Boolean asyncWrite)1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.ExecuteNonQuery() at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.b__0(DbCommand t, DbCommandInterceptionContext
1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite) at System.Data.SqlClient.SqlCommand.ExecuteNonQuery() at System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.b__0(DbCommand t, DbCommandInterceptionContext
1 c)1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func
3操作,TInterceptionContext interceptionContext,Action3 executing, Action
3)在System.Data.Entity.Infrastructure.Interception.DbCommandDispatcher.NonQuery(DbCommand命令,DbCommandInterceptionContext interceptionContext)的System.Data.Entity.SqlServer.SqlProviderServices。 c__DisplayClass1a.b__19(DbConnection conn)at System System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute [TResult]上的System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy。 c__DisplayClass1.b__0()处于.Data.Entity.SqlServer.SqlProviderServices。 c__DisplayClass33.b__32()1 operation) at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute(Action operation) at System.Data.Entity.SqlServer.SqlProviderServices.UsingConnection(DbConnection sqlConnection, Action
Func1 operation) at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute(Action operation) at System.Data.Entity.SqlServer.SqlProviderServices.UsingConnection(DbConnection sqlConnection, Action
在System.Data.Entity.SqlServer.SqlProviderServices.UsingMasterConnection(DbConnection sqlConnection,Action1 act) at System.Data.Entity.SqlServer.SqlProviderServices.CreateDatabaseFromScript(Nullable
1 commandTimeout,DbConnection sqlConnection,String createDatabaseScript)at onction,Action1 act) at System.Data.Entity.SqlServer.SqlProviderServices.CreateDatabaseFromScript(Nullable
System.Data.Entity.SqlServer.SqlProviderServices.DbCreateDatabase(DbConnection连接,Nullable1 commandTimeout, StoreItemCollection storeItemCollection) at System.Data.Entity.Core.Common.DbProviderServices.CreateDatabase(DbConnection connection, Nullable
1 commandTimeout,StoreItemCollection storeItemCollection)。系统中的System.Data.Entity.Migrations.DbMigrator.EnsureDatabaseExists(Action mustSucceedToKeepDatabase)上的System.Data.Entity.Migrations.Utilities.DatabaseCreator.Create(DbConnection连接)中的Data.Entity.Core.Objects.ObjectContext.CreateDatabase()。 Data.Entity.Migrations.Infrastructure.MigratorBase.EnsureDatabaseExists(Action mustSucceedToKeepDatabase)at at 位于System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.Run的System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(String targetMigration)中的System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)( )在System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)的System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
在System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner)的System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner)中,System.Data.Entity.Migrations.UpdateDatabaseCommand上的System.Data.Entity.Migrations.Design.ToolingFacade.Update(String targetMigration,Boolean force)。 c__DisplayClass2。 b__0()在System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(操作命令)ClientConnectionId:23ca49c1-4797-4bc3-8f16-f34fd77f2cbe发生文件激活错误。 物理文件名’\ baza.mdf’可能不正确。 诊断并更正其他错误,然后重试该操作。 CREATE DATABASE失败。 无法创建列出的某些文件名。 检查相关错误。 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
连接字符串的MSDN页面在标题为“支持| DataDirectory |替换字符串…”的部分中有一些额外的详细信息。
问题2(共2个)
- “DataDirectory”在AppDomain中设置。
- 控制台应用程序有自己的AppDomain,它们在启动时创建,在退出时消失。
- 程序包管理器(运行
Update-Database
)无法访问控制台应用程序的AppDomain,您可以在其中设置“DataDirectory”的值。 - 你需要:
- 以编程方式在程序包管理器中设置“DataDirectory”
- 以编程方式在控制台应用程序的上下文中运行“update-database”
我不知道如何以编程方式与Package Manager进行交互,但我确实设法弄清楚如何以编程方式触发“update-database”进程。 只需在设置“DataDirectory”的值后添加以下行:
Database.SetInitializer(new MigrateDatabaseToLatestVersion() );
您还需要至少一个(如果不是两个) 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() ); // 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
有关详细信息,请参阅CodeDN 迁移的MSDN页面。
一旦调用SetInitializer
这行代码与SetInitializer
一起MigrateDatabaseToLatestVersion
,它就MigrateDatabaseToLatestVersion
:每次运行时(这就是为什么这在控制台应用程序的开头完成)它会同步“模型”中的内容之间的任何变化(现在已编译到程序集中)和数据库,确保数据库具有最新版本。 这假设任何新表都在DbContext
类中表示。 但是不需要运行其他程序包管理器命令。
您可以在Enable-Migrations命令创建的Configuration类中为Update-Database设置DataDirectory:
internal sealed class Configuration : DbMigrationsConfiguration { public Configuration() { AutomaticMigrationsEnabled = false; var dataDirPath = ""; 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 }