Duplicate Record/Entity

Posts   
 
    
Posts: 1263
Joined: 10-Mar-2006
# Posted on: 30-Mar-2006 06:49:15   

I have seen several posts on this topic. From what I have seen, many discussions branch off into why this is necessary, etc, or they are talking about duplicate objects - where two objects refer to the same record without going back to the database.

However, I want to duplicate an entity - add a new record starting with the field contents of a given entity.

There are many reasons for this, such as a user adds a new record/entity, 'starting with' an existing record/entity. You have a single record, you want to break up into multiple records, so you want to duplicate it and then change a few things.

So, for example, lets say you selling sandwiches. A person details exactly how they want a sandwich. When you are finished making it, they say, make me another one just like that minus pickles. Or they say, I want a sandwich like the one I had last week. So you just duplicate his 'order' for today.

From what I read so far:


SandwichEntity sandwich = new SandwichEntity();
sandwich.Pickles = true;
//etc.
//At this point, sandwich.Save() may or may not be called.

SandwichEntity sandwich2 = new SandwichEntity();
sandwich2.Fields = sandwich.Fields;

That should copy every field into the new object?
If so, if 'sandwich' was saved, does it copy the primary key?
Would it still have IsNew==true? Will sandwich2.Save() create a new record every time, regardless of sandwich being saved or not?

Is this the best way to do this?

Walaa avatar
Walaa
Support Team
Posts: 14981
Joined: 21-Aug-2005
# Posted on: 30-Mar-2006 08:33:18   
Posts: 1263
Joined: 10-Mar-2006
# Posted on: 06-Apr-2006 06:04:21   

I am going to need some more help here. I have read all those posts and still do not see a definitive answer. I tried the serialization and it does not work for me. It seems most people are trying to get duplicate object to pass around for some reason. I am trying to create a second unique entity, using values from the first one to start with (deep cloned).

Take the following code:


SandwichEntity sandwich = new SandwichEntity();
sandwich.Pickles = true;
sandwich.Lettuce = true;
sandwich.Toasted = true;
//other properties....
sandwich.Kind ="Ham and Cheese";

//now make me a sandwich just like the one I just ordered, but no pickles...
SandwichEntity anotherSandwich = CloneEntityUsingBinarySerialization(sandwich);
anothersandwich.Pickles = false;

Order.DetailItem.Add(sandwich);
Order.DetailItem.Add(anotherSandwich);

Order.Save();

This results in only ONE detail record being produced. I think that has something to do with the Entities having the same ObjectId.

If you change the ObjectId with anotherSandwich.ObjectId = Guid.NewGuid(), then if you have any collections hanging off of SandwichEntity, they start giving errors (perhaps because they also all need new ObjectIds?)

Walaa avatar
Walaa
Support Team
Posts: 14981
Joined: 21-Aug-2005
# Posted on: 06-Apr-2006 07:57:14   

I bet the Order.DetailItem.Count will be just one before the Order.Save(). Please check it.

If so then use Order.DetailItem.DoNotPerformAddIfPresent = false; just before adding any entity to the collection.

If this doesn't solve your problem, please post the code inside CloneEntityUsingBinarySerialization().

Thanks.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39740
Joined: 17-Aug-2003
# Posted on: 06-Apr-2006 10:48:15   

Be sure IsDirty is set to true on the Fields object in the clone.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 1263
Joined: 10-Mar-2006
# Posted on: 06-Apr-2006 22:10:55   

I bet the Order.DetailItem.Count will be just one before the Order.Save(). Please check it.

If so then use Order.DetailItem.DoNotPerformAddIfPresent = false; just before adding any entity to the collection.

The count is 2 on the collection. I can print the entire collection out and everything looks great.

De/Serialization is very basic.


    private SandwichEntity CopyCartItem(SandwichEntity itemToCopy)
    {
        MemoryStream memoryStream = new MemoryStream();
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        binaryFormatter.Serialize(memoryStream, itemToCopy);
        memoryStream.Seek(0, SeekOrigin.Begin);
        SandwichEntity itemCopy = (SandwichEntity)binaryFormatter.Deserialize(memoryStream);
        return itemCopy;
    }

