0

私の場合は、Visual StudioのWeb APIプロジェクトです。私がテストしているとき、APIは同時に複数回呼び出されます。複数のスレッドでDatabase.Logからメッセージが混ざっています

私は、SQL Serverに送信されている生のSQLログインするために、次を使用しています:

context.Database.Log = Console.WriteLine; 

SQLをログに記録されている場合、それは他のスレッド上のクエリと混ざっなってきています。より具体的には、最も頻繁に混乱するパラメータです。これにより、適切なパラメータと正しいクエリを関連付けることが不可能になります。同じAPIが同時に2回呼び出されることもあります。

私は非同期呼び出しを使用していますが、それは問題の原因ではありません。異なる完了スレッドに複数の同時Web要求があることは事実です。

正確な信頼できるロギングが必要なので、出力ウィンドウを調べてSQLを確認できます。

+0

使用しているコードを表示することは可能でしょうか?小さな**作業**のサンプルは素晴らしいでしょう。 – Enigmativity

+0

@Enigmativityこれは一般的なシナリオです。より多くの文脈のためにこの質問を見てください:https://stackoverflow.com/q/16880687/887092 – Todd

答えて

1

コンテキストごとにすべてのログメッセージをバッファリングしてから、dbコンテキストを破棄してそのバッファを書き出す必要があります。

あなたはその後、あなたはそれが強いを保存せずに動作するはずごとのコンテキスト

class LogGroup 
{ 
    static bool ReferenceActiveGroups = true; //I'm not sure if this is needed. It might work fine without. 
    static HashSet<LogGroup> LogGroups = ReferenceActiveGroups ? new HashSet<LogGroup>() : null; 

    /// <summary> 
    /// For the currently being ran query, this outputs the Raw SQL and the length of time it was executed in the Output window (CTRL + ALT + O) when in Debug mode. 
    /// </summary> 
    /// <param name="db">The DbContext to be outputted in the Output Window.</param> 
    public static void Log(ApiController context, AppContext db) 
    { 
     var o = new LogGroup(context, db); 
     o.Initialise(); 
     if (ReferenceActiveGroups) o.Add(); 
    } 

    public LogGroup(ApiController context, AppContext db) 
    { 
     this.context = context; 
     this.db = db; 
    } 

    public void Initialise() 
    { 
     db.OnDisposed += (sender, e) => { this.Complete(); }; 
     db.Database.Log = this.Handler; 

     sb.AppendLine("LOG GROUP START"); 
    } 

    public void Add() 
    { 
     lock (LogGroups) 
     { 
      LogGroups.Add(this); 
     } 
    } 

    public void Handler(string message) 
    { 
     sb.AppendLine(message); 
    } 

    public AppContext db = null; 
    public ApiController context = null; 
    public StringBuilder sb = new StringBuilder(); 

    public void Remove() 
    { 
     lock (LogGroups) 
     { 
      LogGroups.Remove(this); 
     } 
    } 

    public void Complete() 
    { 
     if (ReferenceActiveGroups) Remove(); 

     sb.AppendLine("LOG GROUP END"); 
     System.Diagnostics.Debug.WriteLine(sb.ToString()); 
    } 
} 

をバッファリングを管理するには、このクラスを必要とする、あなたのデシベルコンテキストの処分イベント

protected override void Dispose(bool disposing) 
{ 
    base.Dispose(disposing); 

    if (OnDisposed != null) OnDisposed(this, null); 
} 

public event EventHandler OnDisposed; 

にフックできるようにする必要がありますLogGroupオブジェクトへの参照。しかし、私はまだそれをテストしていません。また、この種のコードをコンテキストに直接含めることができるので、LogGroup参照オブジェクトを保存する必要はありません。しかし、それは移植可能ではありません。 - 要求URI Iはコントローラの参照を渡すので、ログは、いくつかの追加のコンテキスト情報を含むことができること

var db = new MyDbContext(); 
LogGroup.Log(this, db); 

