Debug del metodo seed del database di aggiornamento della console di Gestione pacchetti


106

Volevo eseguire il debug del Seed()metodo nella mia classe di configurazione del database Entity Framework quando eseguo Update-Databasedalla console di Gestione pacchetti ma non sapevo come farlo. Volevo condividere la soluzione con altri nel caso avessero lo stesso problema.

Risposte:


158

Ecco una domanda simile con una soluzione che funziona davvero bene.
NON richiede Thread.Sleep.
Avvia semplicemente il debugger usando questo codice.

Ritagliato dalla risposta

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

@tchelidze puoi chiamare migrate.exedalla console per collegare Visual Studio attualmente in esecuzione. Maggiori informazioni in questa risposta: stackoverflow.com/a/52700520/350384
Mariusz Pawelski

20

Il modo in cui ho risolto questo problema è stato aprire una nuova istanza di Visual Studio e quindi aprire la stessa soluzione in questa nuova istanza di Visual Studio. Ho quindi collegato il debugger in questa nuova istanza alla vecchia istanza (devenv.exe) durante l'esecuzione del comando update-database. Questo mi ha permesso di eseguire il debug del metodo Seed.

Solo per assicurarmi di non aver perso il punto di interruzione non collegandomi in tempo, ho aggiunto un thread.

Spero che questo aiuti qualcuno.


12

Se è necessario ottenere il valore di una variabile specifica, un trucco rapido consiste nel lanciare un'eccezione:

throw new Exception(variable);

3
Veloce e sporco :)
DanKodi

5

Una soluzione più pulita (immagino che richieda EF 6) sarebbe IMHO quella di chiamare il database di aggiornamento dal codice:

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

Ciò consente di eseguire il debug del metodo Seed.

Puoi fare un ulteriore passo avanti e costruire uno unit test (o, più precisamente, un test di integrazione) che crea un database di test vuoto, applica tutte le migrazioni EF, esegue il metodo Seed e rilascia nuovamente il database di test:

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

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

Database.Delete("TestDatabaseNameOrConnectionString");

Ma fai attenzione a non eseguirlo sul tuo database di sviluppo!


1
In EF Core poiché non è presente alcuna classe DbMigrationsConfiguration, utilizzare invece myDbContext.Database.GetPendingMigrations ().
stevie_c

3

So che questa è una vecchia domanda, ma se tutto ciò che vuoi sono messaggi e non ti interessa includere riferimenti a WinForms nel tuo progetto, ho creato una semplice finestra di debug in cui posso inviare eventi di traccia.

Per un debug più serio e dettagliato, aprirò un'altra istanza di Visual Studio, ma non è necessario per cose semplici.

Questo è l'intero codice:

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

E sul tuo file Configuration.cs standard

// ...
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
Ovviamente, la finestra di debug può essere complicata quanto vuoi (potresti persino usare il designer per creare un modulo completo e passarlo in giro in modo che il SeedInternalmetodo possa usarlo)
Jcl


0

Ho 2 soluzioni alternative (senza Debugger.Launch()dato che non funziona per me):

  1. Per stampare un messaggio nella console di Gestione pacchetti usa l'eccezione:
    throw new Exception("Your message");

  2. Un altro modo è stampare il messaggio in file creando un cmdprocesso:


    // 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);
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.