Be sure IsDirty is set to true on the Fields object in the clone.

Note the object I am cloning has NEVER been saved! So, IsNew=true, IsDirty=true, all fields that were changed are still marked change, etc.

This is crazy any other help?

Posts: 1263
Joined: 10-Mar-2006
# Posted on: 06-Apr-2006 23:24:13   

In order to save time of going back and forth on this, I have created a very simple demo project showing this behavior.

It is a one page website. When you view the default.aspx page, it will create an order with one detail item in the order. Click on the 'duplicate' button as many times as you like. That duplicates that order item and changes a few properties on it, adds it to the order.

Click 'save' button - it will refresh the page, reload the details for that order and you will see they did not save out!

To retry the demo, close the webpage and relaunch - it will create the new order, etc.

http://www.waynebrantley.com/demosite.zip

Zip file contains the website, project files, LLBLGen file and all runs agains SQL Server and NorthWind database.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39740
Joined: 17-Aug-2003
# Posted on: 07-Apr-2006 11:30:44   

I ran your demo, and when I click save it saves 1 row, not the 5 I created, that's indeed odd. (now looking into code)

(edit) Ah, I know what's the reason. When you clone an entity, using serialization, it's an exact copy. This means the ObjectID property also has the same value. If you then add 10 copies to the orderDetails collection, the core thinks you just added the same entity 10 times. When the queue is constructed and it wants to process the second entity in the list, it sees that the entity is already in the queue (the ObjectID is unique for all entity instances) and thus it skips the entity, which means that there's just 1 entity saved, not all of them.

If you want to copy an entity, just clone its fields and it's copied. The ObjectID isn't part of the fields. However take note why exactly you want to have a clone to work with. If it's a DIFFERENT entity, create a new INSTANCE, don't clone.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 1263
Joined: 10-Mar-2006
# Posted on: 07-Apr-2006 20:57:37   

I suspected it was related to that ObjectId.

Now, given what you said, can you help me with my original problem?

I have an object with bunch of properties (fields), FK 1:1 relationships to other entities, that entity has FK 1:n relationships, etc. All this is filled out by the user much like my simplified demo.

Now, the user wants to create a new object (remember we have not saved anything yet) and have it based on the one that is already created.

How can I do that? I want a DIFFERENT entity, but I want it to start with all the same values of my existing (unsaved) entity, deep copy. Seems like a simple thing, but under the given framework, it is very difficult.

I have played around with



        OrderDetailEntity result = new OrderDetailEntity();
        result.Fields = (EntityFields)((EntityFields)itemToCopy.Fields).Clone();
        InitializeEntityChanged(result);

        result.AnEntity_InHierarchy = new AnEntity_InHierarchy();
        result.AnEntity_InHierarchy.Fields = (EntityFields)((EntityFields)itemToCopy.AnEntity_InHierarchy.Fields).Clone();
        InitializeEntityChanged(result.AnEntity_InHierarchy);

        foreach (SomeEntity item in itemCopy.SomeEntityCollection)
        {
            SomeEntity newItem = new SomeEntity();
            newItem.Fields = (EntityFields)((EntityFields)item.Fields).Clone();
            InitializeEntityChanged(newItem);
            result.SomeEntityCollection.Add(newItem);
        }

        foreach (YetAnotherEntity item in itemCopy.AnEntity_InHierarchy.YetAnotherEntityCollect)
        {
            YetAnotherEntity newItem = new YetAnotherEntity();
            newItem.Fields = (EntityFields)((EntityFields)item.Fields).Clone();
            InitializeEntityChanged(newItem);
            result.YetAnotherEntityCollect.Add(newItem);
        }


    private void InitializeEntityChanged(EntityBase entity)
    {
        entity.Fields.IsDirty = true;
        for (int i = 0; i < entity.Fields.Count; i++)
        {
            entity.Fields[i].IsChanged = true;
        }
        entity.IsNew = true;
    }

But had limited success with this too, as it would seem like it worked, but the save would aways give errors.

