Deserialization Error during Load method

Aug 13, 2011 at 7:16 AM

I have an odd problem that's baffling me and I'm hoping you can help.

If I instantiate an object in my code and save it directly to the database I can retrieve it as I would expect to using Load or Query methods.

But if I take that object and don't save it to the database but I serialize it, deserialize it and then Save it I can't call Load without Sterling throwing an error related to converting a System.DateTime to System.String. If I browse the object all of the properties are identical to the object that I can .Save and then Load/Query.

Specifically the error is "Object of type 'System.String' cannot be converted to type 'System.DateTime'." - ArgumentException was unhandled by user code. This occurs in Wintellect.Sterling.Serialization.PropertyOrField

public Action<object, object> Setter
        {
            get
            {
                if (_propertyInfo != null)
                {
                    return (obj, prop) => _propertyInfo.GetSetMethod().Invoke(obj, new[] { prop });
                }

                return (obj, prop) => _fieldInfo.SetValue(obj, prop);
            }
        }

The line is - return (obj, prop) => _propertyInfo.GetSetMethod().Invoke(obj, new[] { prop });

The object in obj looks fine if I browse it when it breaks for the exception. Prop is a blank string.

_propertyInfo = System.DateTime SubscriptionEndDate

The object in question is:

 public class User : IMojoDataObject
    {
        private Guid _id = Guid.NewGuid();
        private string _emailAddress = string.Empty;
        private string _pwDigest = string.Empty;
        private string _firstName = string.Empty;
        private string _lastName = string.Empty;
        private string _passwordHint = string.Empty;
        private DateTime _subscriptionEndDate;
        private bool _locked = false;
        private bool _trialAccount = true;
        private DateTime _timestamp;
        private MojoDataObjectState _objectState = MojoDataObjectState.Active;

        public User()
        {
        }

        public Guid ID
        {
            get { return _id; }
            set { _id = value; }
        }
        public string EmailAddress
        {
            get { return _emailAddress; }
            set { _emailAddress = value; }
        }
        public string PWDigest
        {
            get { return _pwDigest; }
            set { _pwDigest = value; }
        }
        public string FirstName
        {
            get { return _firstName; }
            set { _firstName = value; }
        }
        public string LastName
        {
            get { return _lastName; }
            set { _lastName = value; }
        }
        public string PasswordHint
        {
            get { return _passwordHint; }
            set { _passwordHint = value; }
        }
        public DateTime SubscriptionEndDate
        {
            get { return _subscriptionEndDate; }
            set { _subscriptionEndDate = value; }
        }
        public bool Locked
        {
            get { return _locked; }
            set { _locked = value; }
        }
        public bool TrialAccount
        {
            get { return _trialAccount; }
            set { _trialAccount = value; }
        }
        public DateTime Timestamp
        {
            get { return _timestamp; }
            set { _timestamp = value; }
        }
        public MojoDataObjectState ObjectState
        {
            get { return _objectState; }
            set { _objectState = value; }
        }
    }

Aug 13, 2011 at 5:30 PM
Edited Aug 13, 2011 at 5:30 PM

I'm using SilverlightSerializer 1.0 to handle the serialization/deserialization and it seems to be doing its just fine. What's odd is I can call .Save<T>(obj) on my DB in Sterling and I don't get an error or exception, I only get the error when I call .Load. Query doesn't ever return anything. 

I currently save the object using the .Save(type, object) overload but I've tested it with the .Save<T>(object) version as well and have the same result. Both save methods return fine.. Its just when I try to Load the object that I get the exception.

These objects that cause the exception originated in the same program and are being sent to my cloud WCF process for syncronization to other clients. So the object I'm trying to load was originally created on my machine, serialized, then sent to my service where they are just stored as binary data. Where the error happens is when I wipe my local db and populate it from the serialized objects on the cloud. The storage is just of the serialized data, not the actual objects, so there's no chance for changes in type between the client and storage medium.

Hopefully I'm just missing something basic here but I'm at the point where I don't know what that can be.

Aug 13, 2011 at 6:17 PM
Edited Aug 13, 2011 at 6:19 PM

I noticed where I serialize I was receiving the object to serialize by INTERFACE. I've tried casting it to the actual type before I serialize but I'm still finding the same problem.

Aug 15, 2011 at 10:37 PM

Since I couldn't find a solution and nobody seemed to have any ideas to try I've decided to just replace Sterling Database with my own solution and now everything is working great. The problem is definitely inside the Sterling engine somewhere but I couldn't identify it and I need to make progress on my project. It seemed to be something cache related but I'm not sure.

