Author: rgrabowski
Date: Tue Jul  5 19:37:25 2005
New Revision: 209392

URL: http://svn.apache.org/viewcvs?rev=209392&view=rev
Log:
IDataReaderProxy logs result data. Requires at least v1.1.5.0 of 
Castle.DynamicProxy (released 7/2/2005). Code is not referenced by project. 
IDbCommandProxy works correctly when the underlying IDbCommand object has its 
Connection and/or Transaction property set. SqlMapSession.cs contains commented 
out code showing where the IDbCommandProxy should be created.

Added:
    ibatis/trunk/cs/mapper/IBatisNet.Common/Logging/IDataReaderProxy.cs
    ibatis/trunk/cs/mapper/IBatisNet.Common/Logging/IDbCommandProxy.cs
Modified:
    ibatis/trunk/cs/mapper/IBatisNet.DataMapper/SqlMapSession.cs

Added: ibatis/trunk/cs/mapper/IBatisNet.Common/Logging/IDataReaderProxy.cs
URL: 
http://svn.apache.org/viewcvs/ibatis/trunk/cs/mapper/IBatisNet.Common/Logging/IDataReaderProxy.cs?rev=209392&view=auto
==============================================================================
--- ibatis/trunk/cs/mapper/IBatisNet.Common/Logging/IDataReaderProxy.cs (added)
+++ ibatis/trunk/cs/mapper/IBatisNet.Common/Logging/IDataReaderProxy.cs Tue Jul 
 5 19:37:25 2005
