Different bugs using Complex Properties
---------------------------------------
Key: IBATISNET-17
URL: http://issues.apache.org/jira/browse/IBATISNET-17
Project: iBatis for .NET
Type: Bug
Reporter: Henrik Uffe Jensen
I was trying to use some complex properties in resultmaps and parametermaps but
ran into some problems.
The development guide section 3.4.7 (Example 34) is showing complex properties
the way I also tried to use them. I explain the problems using the reference to
this 'product', 'catalog' object example.
Found out that the problems all related to the fact that the seperat
Reflectioninfo is created and cached for each object containing only their
local memberproperties, but different places in the code doesn't handle this.
So with our example two ReflectionInfo's are created. One for 'product'
containing the 'id' and 'description' properties, and one for 'catalog'
containing 'id' and 'description'
* My first problem was that I couldn't save an object with a complex property
containing an enum. (Well no enums in the mentioned example but anyway...)
Found out that this was due to the enum handling in
IBatisNet.DataMapper.Configuration.ParameterMapping.GetValueOfProperty.
The following line of code will try to get the ReflectionInfo from the source
object type and get the propertyinfo using propertyname. But with our comples
property example we acually get the 'Product' instance of the ReflectionInfo
also when we want the 'Catalog' RefelctionInfo. Further more the propertyName
is 'category.id' and only the 'id' part is present in the reflectioninfo.
PropertyInfo propertyInfo =
ReflectionInfo.GetInstance(source.GetType()).GetGetter( propertyName );
I added two new methods to IBatisNet.Common.Utilities.Objects.ObjectProbe,
which can help find the correct type and propertyname for both normal and
complex properties
/// <summary>
/// Return the type of the object that the property belongs to.
/// </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 type of the object the property belongs to.</returns>
public static Type GetObjectType(object obj, string propertyName)
{
if (propertyName.IndexOf('.') > -1)
{
StringTokenizer parser = new StringTokenizer(propertyName, ".");
IEnumerator enumerator = parser.GetEnumerator();
object value = obj;
string token = null;
Type type = null;
while (enumerator.MoveNext())
{
token = (string)enumerator.Current;
value = GetProperty(value, token);
if (value != null && value.GetType().IsClass)
{
type = value.GetType();
}
if (value == null)
{
break;
}
}
return type;
}
else
{
return obj.GetType();
}
}
/// <summary>
/// Return the name of the property used in property maps
/// </summary>
/// <param name="propertyName">The name of the property.</param>
/// <returns>An string representing the name of the property used in property
maps.</returns>
public static string GetPropertyNameForPropertyMap(string propertyName)
{
if (propertyName.IndexOf('.') > -1)
{
string[] arr = propertyName.Split('.');
return arr[arr.Length - 1];
}
else
{
return propertyName;
}
}
I then changed the code in
IBatisNet.DataMapper.Configuration.ParameterMapping.GetValueOfProperty to the
following. Using the new methods to get the correct type and propertyname.
After that I could save a complex property containing an enum
#region Enum case
// HUJ : Get type and propertyname to use for ReflectionInfo and
PropertyMap cache
Type type = ObjectProbe.GetObjectType(source, propertyName);
string propertyNameInMap =
ObjectProbe.GetPropertyNameForPropertyMap(propertyName);
// HUJ : Use above type and propertyname in order to work correct with
complex properties
PropertyInfo propertyInfo =
ReflectionInfo.GetInstance(type).GetGetter( propertyNameInMap );
// PropertyInfo propertyInfo =
ReflectionInfo.GetInstance(source.GetType()).GetGetter( propertyName );
* Next problem then was that I could not use Complex properties at all when
trying to get data from the database. This is actually exactly the example
mentioned.
Found out that this was due to the enum handling in
IBatisNet.DataMapper.Configuration.ResultMapping.SetValueOfProperty and in
IBatis.DataMapper.Configuration.ResultMapping.GetProperties
In 'GetProperties' there are the same problem as mentioned before so I solved
it again by using the two new methods in ObjectProbe. A little extra thing is
that in order to get the type I create an instance of the result object. There
are probably a nicer way to do this but I works.
/// <summary>
/// Get the result properties from the xmNode.
/// </summary>
/// <param name="node">An xmlNode.</param>
private void GetProperties(XmlNode node)
{
XmlSerializer serializer = null;
ResultProperty property = null;
/// HUJ : Create instance of result for use with
ObjectProbe.GetObjectType
object value = CreateInstanceOfResult();
serializer = new XmlSerializer(typeof(ResultProperty));
foreach ( XmlNode resultNode in node.SelectNodes("result") )
{
property = (ResultProperty) serializer.Deserialize(new
XmlNodeReader(resultNode));
PropertyInfo propertyInfo = null;
if ( property.PropertyName != "value" &&
!typeof(IDictionary).IsAssignableFrom(_class) )
{
// HUJ : Get correct type and propertyname for use in
ReflectionInfo and PropertyMap cache
Type type = ObjectProbe.GetObjectType( value,
property.PropertyName );
string propertyNameInMap =
ObjectProbe.GetPropertyNameForPropertyMap( property.PropertyName );
// HUJ : Use aboe type and propertyname
propertyInfo =
ReflectionInfo.GetInstance(type).GetSetter( propertyNameInMap );
//propertyInfo =
ReflectionInfo.GetInstance(_class).GetSetter( property.PropertyName );
}
property.Initialize( propertyInfo );
this.AddResultPropery( property );
}
}
In 'SetValueOfProperty' the problem was that the value of a complex property's
property is trying to be set on the main object. So property 'description' on
object 'category' would actually be set on the 'product' object. This "works"
with example 34 but that's only because 'id' and 'description' is present on
both the 'product' and the 'catagory' object.
Well what I do now is to check if the ReflectedType is the same as the main
object (_class) and if not then user another new method from ObjectProbe to get
the reflected object.
// HUJ : When using complex properties we need to get and use the reflected
object
object reflectedObject = null;
if (property.PropertyInfo.ReflectedType != _class)
{
reflectedObject = ObjectProbe.GetReflectedObject(target,
property.PropertyName);
}
else
{
reflectedObject = target;
}
property.PropertyInfo.SetValue( reflectedObject, dataBaseValue, null );
//property.PropertyInfo.SetValue( target, dataBaseValue, null );
Here is the new method in ObjectProbe
/// <summary>
/// Return the type of the object that the property belongs to.
/// </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 type of the object the property belongs to.</returns>
public static object GetReflectedObject(object obj, string propertyName)
{
if (propertyName.IndexOf('.') > -1)
{
StringTokenizer parser = new StringTokenizer(propertyName, ".");
IEnumerator enumerator = parser.GetEnumerator();
object value = obj;
string token = null;
object reflectedObject = null;
while (enumerator.MoveNext())
{
token = (string)enumerator.Current;
value = GetProperty(value, token);
if (value != null && value.GetType().IsClass)
{
reflectedObject = value;
}
if (value == null)
{
break;
}
}
return reflectedObject;
}
else
{
return obj;
}
}
Well all of the above code works for me now, but I haven't tested it thorougly
with all kinds of configurations and neither have I looked into if things could
be refactored and done in a nicer way.
BTW you mentioned something about that I could take a look in SVN last time I
reported a bug. But where are your SVN located? I can't find it on
svn.apache.org ?
Best regards
Henrik Uffe Jensen
--
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
-
If you want more information on JIRA, or have a bug to report see:
http://www.atlassian.com/software/jira