【问题标题】:How to get last executed SQL query by SqlConnection?如何通过 SqlConnection 获取上次执行的 SQL 查询?
【发布时间】:2017-10-26 22:41:24
【问题描述】:

我的代码可以访问一个SqlConnection 对象。我无法访问所有其他 ADO.NET 对象,例如 SqlCommandSqlParameter 等。这些其他对象由 Dapper Extensions ORM 使用。

我的应用程序使用 SqlConnection 对象和 Dapper Extensions 方法执行 SQL 查询。 SQL 查询由 Dapper Extensions 自动生成;我无法访问生成的查询。我想记录这个 SQL 查询。

我已经有了我的日志记录模块,我唯一需要的是连接对象执行的最后一个 SQL 查询。

如何获取SqlConnection最后执行的SQL查询?

由于SqlCommand 不可访问,因此无法进行以下操作。

如果我得到底层SqlCommand,我可以使用下面的代码从中构建查询;不幸的是,我无法访问它。

public string GetCommandLogString(IDbCommand command)
{
    string outputText;
    if(command.Parameters.Count == 0)
    {
        outputText = command.CommandText;
    }
    else
    {
        StringBuilder output = new StringBuilder();
        output.Append(command.CommandText);
        output.Append("; ");

        IDataParameter objIDataParameter;
        int parameterCount = command.Parameters.Count;
        for(int i = 0; i < parameterCount; i++)
        {
            objIDataParameter = (IDataParameter)command.Parameters[i];
            output.Append(string.Format("{0} = '{1}'", objIDataParameter.ParameterName, objIDataParameter.Value));

            if(i + 1 < parameterCount)
            {
                output.Append(", ");
            }
        }
        outputText = output.ToString();
    }
    return outputText;
}

【问题讨论】:

    标签: c# logging ado.net sqlconnection


    【解决方案1】:

    我过去使用的一种方法是,当我不想依赖任何外部工具(或缺少工具时,例如使用 MS Access 时)时,使用数据库连接和命令“wrapper” " 类,以便我可以将日志记录添加到它们的任何方法或属性中。

    要使用它,您将要使用的任何连接传递给 WrappedDbConnection 的构造函数,如下所示:

    using (var conn = new WrappedDbConnection(GetMyConnection()))
    {
        // Do work using Dapper here against "conn"
    }
    

    (注意:当调用WrappedDbConnection 实例的Dispose 方法时,该方法将被传递到底层连接,因此您不需要为WrappedDbConnection 使用using,而为WrappedDbConnection 使用单独的using您的连接 - 您只需要一个using,如上所示)。

    下面定义了您需要的两个类。

    请注意,ExecuteNonQueryExecuteReaderExecuteReaderExecuteScalar 方法中有 Console.WriteLine 调用,它们将写出将要执行的查询。您可能希望更改此设置以满足您的要求,以便在查询完成后写出查询,或者您可能希望使用与 Console.Writeline 不同的输出,但这些更改应该足够简单。

    public class WrappedDbConnection : IDbConnection
    {
        private readonly IDbConnection _conn;
        public WrappedDbConnection(IDbConnection connection)
        {
            if (connection == null)
                throw new ArgumentNullException(nameof(connection));
    
            _conn = connection;
        }
    
        public string ConnectionString
        {
            get { return _conn.ConnectionString; }
            set { _conn.ConnectionString = value; }
        }
    
        public int ConnectionTimeout
        {
            get { return _conn.ConnectionTimeout; }
        }
    
        public string Database
        {
            get { return _conn.Database; }
        }
    
        public ConnectionState State
        {
            get { return _conn.State; }
        }
    
        public IDbTransaction BeginTransaction()
        {
            return _conn.BeginTransaction();
        }
    
        public IDbTransaction BeginTransaction(IsolationLevel il)
        {
            return _conn.BeginTransaction(il);
        }
    
        public void ChangeDatabase(string databaseName)
        {
            _conn.ChangeDatabase(databaseName);
        }
    
        public void Close()
        {
            _conn.Close();
        }
    
        public IDbCommand CreateCommand()
        {
            return new WrappedDbCommand(_conn.CreateCommand());
        }
    
        public void Dispose()
        {
            _conn.Dispose();
        }
    
        public void Open()
        {
            _conn.Open();
        }
    }
    
    public class WrappedDbCommand : IDbCommand
    {
        private readonly IDbCommand _cmd;
        public WrappedDbCommand(IDbCommand command)
        {
            if (command == null)
                throw new ArgumentNullException(nameof(command));
    
            _cmd = command;
        }
    
        public string CommandText
        {
            get { return _cmd.CommandText; }
            set { _cmd.CommandText = value; }
        }
    
        public int CommandTimeout
        {
            get { return _cmd.CommandTimeout; }
            set { _cmd.CommandTimeout = value; }
        }
    
        public CommandType CommandType
        {
            get { return _cmd.CommandType; }
            set { _cmd.CommandType = value; }
        }
    
        public IDbConnection Connection
        {
            get { return _cmd.Connection; }
            set { _cmd.Connection = value; }
        }
    
        public IDataParameterCollection Parameters
        {
            get { return _cmd.Parameters; }
        }
    
        public IDbTransaction Transaction
        {
            get { return _cmd.Transaction; }
            set { _cmd.Transaction = value; }
        }
    
        public UpdateRowSource UpdatedRowSource
        {
            get { return _cmd.UpdatedRowSource; }
            set { _cmd.UpdatedRowSource = value; }
        }
    
        public void Cancel()
        {
            _cmd.Cancel();
        }
    
        public IDbDataParameter CreateParameter()
        {
            return _cmd.CreateParameter();
        }
    
        public void Dispose()
        {
            _cmd.Dispose();
        }
    
        public int ExecuteNonQuery()
        {
            Console.WriteLine($"[ExecuteNonQuery] {_cmd.CommandText}");
            return _cmd.ExecuteNonQuery();
        }
    
        public IDataReader ExecuteReader()
        {
            Console.WriteLine($"[ExecuteReader] {_cmd.CommandText}");
            return _cmd.ExecuteReader();
        }
    
        public IDataReader ExecuteReader(CommandBehavior behavior)
        {
            Console.WriteLine($"[ExecuteReader({behavior})] {_cmd.CommandText}");
            return _cmd.ExecuteReader();
        }
    
        public object ExecuteScalar()
        {
            Console.WriteLine($"[ExecuteScalar] {_cmd.CommandText}");
            return _cmd.ExecuteScalar();
        }
    
        public void Prepare()
        {
            _cmd.Prepare();
        }
    }
    

    【讨论】:

    • 这是一个非常好的主意,但我无法让它与 NH5 一起使用,因为它需要 DbConnection 而不是 IDbConnection。继承或包装 DbConnection 没有帮助,因为有一些我不知道如何实现的抽象方法,而从 SqlConnection 继承是不可能的,因为它是密封的。
    • 您可以采用相同的方法,但包装一个 DbConnection 并从 DbConnection 派生(而不是包装和实现 IDbConnection),然后覆盖您需要的方法并调用包装的 DbConnection 以便您不必自己实现它们。像这样:gist.github.com/ProductiveRage/31a92e1470b96ce34e454a73bd8e19c4
    • 哪些外部工具?
    • 如果您使用的是 MS SQL 并希望查看查询和响应,那么您可以使用 SQL Profiler(这就是我在提到外部工具时想到的)。
    猜你喜欢
    • 2023-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-01
    • 2012-11-21
    • 2016-02-11
    相关资源
    最近更新 更多