Cloning Entities

Posts   
 
    
ZaneZ
User
Posts: 31
Joined: 21-Dec-2004
# Posted on: 13-Jul-2005 22:20:30   

Copy Entities

I know that I’ve asked this question before and I was advised to use the Serialize method in order to clone an entitiy. However using the Serialize method on a very large entity isn’t efficient. So I created a method for each entity Manager to copy an entity (this method copies only the necessary related entity collections by traversing each collection and cloning the fields of each entity that's needed). This also isn’t efficient because each manager would need the same type of method (inefficient code). I was wondering if there was another way, possibly using the WriteXML method, of cloning an entity and all its related data.

Side note. This is a major problem in our current application. It’s a C# windows .NET app and the problem occurs when we create a form that takes in a pre-loaded entity to manipulate. Basically we don’t want the same entity to be updated in the form because if the form is cancelled we don’t want the entity to be updated. So we copy the entity and pass it into the form, so when OK is hit, the temporary entity within the form is copied back to the original. Going back and forth from the database just to open a form and update an entity isn’t an option. Any Suggestions?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 14-Jul-2005 10:27:43   

ZaneZ wrote:

Copy Entities

I know that I’ve asked this question before and I was advised to use the Serialize method in order to clone an entitiy. However using the Serialize method on a very large entity isn’t efficient. So I created a method for each entity Manager to copy an entity (this method copies only the necessary related entity collections by traversing each collection and cloning the fields of each entity that's needed). This also isn’t efficient because each manager would need the same type of method (inefficient code). I was wondering if there was another way, possibly using the WriteXML method, of cloning an entity and all its related data.

There are two things: 1) cloning a single entity 2) cloning an entity and all its related entities.

in case of 1), you can do (I'm cloning myCustomer here for adapter: )


CustomerEntity clone = new CustomerEntity();
clone.Fields = ((EntityFields2)myCustomer.Fields).Clone();

this now gives you a perfect clone of the entity's fields. WriteXml is slower than binary serialization to a memorystream, so in case of 2) I'd opt for the memorystream trick, which is a generic piece of code.

However, reading your sidenote, I think there is an easier method simple_smile ->

Side note. This is a major problem in our current application. It’s a C# windows .NET app and the problem occurs when we create a form that takes in a pre-loaded entity to manipulate. Basically we don’t want the same entity to be updated in the form because if the form is cancelled we don’t want the entity to be updated. So we copy the entity and pass it into the form, so when OK is hit, the temporary entity within the form is copied back to the original. Going back and forth from the database just to open a form and update an entity isn’t an option. Any Suggestions?

Entities have field versioning build in simple_smile . So if you want to roll back an entity's fields to a previous set of values, use this:


myCustomer.SaveFields("BeforeForm");
// here you open your form, pass in myCustomer
MyForm editForm = new MyForm();
editForm.ToEdit = myCustomer;
DialogResult result = editForm.ShowDialog(null);

if(result == DialogResult.Cancel)
{
// cancel clicked, roll back
myCustomer.RollbackFields("BeforeForm");
}
else
{
// save entity
}

This is per entity, so it doesn't version related entities. Research on this has shown that it can be done, but versioning of graphs is pretty complex, though not impossible simple_smile Graph versioning is planned.

Frans Bouma | Lead developer LLBLGen Pro
wvnoort
User
Posts: 96
Joined: 06-Jan-2005
# Posted on: 14-Jul-2005 14:40:04   

Otis wrote:

There are two things: 1) cloning a single entity 2) cloning an entity and all its related entities.

in case of 1), you can do (I'm cloning myCustomer here for adapter: )


CustomerEntity clone = new CustomerEntity();
clone.Fields = ((EntityFields2)myCustomer.Fields).Clone();

this now gives you a perfect clone of the entity's fields.

I think there is a little typo in the code. It should be:


CustomerEntity clone = new CustomerEntity();
clone.Fields = (IEntityFields2)((EntityFields2)myCustomer.Fields).Clone();

The clone methods returns according to the reference manual 'an exact, deep copy of this EntityFields2 object and its contents'. Will this lead to problems with read-only fields like auto numbered ID's?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 14-Jul-2005 15:35:04   

wvnoort wrote:

Otis wrote:

There are two things: 1) cloning a single entity 2) cloning an entity and all its related entities.

