Deep Clone Entity

Posts   
 
    
MatthewM
User
Posts: 78
Joined: 26-Jul-2006
# Posted on: 15-Sep-2006 22:10:52   

There are many threads about doing this. I thought I would just post the complete code solution. simple_smile Enjoy..

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using SD.LLBLGen.Pro.ORMSupportClasses;

namespace xyz
{
    /// <summary>
    /// For cloning an Entity and all related entities, ie the whole graph
    /// </summary>
    internal class CloneHelper
    {
        private CloneHelper()
        {
        }

        internal static object CloneObject(object o)
        {
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
            bf.Serialize(ms, o);
            ms.Seek(0, SeekOrigin.Begin);
            object oOut = bf.Deserialize(ms);
            ms.Close();
            return oOut;
        }

        internal static void ResetEntityAsNew(IEntity Entity)
        {
            Entity.IsNew = true;
            Entity.IsDirty = true;
            Entity.Fields.IsDirty = true;
            for (int f = 0; f < Entity.Fields.Count; f++)
            {
                Entity.Fields[f].IsChanged = true;
            }
        }

        internal static IEntity CloneEntity(IEntity Entity)
        {
            IEntity newEntity;

            newEntity = (IEntity)CloneObject(Entity);
            ObjectGraphUtils ogu = new ObjectGraphUtils();
            List<IEntity> flatList = ogu.ProduceTopologyOrderedList(newEntity);

            for (int f = 0; f < flatList.Count; f++)
                ResetEntityAsNew(flatList[f]);

            return newEntity;
        }
    }
}

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 16-Sep-2006 19:21:36   

Thanks! simple_smile

Frans Bouma | Lead developer LLBLGen Pro
bot2600
User
Posts: 17
Joined: 12-Mar-2006
# Posted on: 11-Oct-2006 16:19:59   

that code snippet actually doesnt compile for me, I get an error on this line

List<IEntity> flatList = ogu.ProduceTopologyOrderedList(newEntity);

Cannot implicitly convert type 'System.Collections.ArrayList' to 'System.Collections.Generic.List<SD.LLBLGen.Pro.ORMSupportClasses.IEntity>'

any ideas?

Bob

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 12-Oct-2006 08:57:56   

If you are using .NET 2.0 -> ProduceTopologyOrderedList returns List<IEntity> If you are using .NET 1.x -> ProduceTopologyOrderedList returns ArrayList

So make sure you are referencing the correct version of the SD.LLBLGen.Pro.ORMSupportClasses.NETxx.dll

Eischted
User
Posts: 2
Joined: 16-Oct-2006
# Posted on: 16-Oct-2006 22:44:19   

I've got this error - 'SD.LLBLGen.Pro.ORMSupportClasses.ObjectGraphUtils' is inaccessible due to its protection level. Where should I place your code? ObjectGraphUtils is internal class in SD.LLBLGen.Pro.ORMSupportClasses.dll, how can i use it in code?

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 17-Oct-2006 08:23:43   

It should be accessible, it's declared as public class & the method ProduceTopologyOrderedList is public also.

You can check them out in the source code supplied with LLBLGen Pro, in the UtilityClasses.cs file.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 17-Oct-2006 10:10:40   

IT was made public recently, it was made internal by mistake during development of v2.0, so if your version of the runtime libs still has the internal version, please download the latest runtime lib build.

Frans Bouma | Lead developer LLBLGen Pro
Eischted
User
Posts: 2
Joined: 16-Oct-2006
# Posted on: 18-Oct-2006 16:56:53   

Ok, thx.

theadoguy
User
Posts: 11
Joined: 19-Feb-2007
# Posted on: 11-Dec-2007 06:00:35   

MatthewM wrote:

There are many threads about doing this. I thought I would just post the complete code solution. simple_smile Enjoy..

This code doesn't work for me. It compiles and runs but it doesn't save unless I save the individual entities. When I go to the parent object and call Save(true) to save recusively, the cloned entities do not save unless I save them individually. Any idea why this doesn't become part of the recursive link?

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 11-Dec-2007 06:51:27   

Hi theadoguy, It works just fine for me.

Here is my test:

// load some graph
PrefetchPath path = new PrefetchPath((int)EntityType.CustomerEntity);
path.Add(CustomerEntity.PrefetchPathOrders);
CustomerEntity customer = new CustomerEntity("ALFKI", path);

// create a cloned graph
CustomerEntity newCustomer = (CustomerEntity) xyz.CloneHelper.CloneEntity(customer);

// this is my new customer
newCustomer.CustomerId = "YUJU";

// save the whole graph
newCustomer.Save(true);

What LLBLGen version and runtime libraries version are you using? Please post the code snippet that doesn't work for you.

David Elizondo | LLBLGen Support Team
theadoguy
User
Posts: 11
Joined: 19-Feb-2007
# Posted on: 12-Dec-2007 12:30:24   

I don't have time today to cruft up the example to show it here, but change your PK to a autoincremented ID. In my case, the PK is read-only and is server-assigned. I think that's where it is related.

I'll get an example here later this week.

daelmo wrote:

Hi theadoguy, It works just fine for me.

Here is my test:

// load some graph
PrefetchPath path = new PrefetchPath((int)EntityType.CustomerEntity);
path.Add(CustomerEntity.PrefetchPathOrders);
CustomerEntity customer = new CustomerEntity("ALFKI", path);

// create a cloned graph
CustomerEntity newCustomer = (CustomerEntity) xyz.CloneHelper.CloneEntity(customer);

// this is my new customer
newCustomer.CustomerId = "YUJU";

// save the whole graph
newCustomer.Save(true);

