การดีบักวิธีการอัพเดตฐานข้อมูลคอนโซลตัวจัดการแพ็กเกจ


106

ฉันต้องการดีบักSeed()เมธอดในคลาสคอนฟิกูเรชันฐานข้อมูล Entity Framework ของฉันเมื่อฉันเรียกใช้Update-Databaseจาก Package Manager Console แต่ไม่รู้ว่าจะทำอย่างไร ฉันต้องการแบ่งปันวิธีแก้ปัญหากับผู้อื่นในกรณีที่พวกเขามีปัญหาเดียวกัน

คำตอบ:


158

นี่คือคำถามที่คล้ายกันกับวิธีแก้ปัญหาที่ใช้งานได้ดีจริงๆ มันไม่จำเป็นต้อง
เพียงแค่เปิดตัวดีบักเกอร์โดยใช้รหัสนี้Thread.Sleep

คลิปจากคำตอบ

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

@tchelidze คุณสามารถโทรmigrate.exeจากคอนโซลเพื่อแนบวิชวลสตูดิโอที่กำลังทำงานอยู่ ข้อมูลเพิ่มเติมในคำตอบนี้: stackoverflow.com/a/52700520/350384
Mariusz Pawelski

20

วิธีที่ฉันแก้ไขคือเปิดอินสแตนซ์ใหม่ของ Visual Studio จากนั้นเปิดโซลูชันเดียวกันในอินสแตนซ์ใหม่ของ Visual Studio จากนั้นฉันก็แนบดีบักเกอร์ในอินสแตนซ์ใหม่นี้กับอินสแตนซ์เก่า (devenv.exe) ในขณะที่เรียกใช้คำสั่ง update-database สิ่งนี้ทำให้ฉันสามารถดีบักวิธี Seed ได้

เพื่อให้แน่ใจว่าฉันจะไม่พลาดเบรกพอยต์โดยการไม่ติดในเวลาที่ฉันเพิ่มเธรดนอนก่อนเบรกพอยต์

ฉันหวังว่านี่จะช่วยใครบางคนได้


12

หากคุณต้องการรับค่าตัวแปรเฉพาะการแฮ็กอย่างรวดเร็วคือการทิ้งข้อยกเว้น:

throw new Exception(variable);

3
รวดเร็วและสกปรก :)
DanKodi

5

โซลูชันที่สะอาดกว่า (ฉันเดาว่าต้องใช้ EF 6) IMHO จะเรียกฐานข้อมูลการอัปเดตจากรหัส:

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

สิ่งนี้ช่วยให้คุณสามารถดีบักเมธอด Seed

คุณอาจก้าวไปอีกขั้นและสร้างการทดสอบหน่วย (หรืออย่างแม่นยำยิ่งขึ้นคือการทดสอบการรวม) ที่สร้างฐานข้อมูลการทดสอบที่ว่างเปล่าใช้การย้าย EF ทั้งหมดเรียกใช้เมธอด Seed และลดฐานข้อมูลทดสอบอีกครั้ง:

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

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

Database.Delete("TestDatabaseNameOrConnectionString");

แต่ระวังอย่าใช้สิ่งนี้กับฐานข้อมูลการพัฒนาของคุณ!


1
ใน EF Core เนื่องจากไม่มีคลาส DbMigrationsConfiguration ให้ใช้ myDbContext.Database.GetPendingMigrations () แทน
stevie_c

3

ฉันรู้ว่านี่เป็นคำถามเก่า แต่ถ้าทั้งหมดที่คุณต้องการคือข้อความและคุณไม่สนใจที่จะรวมการอ้างอิงถึง WinForms ในโปรเจ็กต์ของคุณฉันได้สร้างหน้าต่างดีบักง่ายๆที่ฉันสามารถส่ง Trace เหตุการณ์ได้

สำหรับการดีบักที่จริงจังและเป็นขั้นเป็นตอนฉันจะเปิดอินสแตนซ์ Visual Studio อื่น แต่ไม่จำเป็นสำหรับสิ่งง่ายๆ

นี่คือรหัสทั้งหมด:

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!")
      // ...
    }
  }
}

1
แน่นอนว่าหน้าต่างดีบักอาจซับซ้อนเท่าที่คุณต้องการ (คุณสามารถใช้ตัวออกแบบเพื่อสร้างแบบฟอร์มที่สมบูรณ์และส่งต่อไปรอบ ๆ เพื่อให้SeedInternalวิธีใช้งานได้)
Jcl

1

เอ่อการดีบักเป็นสิ่งหนึ่ง แต่อย่าลืมเรียก: context.Update ()

นอกจากนี้อย่าห่อลองจับโดยไม่มีข้อยกเว้นด้านในที่ดีหกไปที่คอนโซล
https://coderwall.com/p/fbcyaw/debug-into-entity-framework-code-first ด้วยการจับ (DbEntityValidationException เช่น)


โปรดตรวจสอบURLนี้จะเป็นประโยชน์ในการยกระดับคุณภาพเนื้อหาของคุณ
Willie Cheng

0

ฉันมีวิธีแก้ปัญหา 2 วิธี (ไม่มีDebugger.Launch()เพราะมันไม่ได้ผลสำหรับฉัน):

  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);
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.