Debugging der Packed Manager Console Update-Database Seed-Methode


105

Ich wollte die Seed()Methode in meiner Entity Framework-Datenbankkonfigurationsklasse debuggen, als ich sie Update-Databaseüber die Package Manager-Konsole ausführte, wusste aber nicht, wie das geht. Ich wollte die Lösung mit anderen teilen, falls sie das gleiche Problem haben.

Antworten:


157

Hier ist eine ähnliche Frage mit einer Lösung, die wirklich gut funktioniert.
Es erfordert NICHT Thread.Sleep.
Startet einfach den Debugger mit diesem Code.

Aus der Antwort herausgeschnitten

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

@tchelidze können Sie migrate.exevon der Konsole aus anrufen , um das aktuell ausgeführte Visual Studio anzuhängen. Weitere Informationen in dieser Antwort: stackoverflow.com/a/52700520/350384
Mariusz Pawelski

20

Die Lösung bestand darin, eine neue Instanz von Visual Studio zu öffnen und dann dieselbe Lösung in dieser neuen Instanz von Visual Studio zu öffnen. Ich habe dann den Debugger in dieser neuen Instanz an die alte Instanz (devenv.exe) angehängt, während ich den Befehl update-database ausgeführt habe. Dadurch konnte ich die Seed-Methode debuggen.

Um sicherzustellen, dass ich den Haltepunkt nicht verpasst habe, indem ich ihn nicht rechtzeitig angehängt habe, habe ich vor dem Haltepunkt einen Thread.Sleep hinzugefügt.

Ich hoffe das hilft jemandem.


12

Wenn Sie den Wert einer bestimmten Variablen abrufen müssen, können Sie schnell eine Ausnahme auslösen:

throw new Exception(variable);

3
Schnell und schmutzig :)
DanKodi

5

Eine sauberere Lösung (ich denke, dies erfordert EF 6) wäre meiner Meinung nach, die Update-Datenbank aus dem Code aufzurufen:

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

Auf diese Weise können Sie die Seed-Methode debuggen.

Sie können noch einen Schritt weiter gehen und einen Komponententest (oder genauer einen Integrationstest) erstellen, der eine leere Testdatenbank erstellt, alle EF-Migrationen anwendet, die Seed-Methode ausführt und die Testdatenbank erneut löscht:

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

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

Database.Delete("TestDatabaseNameOrConnectionString");

Aber achten Sie darauf, dies nicht gegen Ihre Entwicklungsdatenbank auszuführen!


1
Verwenden Sie in EF Core stattdessen myDbContext.Database.GetPendingMigrations (), da es keine DbMigrationsConfiguration-Klasse gibt.
stevie_c

3

Ich weiß, dass dies eine alte Frage ist, aber wenn Sie nur Nachrichten wünschen und keine Verweise auf WinForms in Ihr Projekt aufnehmen möchten, habe ich ein einfaches Debug-Fenster erstellt, in dem ich Trace-Ereignisse senden kann.

Für ein ernsthafteres und schrittweises Debuggen öffne ich eine weitere Visual Studio-Instanz, die jedoch für einfache Aufgaben nicht erforderlich ist.

Das ist der ganze 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);
    }
  }
}

Und auf Ihrer Standard 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!")
      // ...
    }
  }
}

1
Natürlich kann das Debug-Fenster so kompliziert sein, wie Sie möchten (Sie können sogar den Designer verwenden, um ein vollständiges Formular zu erstellen und es weiterzugeben, damit die SeedInternalMethode es verwenden kann)
Jcl


0

Ich habe 2 Problemumgehungen (ohne, Debugger.Launch()da es bei mir nicht funktioniert):

  1. Verwenden Sie eine Ausnahme, um Nachrichten in der Package Manager-Konsole zu drucken:
    throw new Exception("Your message");

  2. Eine andere Möglichkeit besteht darin, eine Nachricht in einer Datei zu drucken, indem Sie einen cmdProzess erstellen :


    // 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);
    }
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.