What LLBLGen version and runtime libraries version are you using? Please post the code snippet that doesn't work for you.

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 12-Dec-2007 17:18:08   

I don't have time today to cruft up the example to show it here, but change your PK to a autoincremented ID. In my case, the PK is read-only and is server-assigned. I think that's where it is related.

I don't think this is the problem, and I can't see how it relates to the related entities not being saved in a recursive save.

I'll get an example here later this week.

We will be waiting for your repro solution.

wayneo
User
Posts: 6
Joined: 20-Aug-2007
# Posted on: 02-Sep-2008 17:27:09   

MatthewM wrote:

There are many threads about doing this. I thought I would just post the complete code solution. simple_smile Enjoy..

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using SD.LLBLGen.Pro.ORMSupportClasses;

namespace xyz
{
    /// <summary>
    /// For cloning an Entity and all related entities, ie the whole graph
    /// </summary>
    internal class CloneHelper
    {
        private CloneHelper()
        {
        }

        internal static object CloneObject(object o)
        {
            MemoryStream ms = new MemoryStream();
            BinaryFormatter bf = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone));
            bf.Serialize(ms, o);
            ms.Seek(0, SeekOrigin.Begin);
            object oOut = bf.Deserialize(ms);
            ms.Close();
            return oOut;
        }

        internal static void ResetEntityAsNew(IEntity Entity)
        {
            Entity.IsNew = true;
            Entity.IsDirty = true;
            Entity.Fields.IsDirty = true;
            for (int f = 0; f < Entity.Fields.Count; f++)
            {
                Entity.Fields[f].IsChanged = true;
            }
        }

        internal static IEntity CloneEntity(IEntity Entity)
        {
            IEntity newEntity;

            newEntity = (IEntity)CloneObject(Entity);
            ObjectGraphUtils ogu = new ObjectGraphUtils();
            List<IEntity> flatList = ogu.ProduceTopologyOrderedList(newEntity);

            for (int f = 0; f < flatList.Count; f++)
                ResetEntityAsNew(flatList[f]);

            return newEntity;
        }
    }
}

When I use this method to clone an object - I try to use entity.Save(); and it complains because I'm trying to save 2 items to the DB with the same ID - is there any way of re-setting the ID back to Null?

wdisappointed /

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 03-Sep-2008 11:49:15   

Use ForcedCurrentValueWrite() method.

Please check the last post in the following thread: http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=12800 Which refers back to this thread simple_smile

Posts: 27
Joined: 22-Feb-2012
# Posted on: 02-Mar-2012 18:57:47   

Hello,

Does this work for Entities are are created if using the Adapter ? IEntity?

Thank you, Stephen

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 02-Mar-2012 20:15:24   

stephenpatten wrote:

Does this work for Entities are are created if using the Adapter ? IEntity?

No. You will have to do a Find/Replace from IEntity to IEntity2 on that class.

David Elizondo | LLBLGen Support Team
yowl
User
Posts: 266
Joined: 11-Feb-2008
# Posted on: 06-Mar-2015 13:02:48   

I ran into a bug with this and changed the code to:

                var ms = new MemoryStream();
                var bf = new BinaryFormatter(new FastSerializerSurrogateSelector(), new StreamingContext(StreamingContextStates.Clone));
                bf.Serialize(ms, o);
                ms.Seek(0, SeekOrigin.Begin);
                oOut = bf.Deserialize(ms);
                ms.Close();

I had some automatically generated DataContractSurrogate classes created for the entities loaded into the AppDomain and upon deserialization only one element from the collections was being deserialized, other elements were dropped silently. I noticed this as when trying to look at oOut in the debugger it was erroring with a message like "....Entity is declared both in <my llblgen project> and DataContractSurrogates_<guid>. The above change to specify the LLBLGen selector fixes it.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 07-Mar-2015 07:10:55   

Hi Yowl,

Thanks for the input. For the record, please explain what problem you run into, your runtime library version and a description of the involved graph (entities, deep, inheritance, etc).

David Elizondo | LLBLGen Support Team
simmotech
User
Posts: 1024
Joined: 01-Feb-2006
# Posted on: 07-Mar-2015 08:29:20   

Hi Scott (Yowl)

Long time, no speak!

I have a similar ticket open and turning on Fast Serialization solved the problem. Your solution is another way of turning it on but I'm curious - didn't you have it on by default?

Cheers Simon

yowl
User
Posts: 266
Joined: 11-Feb-2008
# Posted on: 09-Mar-2015 01:37:34   

Hi Simon,

Indeed, long time, hope things are going well for you.

I dont have Fast Serialization turned on, but only because I've not been using remoting.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 09-Mar-2015 11:58:34   

Fast serialization is useful also when you're cloning entities as it's much faster than normal binary serialization. I think we found the issue you also ran into, see: http://www.llblgen.com/tinyforum/GotoMessage.aspx?MessageID=131638&ThreadID=23219

Frans Bouma | Lead developer LLBLGen Pro
yowl
User
Posts: 266
Joined: 11-Feb-2008
# Posted on: 09-Mar-2015 13:14:53   

Thanks everyone for getting to the bottom of it. Nothing to do with having the DataSurrogateClasses loaded then. I will switch on Fast Serialization and download the next build.

I read the linked thread and just like to say I have a lot of respect for the LLBLGen team and Simon who put me on to LLBLGen in the first place and I've not looked back.

Thanks,

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 09-Mar-2015 13:47:28   

Thanks Scott! simple_smile To clarify, the runtime attached to the linked thread isn't yet released in the installer (the installer on the website is from february). We'll update that later this week.

Frans Bouma | Lead developer LLBLGen Pro