I also toyed with changing the ObjectId of every entity and sub-entity, but I seemed to get some wild and crazy errors......

I dont know, this is driving me crazy, so simple, yet going to take a week to get working, if at all....

Your help is appreciated.

Posts: 1263
Joined: 10-Mar-2006
# Posted on: 07-Apr-2006 21:14:50   

I would think I could just change the ObjectId, so I tried using the same sample I posted before.

Previously I had this:


    protected void btnDuplicate_Click(object sender, EventArgs e)
    {
        OrderDetailEntity orderDetailEntity = CopyItem(testOrder.OrderDetail[0]);
        orderDetailEntity.Discount = .05F;
        testOrder.OrderDetail.Add(orderDetailEntity);
        LoadGrid();
    }

I changed it to this:


    protected void btnDuplicate_Click(object sender, EventArgs e)
    {
        OrderDetailEntity orderDetailEntity = CopyItem(testOrder.OrderDetail[0]);
        orderDetailEntity.Discount = .05F;
        orderDetailEntity.ObjectID = Guid.NewGuid();
        orderDetailEntity.ProductId += testOrder.OrderDetail.Count;  //avoid pkey issues
        testOrder.OrderDetail.Add(orderDetailEntity);
        LoadGrid();
    }

That does work, and it now saves out correctly. So, that begs the question - if I were to interate over every object contained by the one I duplicated and set new objectid values, would that take care of the situation????

Posts: 1263
Joined: 10-Mar-2006
# Posted on: 07-Apr-2006 23:28:27   

I thought I would write the code to reset every objectId in the cloned object, so I did this:


    private OrderDetailEntity CopyItemAndResetObjectId(OrderDetailEntity itemToCopy)
    {
        MemoryStream memoryStream = new MemoryStream();
        // Construct a BinaryFormatter and use it to serialize the data to the stream.
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        binaryFormatter.Serialize(memoryStream, itemToCopy);
        memoryStream.Seek(0, SeekOrigin.Begin);
        OrderDetailEntity itemCopy = (OrderDetailEntity)binaryFormatter.Deserialize(memoryStream);
        ResetObjectIDs(itemCopy);
        return itemCopy;
    }

    private void ResetObjectIDs(EntityBase entity)
    {
        entity.ObjectID = Guid.NewGuid();
        ArrayList relatedEntities = entity.GetDependingRelatedEntities();
        foreach (EntityBase relatedEntity in relatedEntities)
        {
            if (relatedEntity != null)
            {
                relatedEntity.ObjectID = Guid.NewGuid();
                ResetObjectIDs(relatedEntity);
            }
        }

        ArrayList relatedCollections = entity.GetMemberEntityCollections();
        foreach (EntityCollectionBase relatedCollection in relatedCollections)
        {
            foreach(EntityBase collEntity in relatedCollection)
            {
                ResetObjectIDs(collEntity);
            }
        }
    }

That part seemed to work, but the cloned object will not save if as in the above example OrderDetailEntity has a 1:n relationship off of it with items in that relationship. It does not insert the FK into the related table - in fact the insert query that is generated does not even output that as one of the fields it is changing. I don't really understand how the primary key from a parent entity gets shoved into the child entity during a save, so hard for me to know what is happening.

Any ideas?

Posts: 1263
Joined: 10-Mar-2006
# Posted on: 08-Apr-2006 00:02:59   

As before, I have a demo using Northwind database to show this problem.

http://www.waynebrantley.com/DemoSite2.zip

If you downloaded the previous demo and still have it, the only thing that changed is the default.cs and default.aspx in the website project.

This demostrates the previous post, where the FK is not populated on a copied entity.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39740
Joined: 17-Aug-2003
# Posted on: 08-Apr-2006 12:35:10   

What I fail to understand is this:

you first write:

WayneBrantley wrote:

... Now, the user wants to create a new object (remember we have not saved anything yet) and have it based on the one that is already created.

How can I do that? I want a DIFFERENT entity, but I want it to start with all the same values of my existing (unsaved) entity, deep copy. Seems like a simple thing, but under the given framework, it is very difficult.

