You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ibatis.apache.org by "Henrik Uffe Jensen (JIRA)" <ib...@incubator.apache.org> on 2005/01/01 07:07:38 UTC

[jira] Created: (IBATISNET-17) Different bugs using Complex Properties

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


[jira] Commented: (IBATISNET-17) Different bugs using Complex Properties

Posted by "Gilles Bayon (JIRA)" <ib...@incubator.apache.org>.
     [ http://issues.apache.org/jira/browse/IBATISNET-17?page=comments#action_57163 ]
     
Gilles Bayon commented on IBATISNET-17:
---------------------------------------

Indeed, there's a current work-around to your second problem. If you product has a category property, you need to declare a mapping as
<result property="Category" resultMapping="Category.CategoryResult" />

Here is an example from NPetshop. Hope this Help

<resultMaps>                                     
<resultMap id="ProductResult" class="Product">
<result property="Id" column="Product_Id"/>
<result property="Name" column="Product_Name"/>
<result property="Description" column="Product_Description"/>
</resultMap>

<resultMap id="ProductList" class="Product" extends="ProductResult">
<result property="Category" resultMapping="Category.CategoryResult" />
</resultMap>
</resultMaps>
	
<!-- =============================================
        MAPPED STATEMENTS 
    ============================================= 
-->
<statements>
<select id="GetProductListByCategory" cacheModel="ProductList-cache" resultMap="ProductList" parameterClass="string">
select 
 Product_Id, 
 Product_Name, 
 Product_Description,
 P.Category_Id, 
 Category_Name, 
 Category_Description 
 from Products as P, Categories as C 
 where C.Category_ID= P.Category_ID and
 P.Category_Id = #value#
</select>

I prefer this syntax (resultMapping="..") from example 34 as you can reuse current declaration and indeed I haven't incorporated the Java resultMap syntax as you have discovered.
-Gilles

> 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


RE: [jira] Created: (IBATISNET-17) Different bugs using Complex Properties

Posted by Gilles Bayon <gi...@laposte.net>.
Thanks for your path and Happy new year.

The SVN repo is not yet migrated to Apache, 
you can get it at
https://wush.net/svn/ibatisnet1/ (guest/guest)

I will incorporate your path in the source. 
If you can post diff path the next time.

Thanks again.

-Gilles



-----Message d'origine-----
De : Henrik Uffe Jensen (JIRA) [mailto:ibatis-dev@incubator.apache.org] 
Envoyé : samedi 1 janvier 2005 07:08
À : ibatis-dev@incubator.apache.org
Objet : [jira] Created: (IBATISNET-17) Different bugs using Complex
Properties

Different bugs using Complex Properties
---------------------------------------

-- 
No virus found in this outgoing message.
Checked by AVG Anti-Virus.
Version: 7.0.296 / Virus Database: 265.6.7 - Release Date: 30/12/2004