[ http://issues.apache.org/jira/browse/IBATISNET-119?page=all ]
Gilles Bayon resolved IBATISNET-119:
------------------------------------
Fix Version: DataMapper 1.3
Resolution: Fixed
In SVN
> CacheKey.Equals(object)/HashCode issue still a problem (was IBATISNET-118)
> --------------------------------------------------------------------------
>
> Key: IBATISNET-119
> URL: http://issues.apache.org/jira/browse/IBATISNET-119
> Project: iBatis for .NET
> Type: Bug
> Components: DataMapper
> Versions: DataMapper 1.2.1
> Environment: Windows
> Reporter: Thomas Tannahill
> Assignee: Gilles Bayon
> Fix For: DataMapper 1.3
>
> The fix for IBATISNET-118 does not address the issue (just hides it in one
> particular instance).
> The issue is that you can not use HashCodes to determine object equality.
> Unique objects do not always have unique HashCodes (no matter how hard you
> try). There just aren't enough hashcodes to go around :)
> I understand why my previous 'patch' wasn't accepted (didn't expect it to
> be). Since we are not using an object's own "GetHashCode()" to get it's
> hashcode, we can not use it's own "Equals" to determine equality. So we
> really have to do is create a new method on ObjectProbe that compares two
> objects in much the same way that ObjectProbe generates hashcodes. I was
> just too lazy to write it yesterday :)
> Here is an updated unit test and a potential fix...
> --- BEGIN UNIT TEST ---
> using IBatisNet.DataMapper;
> using IBatisNet.DataMapper.TypeHandlers;
> using NUnit.Framework;
> namespace IBatisNet.DataMapper.Test.NUnit.SqlMapTests
> {
> [TestFixture]
> public class CacheKeyTest
> {
> [Test]
> public void
> ShouldNotConsider1LAndNegative9223372034707292159LToBeEqual()
> {
> // old version of ObjectProbe gave TestClass based on
> these longs the same HashCode
> DoTestClassEquals(1L, -9223372034707292159L);
> }
> [Test]
> public void
> ShouldNotConsider1LAndNegative9223372036524971138LToBeEqual()
> {
> // current version of ObjectProbe gives TestClass based
> on these longs the same HashCode
> DoTestClassEquals(1L, -9223372036524971138L);
> }
> private static void DoTestClassEquals(long firstLong, long
> secondLong)
> {
> TypeHandlerFactory factory = new TypeHandlerFactory();
>
> // Two cache keys are equal except for the parameter.
> CacheKey key = new CacheKey(factory, "STATEMENT",
> "SQL", new TestClass(firstLong), new string[] {"AProperty"}, 0, 0,
> CacheKeyType.Object);
> CacheKey aDifferentKey = new CacheKey(factory,
> "STATEMENT", "SQL", new TestClass(secondLong), new string[] {"AProperty"}, 0,
> 0, CacheKeyType.Object);
>
> Assert.IsFalse(aDifferentKey.Equals(key)); // should
> not be equal.
> }
> private class TestClass
> {
> private long _property = long.MinValue;
> public TestClass(long aProperty)
> {
> _property = aProperty;
> }
> public long AProperty
> {
> get { return _property; }
> set { _property = value; }
> }
> }
> }
> }
> --- END UNIT TEST ---
> FIX (not sure if this will break other parts of the system... but it
> shouldn't):
> --- BEGIN ObjectProbe ---
> #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.Reflection;
> using IBatisNet.Common.Exceptions;
> namespace IBatisNet.Common.Utilities.Objects
> {
> /// <summary>
> /// Description résumée de ObjectProbe.
> /// </summary>
> public class ObjectProbe
> {
> private static ArrayList _simpleTypeMap = new ArrayList();
> static ObjectProbe()
> {
> _simpleTypeMap.Add(typeof(string));
> _simpleTypeMap.Add(typeof(Byte));
> _simpleTypeMap.Add(typeof(Int16));
> _simpleTypeMap.Add(typeof(char));
> _simpleTypeMap.Add(typeof(Int32));
> _simpleTypeMap.Add(typeof(Int64));
> _simpleTypeMap.Add(typeof(Single));
> _simpleTypeMap.Add(typeof(Double));
> _simpleTypeMap.Add(typeof(Boolean));
> _simpleTypeMap.Add(typeof(DateTime));
> _simpleTypeMap.Add(typeof(Decimal));
> //
> _simpleTypeMap.Add(typeof(Hashtable));
> //
> _simpleTypeMap.Add(typeof(SortedList));
> //
> _simpleTypeMap.Add(typeof(ArrayList));
> //
> _simpleTypeMap.Add(typeof(Array));
> //
> simpleTypeMap.Add(LinkedList.class);
> //
> simpleTypeMap.Add(HashSet.class);
> //
> simpleTypeMap.Add(TreeSet.class);
> // simpleTypeMap.Add(Vector.class);
> //
> simpleTypeMap.Add(Hashtable.class);
> _simpleTypeMap.Add(typeof(SByte));
> _simpleTypeMap.Add(typeof(UInt16));
> _simpleTypeMap.Add(typeof(UInt32));
> _simpleTypeMap.Add(typeof(UInt64));
> _simpleTypeMap.Add(typeof(IEnumerator));
> }
> /// <summary>
> /// Returns an array of the readable properties names exposed
> by an object
> /// </summary>
> /// <param name="obj">The object</param>
> /// <returns>The properties name</returns>
> public static string[] GetReadablePropertyNames(object obj)
> {
> return
> ReflectionInfo.GetInstance(obj.GetType()).GetReadablePropertyNames();
> }
>
> /// <summary>
> /// Returns an array of the writeable properties name exposed
> by a object
> /// </summary>
> /// <param name="obj">The object</param>
> /// <returns>The properties name</returns>
> public static string[] GetWriteablePropertyNames(object obj)
> {
> return
> ReflectionInfo.GetInstance(obj.GetType()).GetWriteablePropertyNames();
> }
> /// <summary>
> /// Returns the type that the set expects to receive as a
> parameter when
> /// setting a property value.
> /// </summary>
> /// <param name="obj">The object to check</param>
> /// <param name="propertyName">The name of the property</param>
> /// <returns>The type of the property</returns>
> private static Type GetPropertyTypeForSetter(object obj, string
> propertyName)
> {
> Type type = obj.GetType();
> if (obj is IDictionary)
> {
> IDictionary map = (IDictionary) obj;
> object value = map[propertyName];
> if (value == null)
> {
> type = typeof(object);
> }
> else
> {
> type = value.GetType();
> }
> }
> else
> {
> if (propertyName.IndexOf('.') > -1)
> {
> StringTokenizer parser = new
> StringTokenizer(propertyName, ".");
> IEnumerator enumerator =
> parser.GetEnumerator();
> while (enumerator.MoveNext())
> {
> propertyName =
> (string)enumerator.Current;
> type =
> ReflectionInfo.GetInstance(type).GetSetterType(propertyName);
> }
> }
> else
> {
> type =
> ReflectionInfo.GetInstance(type).GetSetterType(propertyName);
> }
> }
> return type;
> }
> /// <summary>
> /// Returns the type that the set expects to receive as a
> parameter when
> /// setting a property value.
> /// </summary>
> /// <param name="type">The type to check</param>
> /// <param name="propertyName">The name of the property</param>
> /// <returns>The type of the property</returns>
> private static Type GetPropertyTypeForSetter(Type type, string
> propertyName)
> {
> if (propertyName.IndexOf('.') > -1)
> {
> StringTokenizer parser = new
> StringTokenizer(propertyName, ".");
> IEnumerator enumerator = parser.GetEnumerator();
> while (enumerator.MoveNext())
> {
> propertyName =
> (string)enumerator.Current;
> type =
> ReflectionInfo.GetInstance(type).GetSetterType(propertyName);
> }
> }
> else
> {
> type =
> ReflectionInfo.GetInstance(type).GetSetterType(propertyName);
> }
> return type;
> }
> /// <summary>
> /// Returns the type that the get expects to receive as a
> parameter when
> /// setting a property value.
> /// </summary>
> /// <param name="obj">The object to check</param>
> /// <param name="propertyName">The name of the property</param>
> /// <returns>The type of the property</returns>
> public static Type GetPropertyTypeForGetter(object obj, string
> propertyName)
> {
> Type type = obj.GetType();
> if (obj is IDictionary)
> {
> IDictionary map = (IDictionary) obj;
> object value = map[propertyName];
> if (value == null)
> {
> type = typeof(object);
> }
> else
> {
> type = value.GetType();
> }
> }
> else
> {
> if (propertyName.IndexOf('.') > -1)
> {
> StringTokenizer parser = new
> StringTokenizer(propertyName, ".");
> IEnumerator enumerator =
> parser.GetEnumerator();
> while (enumerator.MoveNext())
> {
> propertyName =
> (string)enumerator.Current;
> type =
> ReflectionInfo.GetInstance(type).GetGetterType(propertyName);
> }
> }
> else
> {
> type =
> ReflectionInfo.GetInstance(type).GetGetterType(propertyName);
> }
> }
> return type;
> }
> /// <summary>
> /// Returns the type that the get expects to receive as a
> parameter when
> /// setting a property value.
> /// </summary>
> /// <param name="type">The type to check</param>
> /// <param name="propertyName">The name of the property</param>
> /// <returns>The type of the property</returns>
> public static Type GetPropertyTypeForGetter(Type type, string
> propertyName)
> {
> if (propertyName.IndexOf('.') > -1)
> {
> StringTokenizer parser = new
> StringTokenizer(propertyName, ".");
> IEnumerator enumerator = parser.GetEnumerator();
> while (enumerator.MoveNext())
> {
> propertyName =
> (string)enumerator.Current;
> type =
> ReflectionInfo.GetInstance(type).GetGetterType(propertyName);
> }
> }
> else
> {
> type =
> ReflectionInfo.GetInstance(type).GetGetterType(propertyName);
> }
> return type;
> }
> private static object GetArrayProperty(object obj, string
> indexedName)
> {
> object value = null;
> try
> {
> int startIndex = indexedName.IndexOf("[");
> int length = indexedName.IndexOf("]");
> string name = indexedName.Substring(0,
> startIndex);
> string index = indexedName.Substring(
> startIndex+1, length-(startIndex+1));
> int i = System.Convert.ToInt32(index);
>
> if (name.Length > 0)
> {
> value = GetProperty(obj, name);
> }
> else
> {
> value = obj;
> }
> if (value is IList)
> {
> value = ((IList) value)[i];
> }
> else
> {
> throw new ProbeException("The '" + name
> + "' property of the " + obj.GetType().Name + " class is not a List or
> Array.");
> }
> }
> catch (ProbeException pe)
> {
> throw pe;
> }
> catch(Exception e)
> {
> throw new ProbeException("Error getting ordinal
> value from .net object. Cause" + e.Message, e);
> }
> return value;
> }
> /// <summary>
> ///
> /// </summary>
> /// <param name="obj"></param>
> /// <param name="propertyName"></param>
> /// <returns></returns>
> protected static object GetProperty(object obj, string
> propertyName)
> {
> ReflectionInfo reflectionCache =
> ReflectionInfo.GetInstance(obj.GetType());
> try
> {
> object value = null;
> if (propertyName.IndexOf("[") > -1)
> {
> value = GetArrayProperty(obj,
> propertyName);
> }
> else
> {
> if (obj is IDictionary)
> {
> value = ((IDictionary)
> obj)[propertyName];
> }
> else
> {
> PropertyInfo propertyInfo =
> reflectionCache.GetGetter(propertyName);
> if (propertyInfo == null)
> {
> throw new
> ProbeException("No Get method for property " + propertyName + " on instance
> of " + obj.GetType().Name);
> }
> try
> {
> value =
> propertyInfo.GetValue(obj, null);
> }
> catch (ArgumentException ae)
> {
> throw new
> ProbeException(ae);
> }
> catch (TargetException t)
> {
> throw new
> ProbeException(t);
> }
> catch
> (TargetParameterCountException tp)
> {
> throw new
> ProbeException(tp);
> }
> catch (MethodAccessException
> ma)
> {
> throw new
> ProbeException(ma);
> }
>
> }
> }
> return value;
> }
> catch (ProbeException pe)
> {
> throw pe;
> }
> catch(Exception e)
> {
> throw new ProbeException("Could not Set
> property '" + propertyName + "' for " + obj.GetType().Name + ". Cause: " +
> e.Message, e);
> }
> }
> private static void SetArrayProperty(object obj, string
> indexedName, object value)
> {
> try
> {
> int startIndex = indexedName.IndexOf("[");
> int length = indexedName.IndexOf("]");
> string name = indexedName.Substring(0,
> startIndex);
> string index = indexedName.Substring(
> startIndex+1, length-(startIndex+1));
> int i = System.Convert.ToInt32(index);
>
> object list = null;
> if (name.Length > 0)
> {
> list = GetProperty(obj, name);
> }
> else
> {
> list = obj;
> }
> if (list is IList)
> {
> ((IList) list)[i] = value;
> }
> else
> {
> throw new ProbeException("The '" + name
> + "' property of the " + obj.GetType().Name + " class is not a List or
> Array.");
> }
> }
> catch (ProbeException pe)
> {
> throw pe;
> }
> catch (Exception e)
> {
> throw new ProbeException("Error getting ordinal
> value from .net object. Cause" + e.Message, e);
> }
> }
> /// <summary>
> ///
> /// </summary>
> /// <param name="obj"></param>
> /// <param name="propertyName"></param>
> /// <param name="propertyValue"></param>
> protected static void SetProperty(object obj, string
> propertyName, object propertyValue)
> {
> ReflectionInfo reflectionCache =
> ReflectionInfo.GetInstance(obj.GetType());
> try
> {
> if (propertyName.IndexOf("[") > -1)
> {
> SetArrayProperty(obj, propertyName,
> propertyValue);
> }
> else
> {
> if (obj is IDictionary)
> {
> ((IDictionary)
> obj)[propertyName] = propertyValue;
> }
> else
> {
> PropertyInfo propertyInfo =
> reflectionCache.GetSetter(propertyName);
>
> if (propertyInfo == null)
> {
> throw new
> ProbeException("No Set method for property " + propertyName + " on instance
> of " + obj.GetType().Name);
> }
> try
> {
>
> propertyInfo.SetValue(obj, propertyValue, null);
> }
> catch (ArgumentException ae)
> {
> throw new
> ProbeException(ae);
> }
> catch (TargetException t)
> {
> throw new
> ProbeException(t);
> }
> catch
> (TargetParameterCountException tp)
> {
> throw new
> ProbeException(tp);
> }
> catch (MethodAccessException
> ma)
> {
> throw new
> ProbeException(ma);
> }
>
> }
> }
> }
> catch (ProbeException pe)
> {
> throw pe;
> }
> catch (Exception e)
> {
> throw new ProbeException("Could not Get
> property '" + propertyName + "' for " + obj.GetType().Name + ". Cause: " +
> e.Message, e);
> }
> }
> /// <summary>
> /// Return the specified property on an object.
> /// </summary>
> /// <param name="obj">The Object on which to invoke the
> specified property.</param>
> /// <param name="propertyName">The name of the property.</param>
> /// <returns>An Object representing the return value of the
> invoked property.</returns>
> public static object GetPropertyValue(object obj, string
> propertyName)
> {
> if (propertyName.IndexOf('.') > -1)
> {
> StringTokenizer parser = new
> StringTokenizer(propertyName, ".");
> IEnumerator enumerator = parser.GetEnumerator();
> object value = obj;
> string token = null;
> while (enumerator.MoveNext())
> {
> token = (string)enumerator.Current;
> value = GetProperty(value, token);
> if (value == null)
> {
> break;
> }
> }
> return value;
> }
> else
> {
> return GetProperty(obj, propertyName);
> }
> }
> /// <summary>
> /// Set the specified property on an object
> /// </summary>
> /// <param name="obj">The Object on which to invoke the
> specified property.</param>
> /// <param name="propertyName">The name of the property to
> set.</param>
> /// <param name="propertyValue">The new value to set.</param>
> public static void SetPropertyValue(object obj, string
> propertyName, object propertyValue)
> {
> if (propertyName.IndexOf('.') > -1)
> {
> StringTokenizer parser = new
> StringTokenizer(propertyName, ".");
> IEnumerator enumerator = parser.GetEnumerator();
> enumerator.MoveNext();
> string currentPropertyName =
> (string)enumerator.Current;
> object child = obj;
>
> while (enumerator.MoveNext())
> {
> Type type =
> GetPropertyTypeForSetter(child, currentPropertyName);
> object parent = child;
> child = GetProperty(parent,
> currentPropertyName);
> if (child == null)
> {
> try
> {
> child =
> Activator.CreateInstance(type);
>
> SetPropertyValue(parent, currentPropertyName, child);
> }
> catch (Exception e)
> {
> throw new
> ProbeException("Cannot set value of property '" + propertyName + "' because
> '" + currentPropertyName + "' is null and cannot be instantiated on instance
> of " + type.Name + ". Cause:" + e.Message, e);
> }
> }
> currentPropertyName =
> (string)enumerator.Current;
> }
> SetProperty(child, currentPropertyName,
> propertyValue);
> }
> else
> {
> SetProperty(obj, propertyName, propertyValue);
> }
> }
> /// <summary>
> /// Checks to see if a Object has a writable property/field be
> a given name
> /// </summary>
> /// <param name="obj"> The object to check</param>
> /// <param name="propertyName">The property to check for</param>
> /// <returns>True if the property exists and is
> writable</returns>
> public static bool HasWritableProperty(object obj, string
> propertyName)
> {
> bool hasProperty = false;
> if (obj is IDictionary)
> {
> hasProperty = ((IDictionary)
> obj).Contains(propertyName);
> }
> else
> {
> if (propertyName.IndexOf('.') > -1)
> {
> StringTokenizer parser = new
> StringTokenizer(propertyName, ".");
> IEnumerator enumerator =
> parser.GetEnumerator();
> Type type = obj.GetType();
> while (enumerator.MoveNext())
> {
> propertyName =
> (string)enumerator.Current;
> type =
> ReflectionInfo.GetInstance(type).GetGetterType(propertyName);
> hasProperty =
> ReflectionInfo.GetInstance(type).HasWritableProperty(propertyName);
> }
> }
> else
> {
> hasProperty =
> ReflectionInfo.GetInstance(obj.GetType()).HasWritableProperty(propertyName);
> }
> }
> return hasProperty;
> }
> /// <summary>
> /// Checks to see if the Object have a property/field be a
> given name.
> /// </summary>
> /// <param name="obj">The Object on which to invoke the
> specified property.</param>
> /// <param name="propertyName">The name of the property to
> check for.</param>
> /// <returns>
> /// True or false if the property exists and is readable.
> /// </returns>
> public static bool HasReadableProperty(object obj, string
> propertyName)
> {
> bool hasProperty = false;
> if (obj is IDictionary)
> {
> hasProperty = ((IDictionary)
> obj).Contains(propertyName);
> }
> else
> {
> if (propertyName.IndexOf('.') > -1)
> {
> StringTokenizer parser = new
> StringTokenizer(propertyName, ".");
> IEnumerator enumerator =
> parser.GetEnumerator();
> Type type = obj.GetType();
> while (enumerator.MoveNext())
> {
> propertyName =
> (string)enumerator.Current;
> type =
> ReflectionInfo.GetInstance(type).GetGetterType(propertyName);
> hasProperty =
> ReflectionInfo.GetInstance(type).HasReadableProperty(propertyName);
> }
> }
> else
> {
> hasProperty =
> ReflectionInfo.GetInstance(obj.GetType()).HasReadableProperty(propertyName);
> }
> }
>
> return hasProperty;
> }
> /// <summary>
> ///
> /// </summary>
> /// <param name="type"></param>
> /// <returns></returns>
> public static bool IsSimpleType(Type type)
> {
> if (_simpleTypeMap.Contains(type))
> {
> return true;
> }
> else if (type.IsSubclassOf(typeof(ICollection)))
> {
> return true;
> }
> else if (type.IsSubclassOf(typeof(IDictionary)))
> {
> return true;
> }
> else if (type.IsSubclassOf(typeof(IList)))
> {
> return true;
> }
> else if (type.IsSubclassOf(typeof(IEnumerable)))
> {
> return true;
> }
> else
> {
> return false;
> }
> }
> /// <summary>
> /// Calculates a hash code for all readable properties of a
> object.
> /// </summary>
> /// <param name="obj">The object to calculate the hash code
> for.</param>
> /// <returns>The hash code.</returns>
> public static int ObjectHashCode(object obj)
> {
> return ObjectHashCode(obj,
> GetReadablePropertyNames(obj));
> }
> public static int ObjectHashCode(object obj, string[]
> properties)
> {
> ArrayList values = UnwrapObjectDownToSimpleTypes(obj,
> properties);
> int hashCode = obj.GetType().FullName.GetHashCode();
> foreach (object simpleObject in values)
> {
> if (simpleObject != null)
> {
> hashCode += simpleObject.GetHashCode();
> hashCode +=
> simpleObject.ToString().GetHashCode();
> hashCode *= 29;
> }
> }
> return hashCode;
> }
> public static bool AreObjectsEqual(object obj1, object obj2)
> {
> return AreObjectsEqual(obj1, obj2,
> GetReadablePropertyNames(obj1));
> }
> public static bool AreObjectsEqual(object obj1, object obj2,
> string[] properties)
> {
> if (obj1 == null && obj2 != null)
> return false;
> if (obj1 != null && obj2 == null)
> return false;
> if (obj1 == null && obj2 == null)
> return true;
> if (obj1.GetType() != obj2.GetType())
> return false;
> ArrayList obj1Values =
> UnwrapObjectDownToSimpleTypes(obj1, properties);
> ArrayList obj2Values =
> UnwrapObjectDownToSimpleTypes(obj2, properties);
> if (obj1Values.Count != obj2Values.Count)
> return false;
> for (int i = 0; i < obj1Values.Count; i++)
> {
> if (obj1Values[i] != obj2Values[i])
> return false;
> }
> return true;
> }
> public static ArrayList UnwrapObjectDownToSimpleTypes(object
> obj, ArrayList objectValues)
> {
> return UnwrapObjectDownToSimpleTypes(obj,
> GetReadablePropertyNames(obj), objectValues);
> }
> public static ArrayList UnwrapObjectDownToSimpleTypes(object
> obj, string[] properties)
> {
> return UnwrapObjectDownToSimpleTypes(obj, properties,
> new ArrayList());
> }
> public static ArrayList UnwrapObjectDownToSimpleTypes(object
> obj, string[] properties, ArrayList objectValues)
> {
> ArrayList alreadyDigested = new ArrayList();
> int hashcode = obj.GetType().FullName.GetHashCode();
> for (int i = 0; i < properties.Length; i++)
> {
> object value = GetProperty(obj, properties[i]);
> if (value != null)
> {
> if (IsSimpleType(value.GetType()))
> {
> objectValues.Add(value);
> }
> else
> {
> // It's a Object
> // Check to avoid endless loop
> (circular dependency)
> if (value != obj)
> {
> if
> (!alreadyDigested.Contains(value))
> {
>
> alreadyDigested.Add(value);
>
> UnwrapObjectDownToSimpleTypes(value, objectValues);
> }
> }
> }
> }
> else
> {
> objectValues.Add(null);
> }
> }
> return objectValues;
> }
> }
> }
> --- END ObjectProbe ---
> --- BEGIN CacheKey.Equals(object) ---
> public override bool Equals(object obj)
> {
> //-----------------------------------
> if (this == obj) return true;
> if (!(obj is CacheKey)) return false;
> CacheKey cacheKey = (CacheKey)obj;
> if (_maxResults != cacheKey._maxResults) return false;
> if (_skipRecords != cacheKey._skipRecords) return false;
> if (_type != cacheKey._type) return false;
> if (_parameter is Hashtable)
> {
> if (_hashCode != cacheKey._hashCode) return
> false;
> if (!_parameter.Equals(cacheKey._parameter))
> return false;
> }
> else if (_parameter != null &&
> _typeHandlerFactory.IsSimpleType(_parameter.GetType()))
> {
> if (_parameter != null ?
> !_parameter.Equals(cacheKey._parameter) : cacheKey._parameter != null) return
> false;
> }
> else
> {
> if (!ObjectProbe.AreObjectsEqual(_parameter,
> cacheKey._parameter, _properties))
> {
> return false;
> }
> }
> if (_sql != null ? !_sql.Equals(cacheKey._sql) :
> cacheKey._sql != null) return false;
> return true;
> }
> --- END CacheKey.Equals(object) ---
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see:
http://www.atlassian.com/software/jira