I have played around with



        OrderDetailEntity result = new OrderDetailEntity();
        result.Fields = (EntityFields)((EntityFields)itemToCopy.Fields).Clone();
        InitializeEntityChanged(result);
...

But had limited success with this too, as it would seem like it worked, but the save would aways give errors.

and then come up with a very complex system while all you had to do was:


OrderDetailsEntity clone = new OrderDetailsEntity();
foreach(EntityField field in source.Fields)
{
    clone.SetNewFieldValue(field.FieldIndex, field.CurrentValue);
}

Or even your routine I quoted above.

Though, and I marked that in bold, is that you apparently ran into errors but I can't find which errors you ran into with the 3 lines I quoted from you, which creates an exact copy of the FIELDS (and thus values!) of an entity in a NEW instance. You then still have to assign the new entity to related entities of course. And that essential thing perhaps was what you didn't do, I don't know.

It would help us a great deal, and thus you too, if you elaborate more specifically about the errors you run into when you try something. We can't see them, and therefore can't provide you with the proper solution as we'll go into guess-mode what might be wrong.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 1263
Joined: 10-Mar-2006
# Posted on: 10-Apr-2006 15:28:14   

It would help us a great deal, and thus you too, if you elaborate more specifically about the errors you run into when you try something. We can't see them, and therefore can't provide you with the proper solution as we'll go into guess-mode what might be wrong.

Perhaps you failed to read my most recent post above where I state specifically what is happening AND provide an example that shows it?

I think I know your confusion - you think I have created lots of code to do, what is a simply copy of an entity. That is not correct. I need to duplicate the ENTIRE object graph - all related entities, etc. There are 1:1 relationships and 1:n relationships and I need EVERY one of them duplicated in this new entity - so I need a DEEP CLONE. The best way to do that is to seralize and deserialize, which I did. As you quickly discovered above, that causes a problem as the objectIds are the same and the save code ignores them. So, that leads me to what I did next - and that is regenerate every objectid in the clone, which then leads me to your save code causing a FK violation.

Again, I have provided a simple Northwind example above, that will duplicate an order entity and add it to the customer entity - then will fail to save when you save the customer.

I think I am very close to a solution here - I just need you to look at my demo.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39740
Joined: 17-Aug-2003
# Posted on: 11-Apr-2006 10:20:14   

WayneBrantley wrote:

It would help us a great deal, and thus you too, if you elaborate more specifically about the errors you run into when you try something. We can't see them, and therefore can't provide you with the proper solution as we'll go into guess-mode what might be wrong.

Perhaps you failed to read my most recent post above where I state specifically what is happening AND provide an example that shows it?

I did read that, but it's not efficient to first read a lot of code, try to grasp what the code does and reverse engineer the problem from that code. It's more efficient to take a step back and analyse what the person wanted to do in the first place, then come up with a solution for that and THEN compare it with what's written.

I think I know your confusion - you think I have created lots of code to do, what is a simply copy of an entity. That is not correct.

I indeed assumed that, because that's what you talked about in this thread AND in the code example! So of course I then didn't assume you were required to clone an entire graph.

Sometimes people get locked into a given thinking-path and if the solution isn't on that path, it's a struggle to get to that solution, as the person first has to snap out of the thinking path, which was my reason to ask why you didn't simply use a simple loop to fill the orderdetail entity in your demo.

I need to duplicate the ENTIRE object graph - all related entities, etc. There are 1:1 relationships and 1:n relationships and I need EVERY one of them duplicated in this new entity - so I need a DEEP CLONE. The best way to do that is to seralize and deserialize, which I did. As you quickly discovered above, that causes a problem as the objectIds are the same and the save code ignores them. So, that leads me to what I did next - and that is regenerate every objectid in the clone, which then leads me to your save code causing a FK violation.

Please consider that if you clone entity objects, you have new data (entities) but the same looking buckets (entity objects). Without any action, you'll likely run into problems. So you then have to manipulate the clones to make them look like new entities.

