Deep Clone of a business object: the quick and dirty way

Print Content | More

I was implementing the IEditableObject interface for some entity classes in my current project and I needed a quick and dirty way to do the deep clone of an object, since my entity classes are shared between WCF, WPF and Silverlight (yes I’m one of the guys so crazy to build multi-target applications) I needed a method that could work in all the environments.

In WPF and WCF you can rely on the binary formatter and the serialization (entity classes have to be marked with the Serializable attribute), and use code like this:

   1: public static partial class Helpers
   2: {
   3:     public static T DeepClone<T>(this T obj)
   4:     {
   5:         T cloned = default(T);
   6:         var serializer = new BinaryFormatter();
   7:         using (var ms = new MemoryStream())
   8:         {
   9:             serializer.Serialize(ms, obj);
  10:             ms.Position = 0;
  11:             cloned = (T)serializer.Deserialize(ms);
  12:         }
  13:         return cloned;
  14:     }
  15: }

In Silverlight you can write something similar and use the DataContractSerializer and the xml serialization:

   1: public static partial class Helpers
   2: {
   3:     public static T DeepClone<T>(this T obj)
   4:     {
   5:         T cloned = default(T);
   6:         var serializer = new DataContractSerializer(typeof(T));
   7:         using (var ms = new MemoryStream())
   8:         {
   9:             serializer.WriteObject(ms, obj);
  10:             ms.Position = 0;
  11:             cloned = (T)serializer.ReadObject(ms);
  12:         }
  13:         return cloned;        
  14:     }
  15: }

Due to the restriction of Silverlight the DataContractSerializer has some limitations and they depends if you are using or not the DataContract + DataMember attributes:

  • With no attributes you can only serialize types that have a default public constructor with no arguments and all public fields/properties, for example something like:
   1: public class TestEntityNoAttributes
   2: {
   3:     public string PublicField;
   4:  
   5:     public TestEntityNoAttributes()
   6:     {
   7:     }
   8:  
   9:     public TestEntityNoAttributes(string pub)
  10:     {
  11:         PublicField = pub;
  12:     }
  13: }
  • Using DataContract + DataMember attribute you can choose what to serialize, you do not need the default constructor anymore, but still you are limited to public fields only...unless you use the InternalsVisibleTo attribute and convert the protected and private fields you want to serialize to internal, the class will look like:
   1: [DataContract]
   2: public class TestEntityAttributes
   3: {
   4:     [DataMember]
   5:     public string PublicField;
   6:  
   7:     /// <summary>
   8:     /// changed from protected to internal
   9:     /// it has to be internal to allow serialization and be used with InternalsVisibleTo assembly attribute
  10:     /// </summary>
  11:     [DataMember]
  12:     internal string _ProtectedField;
  13:     public string ProtectedField
  14:     {
  15:         get { return _ProtectedField; }
  16:     }
  17:  
  18:     /// <summary>
  19:     /// changed from private to internal
  20:     /// it has to be internal to allow serialization and be used with InternalsVisibleTo assembly attribute
  21:     /// </summary>
  22:     [DataMember]
  23:     internal string _PrivateField;
  24:     public string PrivateField
  25:     {
  26:         get { return _PrivateField; }
  27:     }
  28:  
  29:     public TestEntityAttributes(string pub, string pro, string pri)
  30:     {
  31:         PublicField = pub;
  32:         _ProtectedField = pro;
  33:         _PrivateField = pri;
  34:     }
  35: }

plus you have to tag the assembly that holds the entities and make its internal members visibile to the serializer, since DataContractSerializer resides in the System.Runtime.Serialization you have to mark the assembly with:

   1: //needed to allow the serializer to access internal members
   2: [assembly: InternalsVisibleTo("System.Runtime.Serialization")]

Then using the Silverlight Unit Testing Framework from Jeff Wilcox, you can write a couple of tests like the following ones to verify that it works.

   1: [TestMethod]
   2: public void TestEntityNoAttributeDeepClone()
   3: {
   4:     TestEntityNoAttributes e = new TestEntityNoAttributes("1");
   5:     TestEntityNoAttributes eCloned = e.DeepClone();
   6:     Assert.AreEqual(e.PublicField, eCloned.PublicField);
   7: }
   8:  
   9: [TestMethod]
  10: public void TestEntityAttributeDeepClone()
  11: {
  12:     TestEntityAttributes e = new TestEntityAttributes("1", "2", "3");
  13:     TestEntityAttributes eCloned = e.DeepClone();
  14:     Assert.AreEqual(e.PublicField, eCloned.PublicField);
  15:     Assert.AreEqual(e.ProtectedField, eCloned.ProtectedField);
  16:     Assert.AreEqual(e.PrivateField, eCloned.PrivateField);
  17: }

 

Naturally using both those methods you incur in performance penalty...but hey...this is the quick and dirty way after all.

 

 



Clone, Deep clone, Silverlight, Xml serialization, .net, C#, Serialization

9 comments

Related Post

  1. #1 da Reflective Perspective - Chris Alcock The Morning Brew #204 - Saturday March 2010 alle 01:15

    [...] Deep Clone of a business object: the quick and dirty way - Giorgetti Alessandro looks at easy Deep Cloning functionality for Silverlight, WCF and WPF. [...]

  2. #2 da Guardian - Saturday March 2010 alle 01:15

    It's an extension method in C#, declaring the function this way, if you import the namespace you can use it on any class derived from object like: myinstance.DeepClone();

  3. #3 da Serialization Exception: PropertyChangedEventManager is not serializable at Guardians Home - Saturday March 2010 alle 01:15

    [...] Actually I’m working on a multi-target framework for Silverlight and WPF and I have developed a base to be used by any entity and DTO class that can also be sent back and forth through WCF or WebServices; a deriver class supports a generic clone method (see deep clone of a business object: the quick and dirty way). [...]

  4. #4 da Guardian - Saturday March 2010 alle 01:15

    No sorry, I had a 'not-so-well-working-solution' that I used in old .Net 1.1 code based on property discovery by using reflection, but that imposed you some constraints on the object structure. It also had a lot of limitations dealing with lists and array. In my current projects I do not have to deal with very big entities, so I prefer to implement the undo by hand.

  5. #5 da Martin Nyborg - Saturday March 2010 alle 01:15

    I have been working with the CSLA framework and also been taking a deep dive into the code. and I know that implementing undo is no trivial task. Your solution is one half of the problem. backing up the values, but do you also have a good solution to putting the values back?

  6. #6 da Norberg - Saturday March 2010 alle 01:15

    Nice blog and thanks for the info!

  7. #7 da Chad - Saturday March 2010 alle 01:15

    This is awesome. Thank you! C# Question: What does the "this" signify in the method declaration? public static T DeepClone(this T obj)

  8. #8 da Antonio Budano - Saturday March 2010 alle 01:15

    Hi, I am trying your solution but I am getting some problems. I marked all classes as Serializable but I am getting this error Il tipo 'System.ComponentModel.PropertyChangedEventManager' nell'assembly 'WindowsBase, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' non è contrassegnato come serializzabile. I suppose this is due the fact the base class from wich all entities derive implements the INotifiyPropertyChanged interface. Any idea how to solve this?

  9. #9 da Antonio Budano - Saturday March 2010 alle 01:15

    Forget what I said before, I found the post where you are explaining the solution. Thanks

All fields are required and you must provide valid data in order to be able to comment on this post.


(will not be published)
(es: http://www.mysite.com)