@@ -0,0 +1,224 @@
+
+#region Apache Notice
+/*****************************************************************************
+ * $Header: $
+ * $Revision: $
+ * $Date: $
+ * 
+ * iBATIS.NET Data Mapper
+ * Copyright (C) 2004 - Gilles Bayon
+ *  
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ 
********************************************************************************/
+#endregion
+
+using System;
+using System.Collections;
+using System.Data;
+using Castle.DynamicProxy;
+
+namespace IBatisNet.Common.Logging
+{
+       /// <summary>
+       /// Requires at least v1.1.5.0 of Castle.DynamicProxy
+       /// </summary>
+       /// <remarks>
+       /// It is common for the user to expect a DataReader with zero or one 
DataRecords:
+       /// 
+       /// <code>
+       /// if (dr.Read())
+       /// {
+       ///  processDataReader(dr);
+       /// }
+       /// </code>
+       ///  
+       /// There is no way to know when the user is done processing the 
DataReader. There may be a lot more
+       /// code executed before 'dr' is Disposed() or Closed(). When Read() is 
called, we print out the
+       /// column headers as well as the data from the first DataRecord.
+       /// 
+       /// DataReaders with 2 or more DataRecords benefit from a simple cache 
in which the code to generate the
+       /// log message does not make a call to the underlying _dataReader if 
the user has already requested the
+       /// value. If a DataRecord contains 50 columns and the user accessed 45 
of those, the code to produce the
+       /// log message for the DataRecord will only need to make 5 calls to 
GetValue(i) for the missing columns
+       /// rather than iterating through all the columns and calling 
GetValue(i) to retrieve their value. One
+       /// could argue that unaccessed columns should not printed to the log. 
Since IBatisNet now supports
+       /// discriminator nodes in resultMaps, its important to display 
everything in the DataRecord
+       /// or else log messages may contain column values that are mis-aligned 
with column headers (i.e. some
+       /// log messages may have more column values than others when 
discriminators are in use).
+       ///
+       /// _isFirstRow and _isSecondRow could be replaced with:
+       ///
+       /// <code>
+       /// private int _dataRecordsProcessed = 0;
+       /// </code>
+       ///                             
+       /// We don't need to maintain a counter for the number of rows 
processed. We are only concerned with
+       /// the first and second DataRecords.
+       /// </remarks>
+       public class IDataReaderProxy : IInterceptor
+       {
+               private IDataReader _dataReader = null;
+               private bool _isFirstRow = true;
+               private bool _isSecondRow = false;
+               private object[] _columnValuesCache = null;
+               private static readonly ILog _logger = 
LogManager.GetLogger("System.Data.IDataReader");
+               private static readonly ArrayList _getters;
+
+               static IDataReaderProxy()
+               {
+                       // the following methods have a 0th argument of type int
+                       _getters = new ArrayList(15);
+                       _getters.Add("GetInt32");
+                       _getters.Add("GetValue");
+                       _getters.Add("GetBytes");
+                       _getters.Add("GetByte");
+                       _getters.Add("GetDecimal");
+                       _getters.Add("GetInt64");
+                       _getters.Add("GetDouble");
+                       _getters.Add("GetBoolean");
+                       _getters.Add("GetGuid");
+                       _getters.Add("GetDateTime");
+                       _getters.Add("GetFloat");
+                       _getters.Add("GetChars");
+                       _getters.Add("GetString");
+                       _getters.Add("GetChar");
+                       _getters.Add("GetIn16");
+               }
+       
+               internal IDataReaderProxy(IDataReader dataReader)
+               {
+                       _dataReader = dataReader;
+               }
+
+               public static IDataReader NewInstance(IDataReader dataReader)
+               {
+                       IInterceptor handler = new IDataReaderProxy(dataReader);
+
+                       object proxyDataReader = new 
ProxyGenerator().CreateProxy(typeof(IDataReader), handler, dataReader);
+
+                       return (IDataReader)proxyDataReader;
+               }
+
+               public object Intercept(IInvocation invocation, params object[] 
arguments)
+               {
+                       if (invocation.Method.Name == "Read")
+                       {
+                               #region Read()
+                               if (_isFirstRow)
+                               {
+                                       System.Text.StringBuilder sb = new 
System.Text.StringBuilder();
+                               
+                                       for (int 
i=0;i<_dataReader.FieldCount;i++)
+                                       {
+                                               
sb.Append(_dataReader.GetName(i) + ", ");
+                                       }
+
+                                       _logger.Debug("Headers for \"" + 
_dataReader.GetHashCode() + "\": [" + sb.ToString(0, sb.Length - 2) + "]");
+
+                                       _isFirstRow = false;
+
+                                       // advance to the next DataRecord
+                                       // 
(bool)invocation.Method.Invoke(_dataReader, arguments);
+                                       bool read = _dataReader.Read();
+
+                                       if (read == true)
+                                       {
+                                               // log values of the first 
(non-header) DataRecord
+                                               _columnValuesCache = new 
object[_dataReader.FieldCount];
+                                               logColumnValues();
+
+                                               // if the user has chosen the 
'while (dr.Read())' syntax, tell the second DataRecord
+                                               // that its contents have 
already been logged
+                                               _isSecondRow = true;
+                                       }
+
+                                       return read;
+                               }
+                               else
+                               {
+                                       if (_isSecondRow)
+                                       {
+                                               // we've already called 
logColumnValues()
+                                               _isSecondRow = false;
+                                       }
+                                       else
+                                       {
+                                               // the user is most likely 
iterating through the records using the 'while (dr.Read())' notation
+                                               logColumnValues();
+                                       }
+                                       
+                                       _columnValuesCache = new 
object[_dataReader.FieldCount];
+
+                                       return 
invocation.Method.Invoke(_dataReader, arguments);
+                               }
+                               #endregion
+                       }
+                       else if (_getters.Contains(invocation.Method.Name))
+                       {
+                               #region GetInt32(int), GetString(int), 
GetDateTime(int), etc.
+                               // Cache repeated lookups: checking for null is 
probably faster than calling Invoke ???
+                               object value = 
_columnValuesCache[(int)arguments[0]];
+                               if (value == null)
+                               {
+                                       value = 
invocation.Method.Invoke(_dataReader, arguments);
+                                       _columnValuesCache[(int)arguments[0]] = 
value;
+                               }
+                               else
+                               {
+                                       // If someone wrote a custom type 
handler that makes repeated calls to GetXXXXX(int), we only make one call to 
Invoke
+                                       // _logger.Debug("Using cached value 
for column: [" + (int)arguments[0] + "]");
+                               }
+                               return value;
+                               #endregion
+                       }
+                       else
+                       {
+                               return invocation.Method.Invoke(_dataReader, 
arguments);
+                       }
+               }
+
+               private void logColumnValues()
+               {
+                       System.Text.StringBuilder sb = new 
System.Text.StringBuilder();
+
+                       for (int i=0;i<_columnValuesCache.Length;i++)
+                       {
+                               object value = _columnValuesCache[i];
+
+                               if (value == null)
+                               {
+                                       // _logger.Debug("Could not find 
existing value for column [" + i + "]");
+
+                                       value = _dataReader.GetValue(i);
+
+                                       if (value == DBNull.Value)
+                                       {
+                                               value = "NULL";
+                                       }
+                               }
+
+                               sb.Append(value + ", ");
+                       }
+
+                       // remove trailing comma and space
+                       if (sb.Length > 2)
+                       {
+                               sb.Length -= 2;
+                       }
+
+                       _logger.Debug("Results for \"" + 
_dataReader.GetHashCode() + "\": [" + sb.ToString() + "]");
+               }
+       }
+}
\ No newline at end of file