There's a problem internally. The sync information is based on ObjectID's, as that's the only unique value of an entity object which is easy to use as an index.

When you do: myOrder.Customer = myCustomer; (or myCustomer.Orders.Add(myOrder); ) synchronization information is stored to be able to sync myCustomer with myOrder at save time. As two entities can have multiple relations, and you can set each relation reference to a different instance (think in myCustomer.VisitingAddress and myCustomer.BillingAddress, both set to a different Address instance), the objectID is used to determine which sync info to use when a related entity is saved. So when myCustomer is saved, it signals its related FK sides and those are then looking into their sync info if they have sync info stored for the entity with a given ObjectID. If they do, they sync their fk fields based on the info in that sync info. This is all done behind the scenes.

Now, you already feel what's coming now of course, and that's that the sync info is using the old objectid's and therefore syncing doesn't take place at save time, because the stored objectid's in the sync info are different (the old ones). This happens only if you clone new entities in a graph. Existing entities in a graph are already synced and you don't run into this problem.

Now, you can of course opt for manipulating the sync info as well, but that's hidden for you and you shouldn't mess with that manually anyway.

What I now want to know is: why do you need clones of ALL entities in the graph? If you have a graph of 4 different entities and the user has to alter 1 and the other three are references, isn't it better to use a context and simply manually build the cloned graph, clone only the one which has to be edited (without serialization, just clone the fields) and set the referenced entities using the context, OR by setting the references to manually cloned entities (in the case of 1:1 entities)? After all, you just want to have new entities and not copies, the new ones just happen to have the same values for some fields.

Frans Bouma | Lead developer LLBLGen Pro
rblinton
User
Posts: 12
Joined: 11-Apr-2006
# Posted on: 11-Apr-2006 18:15:11   

Hi Wayne,

I'm a bit confused after reviewing your request

I need to duplicate the ENTIRE object graph - all related entities, etc. There are 1:1 relationships and 1:n relationships and I need EVERY one of them duplicated in this new entity - so I need a DEEP CLONE.

Are you saying the following:

Assume you have a "Sliced Pickle" entity, and you use that "Sliced Pickle" on Sandwich1. You then create Sandwich2 and use a "Sliced Pickle" on that one too. When you say you want to duplicate the entire graph are you saying that you want to create a new "Sliced Pickle" entity and not reuse the existing "Sliced Pickle" entry?

I'm sure I've misinterpreted your intentions, because it doesn't seem to make sense to create a bunch of duplicate "Sliced Pickle" entities...if you are keeping an inventory of "Sliced Pickles" using a StockLevel member this would create an administration nightmare. If that's not your intent, the simple member copy algorithm provided by Otis will work perfectly.

Thanks for clarifying

Rob

Posts: 1263
Joined: 10-Mar-2006
# Posted on: 11-Apr-2006 19:07:14   

Clearly, I am not being clear simple_smile

What I now want to know is: why do you need clones of ALL entities in the graph? If you have a graph of 4 different entities and the user has to alter 1 and the other three are references, isn't it better to use a context and simply manually build the cloned graph, clone only the one which has to be edited (without serialization, just clone the fields) and set the referenced entities using the context, OR by setting the references to manually cloned entities (in the case of 1:1 entities)? After all, you just want to have new entities and not copies, the new ones just happen to have the same values for some fields.

Using the sandwich analagy above, here is the scenario.

A user of your website wants two sandwiches. So, using the website, they build the first sandwich - a Ham & Turkey, tomato, lettuce, black olives, green olives, salt, pepper, mayo, mustard. For the purpose of showing the need for the deep clone, lets say they can have up to 5 letters carved into the sandwich. So the sandwich entity contains a collection of 'letterscarved', where every entity in that collection is 'new'. They put this in their cart - which for this example, we will store in the new asp2.0 user profile - which serializes the object out and keeps it.

Now, for the second sandwich, the user is saying, I want it exactly like the first sandwich, except, no mayo. So, the idea is to duplicate the sandwich and let them edit it.