in case of 1), you can do (I'm cloning myCustomer here for adapter: )


CustomerEntity clone = new CustomerEntity();
clone.Fields = ((EntityFields2)myCustomer.Fields).Clone();

this now gives you a perfect clone of the entity's fields.

I think there is a little typo in the code. It should be:


CustomerEntity clone = new CustomerEntity();
clone.Fields = (IEntityFields2)((EntityFields2)myCustomer.Fields).Clone();

Thanks for that, indeed you have to cast back, Clone gives back an object.

The clone methods returns according to the reference manual 'an exact, deep copy of this EntityFields2 object and its contents'. Will this lead to problems with read-only fields like auto numbered ID's?

Well, you have to realize that you've made an exact clone, so if you save the original and then the clone, you'll perhaps get two inserts. It won't give problems with readonly fields, as the clone creation routine uses a shortcut routine to copy the values over. In fact, when you add an entity to a transaction this same technique is used (also used inside the SaveFields() routine) to make sure that when an entity is succesfully saved and another one rolls back the transaction, an already read identity value is rolledback in the entity as well...

Frans Bouma | Lead developer LLBLGen Pro
ZaneZ
User
Posts: 31
Joined: 21-Dec-2004
# Posted on: 01-Sep-2005 22:06:55   

Otis wrote:

Research on this has shown that it can be done, but versioning of graphs is pretty complex, though not impossible simple_smile Graph versioning is planned.

Hi Otis, you were right about the serialization being faster than the WriteXml function; however the serialization is still slow when dealing with large graphs. I wouldn’t be able to use the RolbackFields method because I not only want to rollback the field data, but rollback the entities that were added to the related entity collections.

Is Graph versioning still planned? Thanks!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 02-Sep-2005 11:01:59   

ZaneZ wrote:

Otis wrote:

Research on this has shown that it can be done, but versioning of graphs is pretty complex, though not impossible simple_smile Graph versioning is planned.

Hi Otis, you were right about the serialization being faster than the WriteXml function; however the serialization is still slow when dealing with large graphs. I wouldn’t be able to use the RolbackFields method because I not only want to rollback the field data, but rollback the entities that were added to the related entity collections.

Which is exactly why it is so complex wink

Is Graph versioning still planned? Thanks!

It's still planned, I have an idea how to solve it, but it will be tough simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Posts: 33
Joined: 31-May-2005
# Posted on: 19-Sep-2005 03:21:10   

On a similar note ... is there a good way to determine if an entity has changed since the SaveFields has been called?

We have a 'Save' button on our Windows Forms that are disabled until the user makes a change. Once they make a change, the 'Save' button is enabled until the record is saved or the change cancelled in some fashion. If they change any of the data back to the original (e.g., by pressing ctrl-z), we would like the 'Save' button to be disabled again.

I was hoping to use SaveFields to do this, since IsDirty doesn't seem to be reset when a value returns to its original (we're using databinding in .NET 2.0). After each firing of EntityContentsChanged, we'd compare the current values to the original, and enable/disable the 'Save' button accordingly.

Is this possible?

Thanks, Josh Lindenmuth

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 19-Sep-2005 10:09:23   

JoshLindenmuth wrote:

On a similar note ... is there a good way to determine if an entity has changed since the SaveFields has been called?

We have a 'Save' button on our Windows Forms that are disabled until the user makes a change. Once they make a change, the 'Save' button is enabled until the record is saved or the change cancelled in some fashion. If they change any of the data back to the original (e.g., by pressing ctrl-z), we would like the 'Save' button to be disabled again.

I was hoping to use SaveFields to do this, since IsDirty doesn't seem to be reset when a value returns to its original (we're using databinding in .NET 2.0). After each firing of EntityContentsChanged, we'd compare the current values to the original, and enable/disable the 'Save' button accordingly.

The IsDirty flag isn't reset, because it only compares the new value with the current value, it doesn't keep a history.

If you want to achieve what you describe above, I think you have to call SaveFields() after every change, and RollbackFields() after every cntrl-z. Then, after RollbackFields() check if IsDirty is still set and disable the button accordingly.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 33
Joined: 31-May-2005
# Posted on: 20-Sep-2005 03:40:29   

Otis wrote:

The IsDirty flag isn't reset, because it only compares the new value with the current value, it doesn't keep a history.

If you want to achieve what you describe above, I think you have to call SaveFields() after every change, and RollbackFields() after every cntrl-z. Then, after RollbackFields() check if IsDirty is still set and disable the button accordingly.

The problem is that the user may not always click ctrl-z. I'll use a simple dialog with a customer number textbox, a name textbox, and Save button as an example. When the customer opens the dialog, the Save button is disabled, the Customer number = 1, and the name = 'Frans'. If the user changes the name to 'Otis', the Save button should be enabled. If the user types 'Frans' in the name field, the Save would be disabled after exiting the field since the value is the same as what's in the database.

I was hoping there was some function that would allow me to compare the values saved by SaveFields with the current values. Or would it be better to clone an entity and compare the values that way (or would this not work?).

Thanks, Josh

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39614
Joined: 17-Aug-2003
# Posted on: 20-Sep-2005 11:24:57   

Hmm.

Well, the saved fields aren't available to you, so that's out of the window. Resetting the fields to an earlier version might not help as the sequence of values can be A -> B -> C -> A.

If you want this, I think the only real way to do this, is to copy the initial fields, by using: EntityFields2 copy = ((EntityFields2)myEntity.Fields).Clone();

then, compare after every change of a field, it's value with the initial value. If it's the same, reset the IsChanged flag. Then re-walk all fields to see if a field is changed, if so, keep IsDirty set otherwise reset it. Then, set/reset the Enabled property of the save button. You can do this in a general method.

Frans Bouma | Lead developer LLBLGen Pro