Debugowanie metody inicjującej aktualizację bazy danych konsoli Menedżera pakietów

106

Chciałem debugować Seed()metodę w mojej klasie konfiguracji bazy danych Entity Framework, gdy uruchamiam Update-Databasez konsoli Menedżera pakietów, ale nie wiedziałem, jak to zrobić. Chciałem podzielić się rozwiązaniem z innymi na wypadek, gdyby mieli ten sam problem.

Sachin Kainth
źródło

Odpowiedzi:

158

Tutaj jest podobne pytanie z rozwiązaniem, które działa naprawdę dobrze.
NIE wymaga Thread.Sleep.
Po prostu uruchamia debuger przy użyciu tego kodu.

Wycięte z odpowiedzi

if (!System.Diagnostics.Debugger.IsAttached) 
    System.Diagnostics.Debugger.Launch();
EthR
źródło
@tchelidze możesz zadzwonić migrate.exez konsoli, aby podłączyć aktualnie działające oprogramowanie Visual Studio. Więcej informacji w tej odpowiedzi: stackoverflow.com/a/52700520/350384
Mariusz Pawelski
20

Sposób, w jaki to rozwiązałem, polegał na otwarciu nowego wystąpienia programu Visual Studio, a następnie otwarciu tego samego rozwiązania w tym nowym wystąpieniu programu Visual Studio. Następnie podłączyłem debuger w tej nowej instancji do starej instancji (devenv.exe) podczas uruchamiania polecenia update-database. To pozwoliło mi debugować metodę Seed.

Aby upewnić się, że nie przegapiłem punktu przerwania, nie dołączając go na czas, dodałem Thread.Sleep przed punktem przerwania.

Mam nadzieję, że to komuś pomoże.

Sachin Kainth
źródło
12

Jeśli chcesz uzyskać wartość określonej zmiennej, szybkim hackem jest rzucenie wyjątku:

throw new Exception(variable);
cederlof
źródło
3
Szybko i brudno :)
DanKodi,
5

Czystszym rozwiązaniem (myślę, że wymaga to EF 6) byłoby IMHO wywołanie update-database z kodu:

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

Pozwala to na debugowanie metody Seed.

Możesz pójść o krok dalej i skonstruować test jednostkowy (lub dokładniej test integracji), który tworzy pustą testową bazę danych, stosuje wszystkie migracje EF, uruchamia metodę Seed i ponownie upuszcza testową bazę danych:

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

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

Database.Delete("TestDatabaseNameOrConnectionString");

Ale uważaj, aby nie uruchamiać tego w swojej programistycznej bazie danych!

Jesper Mygind
źródło
1
W EF Core, ponieważ nie ma klasy DbMigrationsConfiguration, zamiast tego użyj myDbContext.Database.GetPendingMigrations ().
stevie_c
3

Wiem, że to stare pytanie, ale jeśli chcesz tylko wiadomości i nie zależy ci na umieszczaniu w projekcie odniesień do WinForms, stworzyłem proste okno debugowania, w którym mogę wysyłać zdarzenia Trace.

W celu bardziej poważnego debugowania krok po kroku otworzę inną instancję programu Visual Studio, ale nie jest to konieczne w przypadku prostych rzeczy.

Oto cały kod:

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);
    }
  }
}

Oraz w standardowym pliku 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!")
      // ...
    }
  }
}
Jcl
źródło
1
Oczywiście okno debugowania może być tak skomplikowane, jak chcesz (możesz nawet użyć projektanta, aby utworzyć kompletny formularz i przekazać go, aby SeedInternalmetoda mogła go użyć)
Jcl
1

Uh Debugowanie to jedno, ale nie zapomnij wywołać: context.Update ()

Nie zawijaj też prób łapania bez dobrych wewnętrznych wyjątków przedostających się do konsoli.
https://coderwall.com/p/fbcyaw/debug-into-entity-framework-code-first with catch (DbEntityValidationException ex)

Steven Packham
źródło
Sprawdź ten adres URL, który pomoże podnieść jakość treści
Willie Cheng
0

Mam 2 obejścia (bez, Debugger.Launch()ponieważ to nie działa dla mnie):

  1. Aby wydrukować wiadomość w konsoli Menedżera pakietów, użyj wyjątku:
    throw new Exception("Your message");

  2. Innym sposobem jest wydrukowanie wiadomości w pliku poprzez utworzenie cmdprocesu:


    // 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);
    }
Gendolph
źródło