Depurar el método de semilla de base de datos de actualización de la consola del administrador de paquetes


106

Quería depurar el Seed()método en mi clase de configuración de la base de datos de Entity Framework cuando lo ejecuto Update-Databasedesde la Consola del Administrador de paquetes, pero no sabía cómo hacerlo. Quería compartir la solución con otros en caso de que tuvieran el mismo problema.

Respuestas:


158

Aquí hay una pregunta similar con una solución que funciona muy bien.
NO requiere Thread.Sleep.
Simplemente inicia el depurador usando este código.

Recortado de la respuesta

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

@tchelidze puede llamar migrate.exedesde la consola para adjuntar el estudio visual que se está ejecutando actualmente. Más información en esta respuesta: stackoverflow.com/a/52700520/350384
Mariusz Pawelski

20

La forma en que resolví esto fue abrir una nueva instancia de Visual Studio y luego abrir la misma solución en esta nueva instancia de Visual Studio. Luego adjunté el depurador en esta nueva instancia a la instancia anterior (devenv.exe) mientras ejecutaba el comando update-database. Esto me permitió depurar el método Seed.

Solo para asegurarme de que no perdí el punto de interrupción al no adjuntar a tiempo, agregué un Thread.Sleep antes del punto de interrupción.

Espero que esto ayude a alguien.


12

Si necesita obtener el valor de una variable específica, un truco rápido es lanzar una excepción:

throw new Exception(variable);

3
Rápido y sucio :)
DanKodi

5

Una solución más limpia (supongo que esto requiere EF 6) sería en mi humilde opinión llamar a la base de datos de actualización desde el código:

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

Esto le permite depurar el método Seed.

Puede dar un paso más y construir una prueba unitaria (o, más precisamente, una prueba de integración) que crea una base de datos de prueba vacía, aplica todas las migraciones de EF, ejecuta el método Seed y vuelve a descartar la base de datos de prueba:

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

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

Database.Delete("TestDatabaseNameOrConnectionString");

¡Pero tenga cuidado de no ejecutar esto en su base de datos de desarrollo!


1
En EF Core, dado que no hay una clase DbMigrationsConfiguration, use myDbContext.Database.GetPendingMigrations () en su lugar.
stevie_c

3

Sé que esta es una pregunta antigua, pero si todo lo que desea son mensajes y no le interesa incluir referencias a WinForms en su proyecto, hice una ventana de depuración simple donde puedo enviar eventos de seguimiento.

Para una depuración más seria y paso a paso, abriré otra instancia de Visual Studio, pero no es necesario para cosas simples.

Este es el código completo:

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

Y en su Configuration.cs estándar

// ...
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
Por supuesto, la ventana de depuración puede ser tan complicada como desee (incluso podría usar el diseñador para crear un formulario completo y pasarlo para que el SeedInternalmétodo pueda usarlo)
Jcl


0

Tengo 2 soluciones alternativas (sin Debugger.Launch()ya que no funciona para mí):

  1. Para imprimir un mensaje en la consola de Package Manager, utilice la excepción:
    throw new Exception("Your message");

  2. Otra forma es imprimir el mensaje en un archivo creando un cmdproceso:


    // 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);
    }
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.