调试包管理器控制台更新-数据库种子方法

当我从 Package Manager Console 运行 Update-Database时,我想在我的实体框架数据库配置类中调试 Seed()方法,但不知道如何做。我想与其他人分享解决方案,以防他们有同样的问题。

36269 次浏览

我解决这个问题的方法是打开一个新的 VisualStudio 实例,然后在这个新的 VisualStudio 实例中打开相同的解决方案。然后,在运行 update-database 命令时,将这个新实例中的调试器附加到旧实例(devenv.exe)。这允许我调试 Seed 方法。

为了确保不会因为没有及时附加而错过断点,我添加了一个 Thread。在断点之前睡眠。

我希望这能帮到别人。

这里有一个类似的 question解决方案,工作得非常好。
它不需要 Thread.Sleep
使用此代码启动调试器。

从答案上剪下来的

if (!System.Diagnostics.Debugger.IsAttached)
System.Diagnostics.Debugger.Launch();

如果需要获取特定变量的值,一个快速的方法是抛出一个异常:

throw new Exception(variable);

我知道这是一个老问题,但是如果您想要的只是消息,并且您不愿意在项目中包含对 WinForms 的引用,那么我做了一些简单的调试窗口,可以在其中发送 Trace 事件。

为了进行更严肃和逐步的调试,我将打开另一个 VisualStudio 实例,但是对于简单的东西不需要这样做。

This is the whole code:

SeedApplicationContext.cs

using System;
using System.Data.Entity;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;


namespace Data.Persistence.Migrations.SeedDebug
{
public class SeedApplicationContext<T> : ApplicationContext
where T : DbContext
{
private class SeedTraceListener : TraceListener
{
private readonly SeedApplicationContext<T> _appContext;


public SeedTraceListener(SeedApplicationContext<T> appContext)
{
_appContext = appContext;
}


public override void Write(string message)
{
_appContext.WriteDebugText(message);
}


public override void WriteLine(string message)
{
_appContext.WriteDebugLine(message);
}
}


private Form _debugForm;
private TextBox _debugTextBox;
private TraceListener _traceListener;


private readonly Action<T> _seedAction;
private readonly T _dbcontext;


public Exception Exception { get; private set; }
public bool WaitBeforeExit { get; private set; }


public SeedApplicationContext(Action<T> seedAction, T dbcontext, bool waitBeforeExit = false)
{
_dbcontext = dbcontext;
_seedAction = seedAction;
WaitBeforeExit = waitBeforeExit;
_traceListener = new SeedTraceListener(this);
CreateDebugForm();
MainForm = _debugForm;
Trace.Listeners.Add(_traceListener);
}


private void CreateDebugForm()
{
var textbox = new TextBox {Multiline = true, Dock = DockStyle.Fill, ScrollBars = ScrollBars.Both, WordWrap = false};
var form = new Form {Font = new Font(@"Lucida Console", 8), Text = "Seed Trace"};
form.Controls.Add(tb);
form.Shown += OnFormShown;
_debugForm = form;
_debugTextBox = textbox;
}


private void OnFormShown(object sender, EventArgs eventArgs)
{
WriteDebugLine("Initializing seed...");
try
{
_seedAction(_dbcontext);
if(!WaitBeforeExit)
_debugForm.Close();
else
WriteDebugLine("Finished seed. Close this window to continue");
}
catch (Exception e)
{
Exception = e;
var einner = e;
while (einner != null)
{
WriteDebugLine(string.Format("[Exception {0}] {1}", einner.GetType(), einner.Message));
WriteDebugLine(einner.StackTrace);
einner = einner.InnerException;
if (einner != null)
WriteDebugLine("------- Inner Exception -------");
}
}
}


protected override void Dispose(bool disposing)
{
if (disposing && _traceListener != null)
{
Trace.Listeners.Remove(_traceListener);
_traceListener.Dispose();
_traceListener = null;
}
base.Dispose(disposing);
}


private void WriteDebugText(string message)
{
_debugTextBox.Text += message;
Application.DoEvents();
}


private void WriteDebugLine(string message)
{
WriteDebugText(message + Environment.NewLine);
}
}
}

还有你的标准 Configuration.cs

// ...
using System.Windows.Forms;
using Data.Persistence.Migrations.SeedDebug;
// ...


namespace Data.Persistence.Migrations
{
internal sealed class Configuration : DbMigrationsConfiguration<MyContext>
{
public Configuration()
{
// Migrations configuration here
}


protected override void Seed(MyContext context)
{
// Create our application context which will host our debug window and message loop
var appContext = new SeedApplicationContext<MyContext>(SeedInternal, context, false);
Application.Run(appContext);
var e = appContext.Exception;
Application.Exit();
// Rethrow the exception to the package manager console
if (e != null)
throw e;
}


// Our original Seed method, now with Trace support!
private void SeedInternal(MyContext context)
{
// ...
Trace.WriteLine("I'm seeding!")
// ...
}
}
}

调试是一回事,但别忘了打电话给: 更新()

如果没有一个好的内部异常溢出到控制台,也不要包装 try catch。
Https://coderwall.com/p/fbcyaw/debug-into-entity-framework-code-first 带 catch (DbEntityValidationException ex)

A cleaner solution (I guess this requires EF 6) would IMHO be to call update-database from code:

var configuration = new DbMigrationsConfiguration<TContext>();
var databaseMigrator = new DbMigrator(configuration);
databaseMigrator.Update();

This allows you to debug the Seed method.

您可以更进一步,构造一个单元测试(或者更确切地说,一个集成测试) ,它创建一个空的测试数据库,应用所有 EF 迁移,运行种子方法,然后再次删除测试数据库:

var configuration = new DbMigrationsConfiguration<TContext>();
Database.Delete("TestDatabaseNameOrConnectionString");


var databaseMigrator = new DbMigrator(configuration);
databaseMigrator.Update();


Database.Delete("TestDatabaseNameOrConnectionString");

但是请注意,不要在开发数据库中运行此命令!

I have 2 workarounds (without Debugger.Launch() since it doesn't work for me):

  1. 要在 Package Manager Console 中打印消息,请使用异常:
    throw new Exception("Your message");

  2. 另一种方法是通过创建 cmd进程在文件中打印消息:


// Logs to file {solution folder}\seed.log data from Seed method (for DEBUG only)
private void Log(string msg)
{
string echoCmd = $"/C echo {DateTime.Now} - {msg} >> seed.log";
System.Diagnostics.Process.Start("cmd.exe", echoCmd);
}