So, from programming point of view, I want to copy the ENTIRE first entity and remove the mayo. Lets assume that this sandwich contains 1:1 relationships for some of the items above and I already described a 1:n relationship.

Once that sandwich is duplicated, the user then actually submits the order from the items stored in the profile - at this time, we do an Order.Save(true) and want everything saved out.

In this case a simple fields.Clone() will not work, as it does not do a deep clone. So, what is the best way to get a duplicate.

I guess I could do 'new SandwichEntity()' and examine the object I have, creating new enties and everything for my copied object, but I want to do it 'generically', where if a new property, entity or entity collection is added to the sandwich, I will not have to come back in and specifically code the copy for that new relationship.

Assume you have a "Sliced Pickle" entity, and you use that "Sliced Pickle" on Sandwich1. You then create Sandwich2 and use a "Sliced Pickle" on that one too. When you say you want to duplicate the entire graph are you saying that you want to create a new "Sliced Pickle" entity and not reuse the existing "Sliced Pickle" entry?

rblinton - What I actually need is the reference and can reuse the existing "Sliced Pickle", but sometimes the entities or entities in a collection attached to the sandwich are 'new' and do not exist yet - further they are instance data - like the "letters on a sandwich" that ARE created for every sandwich - not re-used. So, on the above, I would reuse enties for Pickle, Lettuce, etc - but would not reuse the entities in the collection 'letterscarved'. From a programming point of view, I would NOT reuse them if 'IsNew=true' and would reuse them if 'IsNew=false'.

In theory, the fully cloned entity (using serialization) would provide EXACTLY what I need, it is just that it will not re-save out due to the internal way it keeps tracks of references - which I have been told to NOT MESS WITH ;-) So, I basically need to create a new entity, that has the same values as an existing entity, using deep copy (if it is a reference to an existing entity, use that reference, if it is a new entity, then make a new entity with the same properties)

Clear as mud? confused

Walaa avatar
Walaa
Support Team
Posts: 14981
Joined: 21-Aug-2005
# Posted on: 12-Apr-2006 09:12:15   

I think the problem can be solved in a different way. Please follow me on this.

First how the user choose his first sanwitch? Most probably he check some boxes and make some selections in combows and or edit text boxes.

Then he should say add to be added to his cart or so, before he chooses another one, that's where you read his selections and edits and compose your sandwich object and store it somewhere.

Then normally if he wants to add another sandwich, he should do the same select some items and check some boxes and edit some text boxes, then he adds them to his cart and that's where you also read his selections and compose another sandwich, and again you should store it somewhere.

This is the normal scenario, BUT it seems you give the user a nice feature to duplicate his previous sandwich and edit it if he wants instead of making the same selections again.

That's where you should make those UI selections and edits for him, you may read the previuosly saved sandwich object and fill the form controls with his previous selections, so he may uncheck or edit some of them, and then when he press add to the cart, the same method is run again, which reads his selections and edits and store them in the same way as a new sanditch.

And if you want, you may leave every thing in its place when he adds a a sandwitch to his cart, for the sace of adding another similar one with slightly some differences, saving you the trouble of reading the previously saved item and re-filling the form. With a nice button to clear the form if he wishes to add something completly different.

And for that above scenario I see no need for clonning at all. Simply when the user chooses to have another item similar as the one previously saved, go grab that one and re-fill the form for the user. And that's where the special feature part should end.

Posts: 1263
Joined: 10-Mar-2006
# Posted on: 12-Apr-2006 14:50:41   

I have followed what you are saying. And from yours and Otis's responses - I am guessing what I want to do simply cannot be done with the framework, which is why the answers are 'why are you doing this and how about this for a workaround'.

What you are saying below is 'ok', but I am not really selling sandwiches of course - and in my situation I actually need the item duplicated - because what happens is when they fill out the wizard we have, there are certain times when......

Lets say when they order their sandwich, they order quantity of 6. At the end of the order process, we determine that what they actually have done we should actually make two cart items, each with quantity of 3, and change this and that on each detail item for them.

That is just a requirement - I just need to duplicate that item, like I indicated above - is there no way to do it????

