none
VB function to deep-clone a Linq to SQL entity using datacontract serialization RRS feed

  • Question

  • I am trying to deep-clone a Linq to SQL entity using datacontract serialization, and am having problems getting the full graph hierarchy.  I found c# code from http://blog.frozengraphics.be/post/2010/01/28/Creating-a-deep-copy-of-an-object-in-c.aspx and translated to the following VB, but I'm not sure how to call the function:

    <System.Runtime.CompilerServices.Extension> _
     Public Shared Function DeepClone(Of T)(ByVal obj As T) As T
     Dim cloned As T
     Dim serializer = New DataContractSerializer(GetType(T))
     Using ms = New MemoryStream()
     serializer.WriteObject(ms, obj)
     ms.Position = 0
     cloned = DirectCast(serializer.ReadObject(ms), T)
     End Using
     Return cloned
    End Function

    The compiler does not like the following syntax:

         newR = DeepClone(as RentComp))(r)

    Any suggestions?                Thanks -BGood

    Saturday, April 17, 2010 3:36 AM

Answers

  • OK, I've made some progress on this so I'll reply to my own post.  Additional web references on deep-cloning VB objects includes:

    1. http://bytes.com/topic/visual-basic/insights/779607-clone-any-object-vb-6-0-a
    2. http://connect.microsoft.com/VisualStudio/feedback/details/328459/vb-net-crash-on-deserialize-with-generic

    Although #1 is using binary formatter serialization (which I don't think can be used on Linq objects), the function call to my code would be:

        Dim newR As RentComp = DeepClone(r)

    However, the compiler complains that the class containing the function call "cannot be indexed because it has no default property".  Anyone have any ideas on how to proceed?

    The purpose of this program segment is to copy Linq objects between two Linq datacontexts. This was previously working by simply creating the new object, and manually setting new ID and foreign key values for the child object in the association.  But at some point I must have changed something in my object associations because I now notice that any values I try to update in the cloned object now propagate back to the original object.

    Any suggestions would be appreciated.  -BGood

    Saturday, April 17, 2010 3:58 PM
  • OK, I've made additional progress, but still have not been able to deepClone the object hierarchy for purposes of cloning Linq objects to be reinserted as new objects in the datacontext.  The calling code needs to be:

        Dim newR As New RentComp
        newR = DeepClone(r)

    I removed the function from a module to the calling class and removed the compiler extension:

         <System.Runtime.CompilerServices.Extension>

    However, the full parent-child hierarchy was never returned correctly and I received the following Linq errors:

       1.   System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException was unhandled by user code

       2.   An attempt was made to remove a relationship between a ... and a ... However, one of the relationship's foreign keys ... cannot be set to null.  I made the modifications to the .dbml O/RM file as Beth Massi describes in ttp://blogs.msdn.com/bethmassi/archive/2007/10/02/linq-to-sql-and-one-to-many-relationships.aspx, but the exceptions persist.

    To get the cloning working, I went back to the inelegant brute force method of switching object tracking on/off in the sending/receiving datacontexts, and manually reset the foreign key pointers with multiple hits to the DB.

    It seems there should be a simpler way to do this given the power of Linq, but I just cannot waste any more time searching for it right now.

         -BGood

    Monday, April 19, 2010 1:01 AM

All replies

  • OK, I've made some progress on this so I'll reply to my own post.  Additional web references on deep-cloning VB objects includes:

    1. http://bytes.com/topic/visual-basic/insights/779607-clone-any-object-vb-6-0-a
    2. http://connect.microsoft.com/VisualStudio/feedback/details/328459/vb-net-crash-on-deserialize-with-generic

    Although #1 is using binary formatter serialization (which I don't think can be used on Linq objects), the function call to my code would be:

        Dim newR As RentComp = DeepClone(r)

    However, the compiler complains that the class containing the function call "cannot be indexed because it has no default property".  Anyone have any ideas on how to proceed?

    The purpose of this program segment is to copy Linq objects between two Linq datacontexts. This was previously working by simply creating the new object, and manually setting new ID and foreign key values for the child object in the association.  But at some point I must have changed something in my object associations because I now notice that any values I try to update in the cloned object now propagate back to the original object.

    Any suggestions would be appreciated.  -BGood

    Saturday, April 17, 2010 3:58 PM
  • OK, I've made additional progress, but still have not been able to deepClone the object hierarchy for purposes of cloning Linq objects to be reinserted as new objects in the datacontext.  The calling code needs to be:

        Dim newR As New RentComp
        newR = DeepClone(r)

    I removed the function from a module to the calling class and removed the compiler extension:

         <System.Runtime.CompilerServices.Extension>

    However, the full parent-child hierarchy was never returned correctly and I received the following Linq errors:

       1.   System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException was unhandled by user code

       2.   An attempt was made to remove a relationship between a ... and a ... However, one of the relationship's foreign keys ... cannot be set to null.  I made the modifications to the .dbml O/RM file as Beth Massi describes in ttp://blogs.msdn.com/bethmassi/archive/2007/10/02/linq-to-sql-and-one-to-many-relationships.aspx, but the exceptions persist.

    To get the cloning working, I went back to the inelegant brute force method of switching object tracking on/off in the sending/receiving datacontexts, and manually reset the foreign key pointers with multiple hits to the DB.

    It seems there should be a simpler way to do this given the power of Linq, but I just cannot waste any more time searching for it right now.

         -BGood

    Monday, April 19, 2010 1:01 AM
  • Here's an update to this thread on deep cloning Linq objects.  I revisited this project from a couple of months ago and came across a good code sample in c# which when translated to VB does the job just fine.  The blog posting is from Damien Guard, under the heading "Linq to SQL Tricks #2 - Cloning Entities" (http://damieng.com/blog/2009/04/12/linq-to-sql-tips-and-tricks-2).

    Translated to VB, it becomes:

    Public Shared Function Clone(Of T)(ByVal source As T) As T
            Dim dcs = New System.Runtime.Serialization.DataContractSerializer(GetType(T))
            Using ms = New System.IO.MemoryStream()
                dcs.WriteObject(ms, source)
                ms.Seek(0, System.IO.SeekOrigin.Begin)
                Return DirectCast(dcs.ReadObject(ms), T)
            End Using
        End Function

    With the calling syntax being:

     Dim source = myQuery.First() 
     Dim cloned = Clone(source)

    Thanks for posting, Damien.

         -BGood

    Wednesday, June 16, 2010 12:11 AM