Undo mechanism throws OutOfMemoryException

Undo mechanism throws OutOfMemoryException

Old forum URL: forums.lhotka.net/forums/t/9284.aspx


nbrindic posted on Tuesday, July 27, 2010

Hello everyone!

I have one problem with undo mechanism. Actually, I am currently writting some prototype module outside of CSLA, but I use its undo mechanism. I have a problem with OutOfMemoryException. I really don't understand what is happening. I have simple object vith strings and integers, and 2 properties are of very simple class that has just 3 system type properties (datetime, decimal, and string).

I am using following code for backup and restore:

 

public void Backup()

        {

            if (_backup == null)

            {

                _backup = new Stack<byte[]>();

                object source = this;

                Type sourceType = source.GetType();

                HybridDictionary state = new HybridDictionary();

                FieldInfo[] fields;

 

                do

                {

                    // get the list of fields in this type

                    fields = sourceType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);

 

                    foreach (FieldInfo field in fields)

                    {

                        // make sure we process only our variables

                        if (field.DeclaringType == sourceType)

                        {

                            object value = field.GetValue(source);

                            if (sourceType.IsAssignableFrom(field.FieldType))

                            {

                                // make sure the variable has a value

                                if (value == null)

                                {

                                    // variable has no value - store that fact

                                    state.Add(field.DeclaringType.Name + "!" + field.Name, null);

                                }

                                else

                                {

                                    // this is a child object, cascade the call

                                    ((MyEditObject)value).Backup();

                                }

                            }

                            else

                            {

                                // this is a normal field, simply trap the value

                                state.Add(field.DeclaringType.Name + "!" + field.Name, value);

                            }

                        }

                    }

                    sourceType = sourceType.BaseType;

                }

                while (sourceType != null);

 

                // serialize the state and stack it

                using (MemoryStream buffer = new MemoryStream())

                {

                    BinaryFormatter formatter = new BinaryFormatter();

                    formatter.Serialize(buffer, state);

                    _backup.Push(buffer.ToArray());    

                }

                state.Clear();

                state = null;

            }

        }

 

        public void Restore()

        {

            // if we are a child object we might be asked to

            // undo below the level where stacked states,

            // so just do nothing in that case

            if (_backup != null)

            {

                HybridDictionary state;

                using (MemoryStream buffer = new MemoryStream(_backup.Pop()))

                {

                    BinaryFormatter formatter = new BinaryFormatter();

                    buffer.Position = 0;

                    state = (HybridDictionary)formatter.Deserialize(buffer);

                }

                object source = this;

                Type sourceType = source.GetType();

                FieldInfo[] fields;

 

                do

                {

                    // get the list of fields in this type

                    fields = sourceType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public);

                    foreach (FieldInfo field in fields)

                    {

                        // make sure we process only our variables

                        if (field.DeclaringType == sourceType)

                        {

                            // the field is undoable, so restore its value

                            object value = field.GetValue(source);

 

                            if (sourceType.IsAssignableFrom(field.FieldType))

                            {

                                // this is a child object

                                // see if the previous value was empty

                                if (state.Contains(field.DeclaringType.Name + "!" + field.Name))

                                {

                                    // previous value was empty - restore to empty

                                    field.SetValue(source, null);

                                }

                                else

                                {

                                    // make sure the variable has a value

                                    if (value != null)

                                    {

                                        // this is a child object, cascade the call.

                                        ((MyEditObject)value).Restore();

                                    }

                                }

                            }

                            else

                            {

                                // this is a regular field, restore its value

                                field.SetValue(source,

                                    state[field.DeclaringType.Name + "!" + field.Name]);

                            }

                        }

                    }

                    sourceType = sourceType.BaseType;

                }

                while (sourceType != null);

 

                _backup.Clear();

                _backup = null;

                state.Clear();

                state = null;

            }

        }

Please, it's urgent. Thanx in advance. All the best.

 

 

RockfordLhotka replied on Tuesday, July 27, 2010

You probably have a circular reference. Any references to objects other than direct child objects must be marked as NotUndoable (and probably NonSerialized).

You can not use managed backing fields for such references, because all managed object references are assumed to be child references.

nbrindic replied on Tuesday, July 27, 2010

Hi Mr. Lhotka,

well, I am sure I don't have circular referencing because, for this matter, I intentionally wrote info classes, lightweight ones that have only system type properties. Thus, no possibility is there for circular reference. Assume I have the following case:

[Serializable]

public class MyObject

{

      string name;

      int amount;

      DateTime date;

      SupplierInfo supplier;

}

[Serializable]

public class SupplierInfo

{

      string name;

      string address;

}

 

Both classes have encapsulated fields, but i didn't put them here because of simplicity. Also, have on mind that I'm writting this prototype app outside of CSLA, but I wanted to use its mechanism for undoing. In CSLA itself, few months ago, I wrote application that uses undo option, but I didn't have such a problems. What I need in this case is, actually, 1 level undo. So, if you have any suggestions, I appreciate that.

 

Thank you in advance,

Nebojsha

 

JonnyBee replied on Monday, August 02, 2010

Hi,

You could also get this OutOfMemory if  your restore method gets into an infinite recursive function call. 

May not be easy to debug by set  VisuaStudio debugger to stop exactly when exception is thrown and check the callstack.

 

Copyright (c) Marimer LLC