I guess I can manually walk through duplicating all the contained entities and the entities those entities contain - and if new sandwich properties get introduced will have to adjust that code since the framework cannot handle this????

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39740
Joined: 17-Aug-2003
# Posted on: 12-Apr-2006 16:11:10   

There are an unlimited amount of ways to write the same application. The framework can't possibly support all of them, it has to support a limited (it's always limited) set of features with which you can build your application. Instead of bending the framework to match your app, one should bend the app to match the framework. That's always the case with frameworks, be it llblgen pro or .net or whatever. If you want to deny that, I'd recommend you to port a framework from .NET 1.x to .NET 2.0 generics and also support asp.net 2.0 design time databinding.

Our questions related to 'why are you doing this' etc. are asked so we can understand why you're trying to do ABC so we can advice you to do things differently so it does match the framework. You ask us how to solve your cloning routine. Instead we ask why you need the cloning so perhaps there's a different way to do things so you can AVOID the cloning.

I've explained why the serialization/deserialization cloning doesn't work if you rewrite the objectid's, thats something which is a result of the way the framework is build AND the utilization of serialization/deserialization for cloning, which is a trick, not a general way to do things.

That is just a requirement - I just need to duplicate that item, like I indicated above - is there no way to do it????

Of course there are ways, but as well limitations to what you can do with the various ways of duplicating an item.

The design you've chosen, e.g. at the end some checks which then have to re-build the datastructures that are checked, has limitations too: you then need cloning. Another design perhaps would have been better, (generally speaking) which would result in a simpler setup.

Now you're faced with a problem, and you turn to us to solve it for you. I want to go very far for my customers and add functionality to the framework if that helps them, but even if I do so, there will be limitations and there will be situations in which you will run into them.

take this quote:

Lets say when they order their sandwich, they order quantity of 6. At the end of the order process, we determine that what they actually have done we should actually make two cart items, each with quantity of 3, and change this and that on each detail item for them.

This suggests you have to go through the whole graph anyway to check values, alter things etc. Cloning wouldn't have helped you here to avoid that phase, and because you already have a requirement of fixing every entity in the graph, you can at the same time rebuild the graph with new objects. Because checks for the split are entity specific, you won't avoid specific code for each item which will run into the check which results in a split.

So, even if I invest time to write clone method, would that have helped you avoid specific code? No, not a single bit.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 1263
Joined: 10-Mar-2006
# Posted on: 13-Apr-2006 15:39:06   

Otis, You are preaching to the choir. I understand and generally agree with everything you are saying.

There are an unlimited amount of ways to write the same application. The framework can't possibly support all of them, it has to support a limited (it's always limited) set of features with which you can build your application. Instead of bending the framework to match your app, one should bend the app to match the framework. That's always the case with frameworks, be it llblgen pro or .net or whatever. If you want to deny that, I'd recommend you to port a framework from .NET 1.x to .NET 2.0 generics and also support asp.net 2.0 design time databinding.

I hear you and that is fine, but in my initial request - would have been best to just say, you cannot duplicate an object the way you are wanting to because of x,y,z.

Our questions related to 'why are you doing this' etc. are asked so we can understand why you're trying to do ABC so we can advice you to do things differently so it does match the framework. You ask us how to solve your cloning routine. Instead we ask why you need the cloning so perhaps there's a different way to do things so you can AVOID the cloning.

Sure, however, if you just said 'cannot be done', then I would obviously find another way 10 messages ago.

I've explained why the serialization/deserialization cloning doesn't work if you rewrite the objectid's, thats something which is a result of the way the framework is build AND the utilization of serialization/deserialization for cloning, which is a trick, not a general way to do things.

Yes you did - and I appreciate that and have since abandoned that approach.

The design you've chosen, e.g. at the end some checks which then have to re-build the datastructures that are checked, has limitations too: you then need cloning. Another design perhaps would have been better, (generally speaking) which would result in a simpler setup.

Now you're faced with a problem, and you turn to us to solve it for you. I want to go very far for my customers and add functionality to the framework if that helps them, but even if I do so, there will be limitations and there will be situations in which you will run into them.

