实现消息截获,我们熟知的方法有将需要监控的类型从 ContextBandObject类派生,从而用消息截获器实行对 对象的横向监控。 以下是另一种简单的实行对象调用的监控AOP技术实现;
1. 调用代码例子
2. 源代码实现
/// <summary> /// Executes both standalone and transacted SQL queries described by strongly-typed interface. /// </summary> /// <typeparam name="T">Interface with function prototypes</typeparam> /// <exception cref="System.ArgumentException">Thrown when there is an error in prototype description.</exception> /// <exception cref="System.Data.SqlClient.SqlException">Error from SQL Server while executing query.</exception> public sealed class SqlQuery<T> : RealProxy where T : class { private readonly SqlConnection connection; private readonly SqlTransaction transaction; private readonly bool closeConnection; public static T Create(SqlConnection connection) { return (new SqlQuery<T>(connection, null)).GetTransparentProxy() as T; } public static T Create(SqlTransaction transaction) { return (new SqlQuery<T>(transaction.Connection, transaction)).GetTransparentProxy() as T; } public static T Create(string connectionString) { return (new SqlQuery<T>(connectionString)).GetTransparentProxy() as T; } private SqlQuery(SqlConnection conn, SqlTransaction tran) : base(typeof(T)) { connection = conn; transaction = tran; closeConnection = false; } private SqlQuery(string connectionString) : base(typeof(T)) { connection = new SqlConnection(connectionString); transaction = null; closeConnection = true; } [DebuggerStepThrough] public override IMessage Invoke(IMessage msg) { IMethodCallMessage method = msg as IMethodCallMessage; if (method == null) throw new ArgumentException("Failed to cast IMessage to IMethodCallMessage"); if (method.HasVarArgs) throw new ArgumentException("Variable arguments are not supprted by SQL Server"); MethodInfo mi = (MethodInfo)method.MethodBase; Type t = mi.ReturnType; bool Void = (t == null || t.FullName == "System.Void"); SqlParameterCacheEntry entry = SqlParameterCache.GetEntry(mi); using (SqlCommand cmd = connection.CreateCommand()) { cmd.Transaction = transaction; cmd.CommandType = entry.CommandType; cmd.CommandText = entry.CommandText; SqlParameter[] parameters = entry.GetParameters(); int i = -1; int recordsAffected = -1; int argCount = method.ArgCount; object recordsSelected = null; foreach (SqlParameter p in parameters) { cmd.Parameters.Add(p); if (entry.ReturnsValue && p.ParameterName.StartsWith("@")) continue; if (++i >= argCount) throw new ArgumentException("Too many parameters passed"); if (!p.ParameterName.Equals(method.GetArgName(i), StringComparison.InvariantCultureIgnoreCase)) throw new ArgumentException("Parameter name \'" + p.ParameterName + "\' does not match defined name", method.GetArgName(i)); if (p.Direction == ParameterDirection.ReturnValue) continue; if (p.Direction == ParameterDirection.Output) continue; object value = method.Args[i]; if (value is IDbDataParameter) { IDbDataParameter dbp = (IDbDataParameter)value; p.Value = (dbp.Value == null) ? DBNull.Value : dbp.Value; } else p.Value = (value == null) ? DBNull.Value : value; } if (i < argCount - 1) throw new ArgumentException("Not enough parameters passed"); List<object> outParams = new List<object>(method.ArgCount); try { if (closeConnection) connection.Open(); if (entry.ReturnsRecordset) { bool singleRow = false; CommandBehavior opts = CommandBehavior.Default; if (t == typeof(DataSet)) recordsSelected = new DataSet(); else if (t == typeof(DataTable)) opts = CommandBehavior.SingleResult; else if (t == typeof(SingleRowDataTable)) { opts = CommandBehavior.SingleResult | CommandBehavior.SingleRow; singleRow = true; } else throw new ArgumentException("DataSet, DataTable or SingleRowDataTable must be returned", "Return value"); using (SqlDataReader rd = cmd.ExecuteReader(opts)) { if (!singleRow) { do { DataColumn[] columns = GetColumnInfo(rd); object[] values = new object[columns.Length]; DataTable table = new DataTable(); table.BeginLoadData(); table.Columns.AddRange(columns); while (rd.Read()) { rd.GetValues(values); table.Rows.Add(values); } table.EndLoadData(); table.AcceptChanges(); if (recordsSelected != null && recordsSelected is DataSet) ((DataSet)recordsSelected).Tables.Add(table); else { recordsSelected = table; break; } } while (rd.NextResult()); } else { DataColumn[] columns = GetColumnInfo(rd); object[] values = new object[columns.Length]; SingleRowDataTable table = new SingleRowDataTable(); table.BeginLoadData(); table.Columns.AddRange(columns); if (rd.Read()) { rd.GetValues(values); table.Rows.Add(values); } table.EndLoadData(); table.AcceptChanges(); recordsSelected = table; } rd.Close(); } } else { recordsAffected = cmd.ExecuteNonQuery(); } for (int j = 0; j < method.ArgCount; j++) { SqlParameter p = cmd.Parameters[method.GetArgName(j)]; outParams.Add(((p.Direction == ParameterDirection.Input || p.Value == DBNull.Value) ? null : p.Value)); } } finally { if (closeConnection) connection.Close(); } if (entry.ReturnsRecordset) return new ReturnMessage(recordsSelected, outParams.ToArray(), outParams.Count, method.LogicalCallContext, method); if (entry.ReturnsValue) { object value = cmd.Parameters[entry.ReturnParameter].Value; if (value == DBNull.Value) value = null; return new ReturnMessage(value, outParams.ToArray(), outParams.Count, method.LogicalCallContext, method); } else if (Void) return new ReturnMessage(null, outParams.ToArray(), outParams.Count, method.LogicalCallContext, method); else if (t == typeof(int)) return new ReturnMessage(recordsAffected, outParams.ToArray(), outParams.Count, method.LogicalCallContext, method); throw new ArgumentException("Method must return SqlReturn-marked value, DataSet, DataTable, SingleRowDataTable, Int or Void", "Return value"); } }