Cloning with m:n relationship

Posts   
 
    
howez
User
Posts: 28
Joined: 12-May-2007
# Posted on: 22-Feb-2008 21:05:36   

When using the CloneHelper class. I encounter an interesting issue that maybe someone could help me with...

LLBLGEN v2.5 Final SQL server 2000 / 2005 adapter

db heirarchy is as follows..

Project --Permit ----permit to inspection assoc ----permit to plans assoc ----permit to fees assoc --Inspection ----inspection to plans assoc (redundant) --Plans ----plans to permit assoc (redundant) --Fees ----fees to permit assoc (redundant)

there is an association table (m:n) from Permit to Inspection,Plans,Fees

when cloning in the association tables I get the new PermitID but it retains the source InspectionID(s), FeeID(s),PlanID(s). I am currently graphing after the fact to 'cleanup' but this is clunky..

I am using a prefetchpath to get everything I need before I clone. Removing the redundant relationships has no effect. I have tried to set the copied IDs to '0' in hopes that it would use the genereated ones, but this did not work either..

best,

jon

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 23-Feb-2008 02:56:21   

Could you please post the code where you build the prefetch path and send it to the fetch call, then the code where you clone the entity?

Could you try to simplify the scenario to identify what is happening.

David Elizondo | LLBLGen Support Team
howez
User
Posts: 28
Joined: 12-May-2007
# Posted on: 23-Feb-2008 07:33:27   

CloneHelper is found here:

http://llblgen.com/TinyForum/Messages.aspx?ThreadID=10325&HighLight=1

my prefetch code is shown below:

        if (prefetchProjectDefs.PermitPath)
        {
            IPrefetchPathElement2 pfPermitElement = prefetchPath.Add(ProjectEntity.PrefetchPathProjectPermitsAssoc);
            pfPermitElement.SubPath.Add(ProjectPermitsAssocEntity.PrefetchPathFee2ProjectPermitAssoc);
            pfPermitElement.SubPath.Add(ProjectPermitsAssocEntity.PrefetchPathInspSchedPermitAssoc);
            pfPermitElement.SubPath.Add(ProjectPermitsAssocEntity.PrefetchPathPlansPermitAssoc);
            pfPermitElement.SubPath.Add(ProjectPermitsAssocEntity.PrefetchPathPermits);
        }

when I use this I get the correct ProjectPermitAssocIDs (the newly created ones), but I get the existing Id for the other side of the association (m:n) If I setup the prefetch to originate from the otherside my results reverse.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 25-Feb-2008 03:30:44   

howez, I need you simplify the sample. Please make a one entity+ a prefetch node, then run pass it to the CloneHelper. Could you please indicate the exact implementation of the helper you are using (there is more than one I think).

David Elizondo | LLBLGen Support Team
howez
User
Posts: 28
Joined: 12-May-2007
# Posted on: 25-Feb-2008 05:56:15   

Using a single entity will not reproduce the problem. I am not having any issues with the prefetch or the clone. It am running into an issue when cloning a child table using a m:n relationship. I have attached an image of a simplified example of this... It contains four tables..

From the image, I am attempting to clone the project. I can clone everything as expected except for the InspSchedPermitAssoc Table. With the current prefetch the InspSchedPermitAssoc table grabs a new ProjectPermitAssocID (this is good), but is using the InspSchedID from the original Project Entity and not from the newly created one. I am regraphing through the project entity after the fact to fix this, but I just need to know if there is something i can do within the prefetch to make sure that the clone will get the newly created InspSched Ids and not reuse the one from the orginal source entity.

I also have included the simple prefetch code and cloner helper class.

btw, thx for having a look. Let me know if you need anything else.

