如何在控制台应用程序中为 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-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
目录中创建我的数据库?!我在 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, 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, 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, DbCommandInterceptionContext
1 c) at
System.Data.Entity.Infrastructure.Interception.InternalDispatcher1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget
target, Func
3 operation, TInterceptionContext interceptionContext,
Action3 executing, Action
3 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, Action
1 act) at
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
System.Data.Entity.SqlServer.SqlProviderServices.DbCreateDatabase(DbConnection
connection, Nullable1 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
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 个)
- "DataDirectory" 在 AppDomain 中设置。
- 控制台应用程序有自己的 AppDomains,它们在启动时创建并在退出时消失。
- 包管理器(您所在的位置 运行ning
Update-Database
)无权访问您设置 "DataDirectory" 值的控制台应用程序的 AppDomain。
- 您需要:
- 在程序包管理器中以编程方式设置 "DataDirectory",或
- 在控制台应用程序的上下文中以编程方式运行"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
}
我尝试为 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-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
目录中创建我的数据库?!我在 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, 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, 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, DbCommandInterceptionContext
1 c) at System.Data.Entity.Infrastructure.Interception.InternalDispatcher1.Dispatch[TTarget,TInterceptionContext,TResult](TTarget target, Func
3 operation, TInterceptionContext interceptionContext, Action3 executing, Action
3 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, Action
1 act) at 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 System.Data.Entity.SqlServer.SqlProviderServices.DbCreateDatabase(DbConnection connection, Nullable1 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 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 个)
- "DataDirectory" 在 AppDomain 中设置。
- 控制台应用程序有自己的 AppDomains,它们在启动时创建并在退出时消失。
- 包管理器(您所在的位置 运行ning
Update-Database
)无权访问您设置 "DataDirectory" 值的控制台应用程序的 AppDomain。 - 您需要:
- 在程序包管理器中以编程方式设置 "DataDirectory",或
- 在控制台应用程序的上下文中以编程方式运行"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
}