Added: ibatis/trunk/cs/mapper/IBatisNet.Common/Logging/IDbCommandProxy.cs
URL: 
http://svn.apache.org/viewcvs/ibatis/trunk/cs/mapper/IBatisNet.Common/Logging/IDbCommandProxy.cs?rev=209392&view=auto
==============================================================================
--- ibatis/trunk/cs/mapper/IBatisNet.Common/Logging/IDbCommandProxy.cs (added)
+++ ibatis/trunk/cs/mapper/IBatisNet.Common/Logging/IDbCommandProxy.cs Tue Jul  
5 19:37:25 2005
@@ -0,0 +1,80 @@
+
+#region Apache Notice
+/*****************************************************************************
+ * $Header: $
+ * $Revision: $
+ * $Date: $
+ * 
+ * iBATIS.NET Data Mapper
+ * Copyright (C) 2004 - Gilles Bayon
+ *  
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * 
+ 
********************************************************************************/
+#endregion
+
+using System.Data;
+using Castle.DynamicProxy;
+
+namespace IBatisNet.Common.Logging
+{
+       /// <summary>
+       /// 
+       /// </summary>
+       public class IDbCommandProxy : IInterceptor
+       {
+               private IDbCommand _command = null;
+               private static readonly ILog _dataReaderLogger = 
LogManager.GetLogger("System.Data.IDataReader");
+
+               internal IDbCommandProxy(IDbCommand command)
+               {
+                       _command = command;
+               }
+
+               /// <summary>
+               /// 
+               /// </summary>
+               /// <param name="command"></param>
+               /// <returns></returns>
+               public static IDbCommand NewInstance(IDbCommand command)
+               {
+                       IInterceptor handler = new IDbCommandProxy(command);
+
+                       object proxyCommand = new 
ProxyGenerator().CreateProxy(typeof(IDbCommand), handler, command);
+
+                       return (IDbCommand) proxyCommand;
+               }
+
+               /// <summary>
+               /// 
+               /// </summary>
+               /// <param name="invocation"></param>
+               /// <param name="arguments"></param>
+               /// <returns></returns>
+               public object Intercept(IInvocation invocation, params object[] 
arguments)
+               {
+                       object returnValue = invocation.Method.Invoke(_command, 
arguments);
+
+                       if (invocation.Method.Name == "ExecuteReader")
+                       {
+                               if (_dataReaderLogger.IsDebugEnabled)
+                               {
+                                       returnValue = 
IDataReaderProxy.NewInstance((IDataReader)returnValue);
+                               }
+                       }
+
+                       return returnValue;
+               }
+       }
+}

Modified: ibatis/trunk/cs/mapper/IBatisNet.DataMapper/SqlMapSession.cs
URL: 
http://svn.apache.org/viewcvs/ibatis/trunk/cs/mapper/IBatisNet.DataMapper/SqlMapSession.cs?rev=209392&r1=209391&r2=209392&view=diff
==============================================================================
--- ibatis/trunk/cs/mapper/IBatisNet.DataMapper/SqlMapSession.cs (original)
+++ ibatis/trunk/cs/mapper/IBatisNet.DataMapper/SqlMapSession.cs Tue Jul  5 
19:37:25 2005
@@ -411,6 +411,11 @@
                                }
                        }
 
+//                     if (_logger.IsDebugEnabled)
+//                     {
+//                             command = IDbCommandProxy.NewInstance(command);
+//                     }
+
                        return command;
                }
 


Reply via email to