[英]How to automate Package Manager Console in Visual Studio 2013
My specific problem is how can I automate "add-migration" in a build process for the Entity Framework. 我的具体问题是如何在Entity Framework的构建过程中自动执行“添加迁移”。 In researching this, it seems the mostly likely approach is something along the lines of automating these steps 在研究这个问题时,似乎最常见的方法是自动化这些步骤
This initial approach is based on my own research and this question , the powershell script ultimately behind Add-Migration requires quite a bit of set-up to run. 这个初始方法基于我自己的研究和这个问题 ,最终在Add-Migration背后的powershell脚本需要相当多的设置才能运行。 Visual Studio performs that setup automatically when creating the Package Manager Console and making the DTE object available. Visual Studio在创建程序包管理器控制台并使DTE对象可用时自动执行该设置。 I would prefer not to attempt to duplicate that setup outside of Visual Studio. 我宁愿不尝试在Visual Studio之外复制该设置。
One possible path to a solution is this unanswered stack overflow question 解决方案的一个可能途径是未解决的堆栈溢出问题
In researching the NuGet API, it does not appear to have a "send this text and it will be run like it was typed in the console". 在研究NuGet API时,它似乎没有“发送此文本,它将像在控制台中键入的那样运行”。 I am not clear on the lines between Visual Studio vs NuGet so I am not sure this is something that would be there. 我不清楚Visual Studio与NuGet之间的界限,所以我不确定这是否会存在。
I am able to find the "Pacakage Manager Console" ironically enough via "$dte.Windows" command in the Package Manager Console but in a VS 2013 window, that collection gives me objects which are "Microsoft.VisualStudio.Platform.WindowManagement.DTE.WindowBase". 我能够通过软件包管理器控制台中的“$ dte.Windows”命令找到“Pacakage Manager Console”,但在VS 2013窗口中,该集合为我提供了“Microsoft.VisualStudio.Platform.WindowManagement.DTE”对象。 .WindowBase”。 If there is a way stuff text into it, I think I need to get it to be a NuGetConsole.Implementation.PowerConsoleToolWindow" through reviewing the source code I am not clear how the text would stuffed but I am not at all familiar with what I am seeing. 如果有一种方式填充文本,我想我需要把它变成一个NuGetConsole.Implementation.PowerConsoleToolWindow“通过查看源代码我不清楚文本是如何填充但我不熟悉我的内容我看到了。
Worst case, I will fall back to trying to stuff keys to it along the lines of this question but would prefer not to since that will substantially complicate the automation surrounding the build process. 最糟糕的情况是,我会回过头来尝试按照这个问题来填充密钥,但是不愿意这样做,因为这会使构建过程周围的自动化大大复杂化。
All of that being said, 所有这些都说,
Thanks for any suggestions, advice, help, non-abuse in advance, 感谢您提出任何建议,建议,帮助,非滥用,
John 约翰
The approach that worked for me was to trace into the entity framework code starting in with the AddMigrationCommand.cs in the EntityFramework.Powershell project and find the hooks into the EntityFramework project and then make those hooks work so there is no Powershell dependency. 对我有用的方法是从EntityFramework.Powershell项目中的AddMigrationCommand.cs开始跟踪实体框架代码,找到EntityFramework项目的钩子,然后使这些钩子工作,这样就没有Powershell依赖。
You can get something like... 你可以得到像......
public static void RunIt(EnvDTE.Project project, Type dbContext, Assembly migrationAssembly, string migrationDirectory,
string migrationsNamespace, string contextKey, string migrationName)
{
DbMigrationsConfiguration migrationsConfiguration = new DbMigrationsConfiguration();
migrationsConfiguration.AutomaticMigrationDataLossAllowed = false;
migrationsConfiguration.AutomaticMigrationsEnabled = false;
migrationsConfiguration.CodeGenerator = new CSharpMigrationCodeGenerator(); //same as default
migrationsConfiguration.ContextType = dbContext; //data
migrationsConfiguration.ContextKey = contextKey;
migrationsConfiguration.MigrationsAssembly = migrationAssembly;
migrationsConfiguration.MigrationsDirectory = migrationDirectory;
migrationsConfiguration.MigrationsNamespace = migrationsNamespace;
System.Data.Entity.Infrastructure.DbConnectionInfo dbi = new System.Data.Entity.Infrastructure.DbConnectionInfo("DataContext");
migrationsConfiguration.TargetDatabase = dbi;
MigrationScaffolder ms = new MigrationScaffolder(migrationsConfiguration);
ScaffoldedMigration sf = ms.Scaffold(migrationName, false);
}
You can use this question to get to the dte object and from there to find the project object to pass into the call. 您可以使用此问题到达dte对象,并从那里找到要传递给调用的项目对象。
This is an update to John's answer whom I have to thank for the "hard part", but here is a complete example which creates a migration and adds that migration to the supplied project (project must be built before) the same way as Add-Migration InitialBase -IgnoreChanges
would: 这是对约翰答案的更新,我要感谢“困难部分”,但这里有一个完整的例子,它创建了一个迁移并将迁移添加到提供的项目(之前必须构建项目),与Add-Migration InitialBase -IgnoreChanges
相同 - Add-Migration InitialBase -IgnoreChanges
将:
public void ScaffoldedMigration(EnvDTE.Project project)
{
var migrationsNamespace = project.Properties.Cast<Property>()
.First(p => p.Name == "RootNamespace").Value.ToString() + ".Migrations";
var assemblyName = project.Properties.Cast<Property>()
.First(p => p.Name == "AssemblyName").Value.ToString();
var rootPath = Path.GetDirectoryName(project.FullName);
var assemblyPath = Path.Combine(rootPath, "bin", assemblyName + ".dll");
var migrationAssembly = Assembly.Load(File.ReadAllBytes(assemblyPath));
Type dbContext = null;
foreach(var type in migrationAssembly.GetTypes())
{
if(type.IsSubclassOf(typeof(DbContext)))
{
dbContext = type;
break;
}
}
var migrationsConfiguration = new DbMigrationsConfiguration()
{
AutomaticMigrationDataLossAllowed = false,
AutomaticMigrationsEnabled = false,
CodeGenerator = new CSharpMigrationCodeGenerator(),
ContextType = dbContext,
ContextKey = migrationsNamespace + ".Configuration",
MigrationsAssembly = migrationAssembly,
MigrationsDirectory = "Migrations",
MigrationsNamespace = migrationsNamespace
};
var dbi = new System.Data.Entity.Infrastructure
.DbConnectionInfo("ConnectionString", "System.Data.SqlClient");
migrationsConfiguration.TargetDatabase = dbi;
var scaffolder = new MigrationScaffolder(migrationsConfiguration);
ScaffoldedMigration migration = scaffolder.Scaffold("InitialBase", true);
var migrationFile = Path.Combine(rootPath, migration.Directory,
migration.MigrationId + ".cs");
File.WriteAllText(migrationFile, migration.UserCode);
var migrationItem = project.ProjectItems.AddFromFile(migrationFile);
var designerFile = Path.Combine(rootPath, migration.Directory,
migration.MigrationId + ".Designer.cs");
File.WriteAllText(designerFile, migration.DesignerCode);
var designerItem = project.ProjectItems.AddFromFile(migrationFile);
foreach(Property prop in designerItem.Properties)
{
if (prop.Name == "DependentUpon")
prop.Value = Path.GetFileName(migrationFile);
}
var resxFile = Path.Combine(rootPath, migration.Directory,
migration.MigrationId + ".resx");
using (ResXResourceWriter resx = new ResXResourceWriter(resxFile))
{
foreach (var kvp in migration.Resources)
resx.AddResource(kvp.Key, kvp.Value);
}
var resxItem = project.ProjectItems.AddFromFile(resxFile);
foreach (Property prop in resxItem.Properties)
{
if (prop.Name == "DependentUpon")
prop.Value = Path.GetFileName(migrationFile);
}
}
I execute this in my project template's IWizard
implementation where I run a migration with IgnoreChanges
, because of shared entites with the base project. 我在项目模板的IWizard
实现中执行此操作,我使用IgnoreChanges
运行迁移,因为它与基础项目共享。 Change scaffolder.Scaffold("InitialBase", true)
to scaffolder.Scaffold("InitialBase", false)
if you want to include the changes. 如果要包含更改scaffolder.Scaffold("InitialBase", true)
更改为scaffolder.Scaffold("InitialBase", false)
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.