在我的例子中,它是Visual Studio中的Web API项目。当我测试时,API会同时被多次调用。
我正在使用以下内容记录发送到SQL Server的Raw SQL:
context.Database.Log = Console.WriteLine;
当记录SQL时,它会与其他线程上的查询混淆。更具体地说,它通常是混淆的参数。这使得几乎不可能将正确的参数与正确的查询相关联。有时同时调用两次相同的API。
我正在使用异步调用,但这不会导致问题。事实上,在不同的完成线程上存在多个并发Web请求。
我需要准确可靠的日志记录,因此我可以回顾输出窗口并查看SQL。
答案 0 :(得分:1)
您需要按上下文缓冲所有日志消息,然后在处理数据库上下文时写出该缓冲区。
您需要能够挂钩数据库上下文的dispose事件
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (OnDisposed != null) OnDisposed(this, null);
}
public event EventHandler OnDisposed;
然后你需要这个类来管理每个上下文的缓冲
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());
}
}
它应该可以在不保存对LogGroup对象的强引用的情况下工作。但我还没有测试过。此外,您可以直接在上下文中包含此类代码,因此您绝对不需要保存LogGroup引用对象。但这不会那么便携。
在控制器动作功能中使用它:
var db = new MyDbContext();
LogGroup.Log(this, db);
注意,我传递了控制器引用,因此日志可以包含一些额外的上下文信息 - 请求URI。
解读您的日志
现在日志工作,您会发现日志输出中的注释参数很难处理。您通常必须手动将它们更改为正确的SQL参数,但即使这样,也很难使用参数运行较大SQL查询的子部分。
我知道有一两种方法可以让EF输出日志。这些方法确实提供了对参数输出方式的更好控制,但鉴于答案是关于使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;
}
}