注:コントローラアクション目的球にそれを使用する

は、ログの作品は、あなたがログ出力でコメントしたパラメータを見つけること

今、あなたのログ解釈することで動作するように痛みます。通常、それらを手動で適切なSQLパラメータに変更する必要がありますが、パラメータを使用してより大きなSQLクエリのサブセクションを実行することは困難です。

私はEFにログを出力させる1つまたは2つの方法があることは知っています。これらのメソッドは、パラメータの出力方法をよりよく制御できますが、Database.Logを作成するという答えが得られれば、このツールをWinFormsに含めるので、機能的なクエリでクリップボードを書き直すことができます。

public partial class Form1 : Form 
{ 
    public Form1() 
    { 
     InitializeComponent(); 
    } 

    class parameter 
    { 
     public string Name; 
     public string Value; 
     public string Type; 

     public string FormattedValue 
     { 
      get 
      { 
       if (Type == "Boolean") 
       { 
        if (Value == "True") 
         return "1"; 
        else 
         return "0"; 
       } 
       else if (Type == "Int32") 
       { 
        return Value; 
       } 
       else 
        throw new Exception("Unsupported type - " + Type); 
      } 

     } 

     public override string ToString() 
     { 
      return string.Format("{0} - {1} - {2} - {3}", Name, Value, Type, FormattedValue); 

     } 
    } 

    private void button1_Click(object sender, EventArgs e) 
    { 


     var sb = new StringBuilder(); 
     var data = Clipboard.GetText(TextDataFormat.UnicodeText); 
     var lines = data.Split(new string[] { "\r\n" }, StringSplitOptions.None); 
     var parameters = GetParmeters(lines); 
     parameters.Reverse(); 

     foreach (var item in lines) 
     { 
      if (item.Trim().Length == 0) 
       continue; 
      if (item.TrimStart().StartsWith("--")) 
       continue; 

      var SQLLine = item; 
      foreach (var p in parameters) 
      { 
       SQLLine = SQLLine.Replace("@" + p.Name, p.FormattedValue); 
      } 
      sb.AppendLine(SQLLine); 
     } 

     Clipboard.SetText(sb.ToString()); 
    } 

    private static List<parameter> GetParmeters(string[] lines) 
    { 
     var parameters = new List<parameter>(); 
     foreach (var item in lines) 
     { 
      var trimed = item.Trim(); 
      if (trimed.StartsWith("-- p__linq__") == false) 
       continue; 

      var colonInd = trimed.IndexOf(':'); 
      if (colonInd == -1) 
       continue; 

      var paramName = trimed.Substring(3, colonInd - 3); 
      var valueStart = colonInd + 3; 
      var valueEnd = trimed.IndexOf('\'', valueStart); 
      if (valueEnd == -1) 
       continue; 

      var value = trimed.Substring(valueStart, valueEnd - valueStart); 

      var typeStart = trimed.IndexOf("(Type = "); 
      if (typeStart == -1) 
       continue; 
      typeStart += 8; 

      var typeEnd = trimed.IndexOf(',', typeStart); 
      if (typeEnd == -1) 
       typeEnd = trimed.IndexOf(')', typeStart); 
      if (typeEnd == -1) 
       continue; 

      var type = trimed.Substring(typeStart, typeEnd - typeStart); 

      var param = new parameter(); 
      param.Name = paramName; 
      param.Value = value; 
      param.Type = type; 

      parameters.Add(param); 
     } 

     return parameters; 
    } 
} 
+0

しかし、バッファリングがクラッシュのイベントで失われたログデータにつながることに注意してください。 –

+0

私は、loggingのdatabase.logメソッドには、失敗したクエリがすでにログに記録されていないという問題があると思います。あなたのポイントは本当ですが、私はこれを本番で使用するつもりはありませんでした。クラッシュがあった場合はtryブロックを追加してください。また、バッファリングをautoflushでファイル出力に変更することもできます。 – Todd

関連する問題