Prefetch:

    public IPrefetchPath2 GetProjectRelationsCopy()
    {

        IPrefetchPath2 prefetchPath = new PrefetchPath2((int)EntityType.ProjectEntity);


        IPrefetchPathElement2 pfPermitElement = prefetchPath.Add(ProjectEntity.PrefetchPathProjectPermitsAssoc);
        pfPermitElement.SubPath.Add(ProjectPermitsAssocEntity.PrefetchPathInspSchedPermitAssoc ).SubPath.Add(InspSchedPermitAssocEntity.PrefetchPathInspSched);
        //pfPermitElement.SubPath.Add(ProjectPermitsAssocEntity.Pref)


        IPrefetchPathElement2 pfScheduleElement = prefetchPath.Add(ProjectEntity.PrefetchPathInspSched);
        pfScheduleElement.SubPath.Add(InspSchedEntity.PrefetchPathInspSchedPermitAssoc);

        return prefetchPath;


    }

Clone Helper

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

namespace GovPartner.CDP.CDPAdapterServices.Helper { /// <summary> /// For cloning an Entity and all related entities, ie the whole graph /// </summary> public class CloneHelper { public CloneHelper() { }

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

    private static void ResetEntityAsNew(IEntity2 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;
        }
    }

    public static IEntity2 CloneEntity(IEntity2 Entity, bool ResetAsNew)
    {
        IEntity2 newEntity;

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

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

        return newEntity;
    }

    public static IEntity2 CloneEntity(IEntity2 Entity)
    {
        return CloneEntity(Entity, true);
    }

    public static T CloneEntity2<T>(T Entity)
        where T : EntityBase2
    {
        return (T)CloneEntity(Entity, true);
    }

    public static T CloneEntity2<T>(T Entity, bool ResetAsNew)
        where T : EntityBase2
    {
        return (T)CloneEntity(Entity, ResetAsNew);
    }
}

}

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 26-Feb-2008 10:33:19   

I have attached an image of a simplified example of this

Nothing is attached.

howez
User
Posts: 28
Joined: 12-May-2007
# Posted on: 26-Feb-2008 17:21:57   

Adding attachment...

Ha, forgot to hit the upload button...

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39905
Joined: 17-Aug-2003
# Posted on: 27-Feb-2008 12:43:01   

It should be possible, though you've to fetch the whole graph with a context. This is required so entities which are multiple times in the graph (e.g. inside an M:N relation and referenced elsewhere as a m:1 relation for example) are the same instance and not different instances.

Could you try that please? (so associate the entity collection with a context, or pass a context if you're fetching a single entity, see manual on using a context).

Frans Bouma | Lead developer LLBLGen Pro
howez
User
Posts: 28
Joined: 12-May-2007
# Posted on: 28-Feb-2008 20:43:07   

That did it.. Here are a couple of code snippets for anyone interested...

Thanks!

from the clone:


           ProjectEntity projectSource = GetProjectCopyEntity(dataAdapter, projectID);

           IEntity2 projectDest = CloneHelper.CloneEntity(projectSource);

This used to return the ProjectEntity and now it returns the ProjectEntity from the Context


        public ProjectEntity GetProjectCopyEntity(DataAccessAdapter dataAdapter, decimal projectID)
        {

            bool bLocalConnection = false;

            if (dataAdapter == null)
            {
                dataAdapter = new DataAccessAdapter();
                bLocalConnection = true;
            }


            ProjectEntity project = new ProjectEntity(projectID);

            //create a context 
            Context myContext = new Context();

            try
            {
                if (dataAdapter.FetchEntity(project, projectPrefetchFactory.GetProjectRelationsCopy(), myContext) != true)
                    project = null;
            }
            catch (ORMQueryExecutionException err)
            {
                throw;
            }
            finally
            {
                if (bLocalConnection)
                    dataAdapter.Dispose();
            }
            
            //get the entity out of the context.  This verifies that everything in the context will get new IDs (no reuse)
            return (ProjectEntity)myContext.Get(project);
        }

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 29-Feb-2008 08:59:45   

Thanks for the feedback.