Uploaded image for project: 'iBatis for .NET'
  1. iBatis for .NET
  2. IBATISNET-17

Different bugs using Complex Properties

Details

    • Bug
    • Status: Closed
    • Major
    • Resolution: Fixed
    • None
    • DataMapper 1.6.2
    • None
    • None

    Description

      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

      Attachments

        Activity

          People

            gilles Gilles Bayon
            hujensen Henrik Uffe Jensen
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Slack

                Issue deployment