Under my requirements, unfortunately, the design is a must. Do not get me wrong, I appreciate your responses and responsiveness.

This suggests you have to go through the whole graph anyway to check values, alter things etc. Cloning wouldn't have helped you here to avoid that phase, and because you already have a requirement of fixing every entity in the graph, you can at the same time rebuild the graph with new objects. Because checks for the split are entity specific, you won't avoid specific code for each item which will run into the check which results in a split.

Suggests, is a good word - as that is not true - in my application, we know ahead of time EXACTLY what to change - so we only change those specific things (very few things) and do not have to traverse the whole graph.

So, even if I invest time to write clone method, would that have helped you avoid specific code? No, not a single bit.

Perhaps, perhaps not. If you made the cloning work in the last demo I prepared for you using Northwind, then cloning would absolutely help me in this case. I would still have specific code for a couple of specific changes to the entities, but I would not have specific code for every contained entity and every relationship in the object!

Given that the cloning will not work in the given framework, I just wrote the code to duplicate the entity. In case it would help anyone in the future here is how it works:

First you need a method that will copy an entity. The entity might represent something in the database or it might be a 'new' entity. This method will NOT copy any contained entity objects or object collections - and this is due to the framework, you need to duplicate those each manually.



    private void CopyFieldsInEntity(EntityBase srcEntity, EntityBase destEntity)
    {
        if (!srcEntity.IsNew)
            destEntity.Fields = (IEntityFields)((EntityFields)srcEntity.Fields).Clone();

        if (srcEntity.IsNew || srcEntity.IsDirty)
        {
            foreach (EntityField field in (EntityFields)srcEntity.Fields)
            {
                if (field.IsChanged)
                    destEntity.SetNewFieldValue(field.FieldIndex, field.CurrentValue);
            }
        }
    }

Then you just write specific code for what you are trying to duplicate:


    private OrderDetailEntity CopyItemManually(OrderDetailEntity itemToCopy)
    {
        //copy the primary entity
        OrderDetailEntity result = new OrderDetailEntity();
        CopyFieldsInEntity(itemToCopy, result);

        //write code to copy any contained enties
        result.SomeContainedEntity = new SomeContainedEntity();
        CopyFieldsInEntity(itemToCopy.SomeContainedEntity, result.SomeContainedEntity);

        //write code to copy any entity collections
        foreach (EntityBase item in itemToCopy.Some1-NCollection)
        {
            EntityBase newItem = (EntityBase)result.Some1-NCollection.AddNew();
            CopyFieldsInEntity(item, newItem);
            //if newItem in the foreach below has contained entities or contained collections, you have to 
            //do this same sort of thing 'recursively'
        }

        //write code to copy any entity collections
        foreach (EntityBase item in itemToCopy.AnotherCollection)
        {
            EntityBase newItem = (EntityBase)result.AnotherCollection.AddNew();
            CopyFieldsInEntity(item, newItem);
        }

        //likewise if contained entities have collections, those have to be duplicated too..
        foreach (EntityBase item in itemToCopy.SomeContainedEntity.ACollection)
        {
            EntityBase newItem = (EntityBase)result.SomeContainedEntity.ACollection.AddNew();
            CopyFieldsInEntity(item, newItem);
        }

        return result;
    }

Of course if you look at the above code, this is setup such that it could be generated by the framework easily by modifing the templates. If each entity supported this, then what could be complex code above would be greatly simplified.

In my case, I do not ever duplicate UP the relationship - so if you are duplicating an object that is the 'N', in a 1:N relationship, I do not do a full entity copy of the '1' side when duplicating the 'N'.

Just to be clear - I am happy with the framework, happy with the support and am very greatful for all your suggestions and responses!!

Thanks again simple_smile

Walaa avatar
Walaa
Support Team
Posts: 14981
Joined: 21-Aug-2005
# Posted on: 13-Apr-2006 15:59:15   

Just to be clear - I am happy with the framework, happy with the support and am very greatful for all your suggestions and responses!!

That's the best thing I've heared lately simple_smile