我正在尝试记录已记录到表的SQL,但是我收到OutOfMemoryException。我知道为什么得到例外,但我不知道如何避免。
ApplicationDBContext:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser> {
public ApplicationDbContext() :
base("IdentityDBContext", false) {
DbInterception.Add(new InsertUpdateInterceptor());
}
}
WebDataEntities:
public WebDataEntities()
: base("name=WebDataEntities")
{
}
日志类:
public class InsertUpdateInterceptor : IDbCommandInterceptor {
public virtual void NonQueryExecuting(
DbCommand command, DbCommandInterceptionContext<int> interceptionContext) {
logCommand(command);
}
public virtual void ReaderExecuting(
DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) {
// this will capture all SELECT queries if you care about them..
// however it also captures INSERT statements as well
logCommand(command);
}
public virtual void ScalarExecuting(
DbCommand command, DbCommandInterceptionContext<object> interceptionContext) {
logCommand(command);
}
private void logCommand(DbCommand dbCommand) {
StringBuilder commandText = new StringBuilder();
commandText.AppendLine("-- New statement generated: " + System.DateTime.Now.ToString());
commandText.AppendLine();
// as the command has a bunch of parameters, we need to declare
// those parameters here so the SQL will execute properly
foreach (DbParameter param in dbCommand.Parameters) {
var sqlParam = (SqlParameter)param;
commandText.AppendLine(String.Format("DECLARE {0} {1} {2}",
sqlParam.ParameterName,
sqlParam.SqlDbType.ToString().ToLower(),
getSqlDataTypeSize(sqlParam)));
var escapedValue = sqlParam.SqlValue.ToString().Replace("'", "''");
commandText.AppendLine(String.Format("SET {0} = '{1}'", sqlParam.ParameterName, escapedValue));
commandText.AppendLine();
}
commandText.AppendLine(dbCommand.CommandText);
commandText.AppendLine("GO");
commandText.AppendLine();
commandText.AppendLine();
using(var context = new WebDataEntities()) {
context.tbl_FSV_AspNetIdentity_SQL_Log.Add(new tbl_FSV_AspNetIdentity_SQL_Log { Sql = commandText.ToString() });
context.SaveChanges();
}
//System.IO.File.AppendAllText("outputfile.sql", commandText.ToString());
}
private string getSqlDataTypeSize(SqlParameter param) {
if (param.Size == 0) {
return "";
}
if (param.Size == -1) {
return "(MAX)";
}
return "(" + param.Size + ")";
}
// To implement the IDbCommandInterceptor interface you need to also implement these methods like so
public void NonQueryExecuted(
DbCommand command, DbCommandInterceptionContext<int> interceptionContext) {
}
public void ReaderExecuted(
DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) {
}
public void ScalarExecuted(
DbCommand command, DbCommandInterceptionContext<object> interceptionContext) {
}
}
我收到异常,是因为当我尝试记录SQL时,它陷入了试图记录sql的循环中,而该日志应该将sql记录到DB中。但是我使用两个不同的上下文,所以为什么要尝试两次记录SQL?
答案 0 :(得分:2)
关于EF拦截上下文的两个重要事实:
因此,一行...
DbInterception.Add(new InsertUpdateInterceptor());
每次创建新的InsertUpdateInterceptor
时,只需在拦截上下文中添加一个新的ApplicationDbContext
实例,就会为您的应用程序中的每种上下文类型这样做。
这就是WebDataEntities
中的任何命令都会导致无限循环的原因:它们自己记录。
解决方案是使用简单的ADO.Net代码(SqlCommand
等)在命令拦截器或其他类型的ORM(例如LINQ-to-SQL,现在仍然如此)中将任何内容记录到数据库中尽管已过时,但仍受支持)。
DbInterception.Add(new InsertUpdateInterceptor())
行应在应用程序启动时被调用一次。在ApplicationDbContext
中执行它并不意味着它仅适用于该实例。这是一个静态调用,因此它不知道其环境。
答案 1 :(得分:0)
盖特钉牢了。我只是在第二次通读中注意到了这一点。要将拦截器添加到单个上下文中:
public ApplicationDbContext() :
base("IdentityDBContext", false)
{
this.AddInterceptor(new InsertUpdateInterceptor());
}