[ https://issues.apache.org/jira/browse/IBATISNET-17?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Gilles Bayon closed IBATISNET-17. --------------------------------- Resolution: Fixed Fix Version/s: DataMapper 1.6.2 Assignee: Gilles Bayon close old issue > Different bugs using Complex Properties > --------------------------------------- > > Key: IBATISNET-17 > URL: https://issues.apache.org/jira/browse/IBATISNET-17 > Project: iBatis for .NET > Issue Type: Bug > Reporter: Henrik Uffe Jensen > Assignee: Gilles Bayon > Fix For: DataMapper 1.6.2 > > > 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. - You can reply to this email to add a comment to the issue online.