Thanks anyway. I guess for everyone else the project meets their needs.

Aug 26, 2011 at 2:49 PM

I get the same behaviour - but I'm totally confused as to why....

My unit test just calls Save and then Load.

Fails in the same place when trying to set a string value (which actually belongs to another property) to an enumeration property.

Looks like there's an issue with the property cache that's being built - but I'm not entirely sure where....

Bit of a show-stopper for our project too.

Coordinator
Aug 26, 2011 at 3:44 PM

It's likely because Sterling doesn't know how to deal with the Action object. It's not serializeable, but Sterling tries to serialize and deserialize any public properties and fields. Can you either provide a custom serializer for the Action<object,object> type or use the SterlingIgnoreAttribute and see if you still have the issue?

Aug 29, 2011 at 12:42 PM

Jeremy - I believe the Action<string, string> BE is referring to, is the Setter property inside PropertyOrField.cs inside Sterling code.

Here's the stacktrace of my error:

   at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast)
   at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr)
   at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
   at Wintellect.Sterling.Serialization.PropertyOrField.<get_Setter>b__0(Object obj, Object prop)
   at Wintellect.Sterling.Serialization.SerializationHelper.<>c__DisplayClassd.<_CacheProperties>b__5(Object parent, Object property)
   at Wintellect.Sterling.Serialization.SerializationHelper._Deserialize(BinaryReader br, CycleCache cache)
   at Wintellect.Sterling.Serialization.SerializationHelper.Load(Type type, Object key, BinaryReader br, CycleCache cache)
   at Wintellect.Sterling.Database.BaseDatabaseInstance.Load(Type type, Object key, CycleCache cache)
   at Wintellect.Sterling.Database.BaseDatabaseInstance.Load(Type type, Object key)
   at Wintellect.Sterling.Database.BaseDatabaseInstance.Load[T,TKey](TKey key)
   at Wintellect.Sterling.Keys.TableKey`2.<.ctor>b__0()
   at System.Lazy`1.CreateValue()

When I debug what the value of the "prop" variable is - it's clear Sterling is trying to set the value of another property, into a property of a different type.
In my case, it's trying to insert Code (of type string) value into an EditState (of type enumeration) property.

 

Aug 29, 2011 at 3:03 PM
Edited Aug 29, 2011 at 3:16 PM

Jeremy - I've isolated code that causes the issue:

See sample code with unit tests here:

 

Basically - it's a combination of 2 things:
1) calling GetInstancePropertyValue in ValidateProperty method of OnKeyObjectBase - which is called when the setter is applied on Deserialization.
2) Setting EditState = EditState.New in the constructor of OnKeyObjectBase

Unit test fails with "System.ArgumentException : Object of type 'System.String' cannot be converted to type 'System.Nullable`1[System.Int32]'." because Sterling is trying to set the string SomeValue value to the int? Id property.
Commenting out either of these 2 lines of code will cause the test to pass. It seems the combination of the 2 are throwing Sterling's property cache for a loop.

The code is a subset of our domain objects we use in our silverlight LOB app.

Coordinator
Aug 29, 2011 at 4:05 PM

It looks like the underlying state objects are throwing it off - possibly a custom serializer will help? We are looking at two changes to future versions: first, organizing the property names alphabetically so they always come in the same order, and second to store a property dictionary/signature to handle multiple versions. Both will require a conversion process to the underlying database to accommodate the change in serialization strategy but should resolve these issues in the future.

Sep 2, 2011 at 5:35 AM

I'm seeing something similar when using nested objects with default property types but not with simple classes:

1. Start app ... Save class with nested class & exit.

2. Start app ... successfully loaded class with nested class, modify data, save changes and exit

3. Start app ... Try and Load data and an "Object of type 'System.String' cannot be converted to type 'System.Int32'" error is throw on the following line in the propertyorfield.cs file:

       public Action<object, object> Setter
        {
            get
            {
                if (_propertyInfo != null)
                {
                    return (obj, prop) => _propertyInfo.GetSetMethod().Invoke(obj, new[] { prop });   // ERROR HERE!
                }

                return (obj, prop) => _fieldInfo.SetValue(obj, prop);
            }
        }

 

Is this a similar issue and when are you looking at implementing the "change in serialization strategy"?

Apr 21, 2012 at 6:29 AM

Do we have a fix for this?

Massive problem and makes the project unusable.