EntityCollection with DirtyEntities and DeletedEntities

Posts   
1  /  2  /  3
 
    
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 07-Apr-2006 10:31:57   

I'll spend some time thinking about this problem. I agree that a UoW is a bit low level for just tracking changes, and that the oddness of having saves being stored INSIDE the entity collection but deletes OUTSIDE the entity collection is something that can lead to the necessity of weird constructions you want to avoid.

I must say I find it hard to come up with a proper solution as well. In general most O/R mappers use a context / session like object and you tell it to perform the changes made, but that's also undesired because you hten need to have a live session/context to have it tracking changes and also don't have control over the order in which things happen, like deletes, which is a tricky issue (and unsolvable without help from the developer, as the o/r mapper has to pull in all data of related objects if an entity is deleted to reset fk fields first)

Frans Bouma | Lead developer LLBLGen Pro
sami
User
Posts: 93
Joined: 28-Oct-2005
# Posted on: 24-Apr-2006 13:33:08   

I just want to add my opinion about this issue, that I would ALSO like to see the deleted entities somehow.

I tried to implement my own solution for this by extending entitycollection, but of course it doesn't work when it comes to serialization/deserialization.

omar avatar
omar
User
Posts: 569
Joined: 15-Oct-2004
# Posted on: 25-Apr-2006 07:32:54   

sami wrote:

I just want to add my opinion about this issue, that I would ALSO like to see the deleted entities somehow.

I tried to implement my own solution for this by extending entitycollection, but of course it doesn't work when it comes to serialization/deserialization.

The other problem with extending your own EntityCollection in the BL is that the BL consumer (usually the UI) expects his data objects to come from the DAL (entitities and entityCollections) so it will be very artificial to demand the BL consumer to ignore the DAL.EntityCollection and use instead BL.MyEntityCollection.

Ofcourse the other option would be to totally hide the DAL from the UI, but then you have to re-create BL versions of everything (entities & entityCollections) for the UI.cry

sami
User
Posts: 93
Joined: 28-Oct-2005
# Posted on: 25-Apr-2006 08:25:17   

Actually what I did, was that I extented the entitycollectionadapter template. And I actually managed to get it working over .NET remoting. My entitycollections now holds internal references to removed entities. I had to put some stuff to ISerializable implementations for that.

sipboy
User
Posts: 2
Joined: 28-Aug-2006
# Posted on: 28-Aug-2006 05:25:00   

I agree with you. I just discovered that my life will be lot easier if the EntityCollection can track of deleted entities. Since adding an entity means the adding operations on the physical database, why this can not happen for deleting? I read Otis' comments regarding this issue, but that still can not convince me at all.

As a new user to the LLBLGen, I like the product so far, but this entitycollection deleting thing really is something I don't like, for me, it is quite annoying.

After searching the forum, it seems that there are some requests for this from other users as well, do you (LLBLGen) have any plan to implement this? Thanks a lot.

omar wrote:

sami wrote:

I just want to add my opinion about this issue, that I would ALSO like to see the deleted entities somehow.

I tried to implement my own solution for this by extending entitycollection, but of course it doesn't work when it comes to serialization/deserialization.

The other problem with extending your own EntityCollection in the BL is that the BL consumer (usually the UI) expects his data objects to come from the DAL (entitities and entityCollections) so it will be very artificial to demand the BL consumer to ignore the DAL.EntityCollection and use instead BL.MyEntityCollection.

Ofcourse the other option would be to totally hide the DAL from the UI, but then you have to re-create BL versions of everything (entities & entityCollections) for the UI.cry

Walaa avatar
Walaa
Support Team
Posts: 14950
Joined: 21-Aug-2005
# Posted on: 28-Aug-2006 07:33:58   

The UnitOfWork object is a collection that keeps track of deleted, inserted and updated entities.

sipboy
User
Posts: 2
Joined: 28-Aug-2006
# Posted on: 28-Aug-2006 15:11:53   

Things can be easier, that's the whole point to use LLBLGen.

Walaa wrote:

The UnitOfWork object is a collection that keeps track of deleted, inserted and updated entities.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 29-Aug-2006 10:46:00   

EntityCollection doesn't track deletes as it's ambiguistic: sometimes you want to remove an entity from the set of entities you have in memory but not from the db. This is because the entitycollection is a container for resultsets, not how it is in the db.

You can subscribe to events in the entitycollection to make things very easy: in the eventhandler of the event (EntityRemoved) you simply add your entity to the unitofwork.

Frans Bouma | Lead developer LLBLGen Pro
arschr
User
Posts: 893
Joined: 14-Dec-2003
# Posted on: 29-Aug-2006 13:36:45   

EntityCollection doesn't track deletes as it's ambiguistic: sometimes you want to remove an entity from the set of entities you have in memory but not from the db.

I must be missing something. disappointed Isn't making it non-ambiguous "just" a matter of providing syntax that allows you to specify which you want? For example: collection.remove[i] removes the entity from the collection but doesn't delete it. collection.IWantToDeleteThisEntityFromTheDataBaseOnSave[i] moves the entity to an internal (to the collection) collection of entitites to delete?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 29-Aug-2006 14:31:10   

Yes, but that would make EntityCollection a unitofwork object as well and as there's already a unitofwork object, this wasn't added. The unitofwork class is precisely there to collect actions to be executed in one go. What I don't understand is why people keep on trying to get all kinds of features, which are already available through other classes, fragmented across the framework into other classes which aren't designed for the purposes of the features. Why not use the class which is designed for the purpose?

The real cause of the confusion is the fact that you can use Add() to add an entity for save, but you can't use Remove() for adding an entity for delete.

This is the case because Add() isn't doing something for save. It's just that it happens to be the case that the collection can be used as a container to save entities as the entities to save are IN the collection. Remove() removes an entity from the collection, so it's not there anymore so the collection can't be used for that purpose.

Now, the question of course arises: why not keep the entities around after they're removed from the collection, inside a hidden structure in the entity collection? Well, you really don't want that, as that would keep a removed entity around and block garbage collection for that entity among other severe problems of syncing and referencing. Having this feature added to the entitycollection WILL cause these problems. It will add a 'hidden' set of entities which are still alive and will be kept around till the persistence action is executed. And what kind of persistence action would that be? It can't be SaveEntityCollection() as that means: save the changes to the entities in this collection. But delete is not a change to an entity, it's a delete.

An operation on a collection is an operation on the collection, not on the db.

As this discussion has been held a couple of times with the same answers from me, I will from now on refer to those discussions. In v2.0, events have been added to make using the unitofwork very easy in these scenario's.

Frans Bouma | Lead developer LLBLGen Pro
arschr
User
Posts: 893
Joined: 14-Dec-2003
# Posted on: 29-Aug-2006 16:34:06   

I'm not trying to drag this out, but ...

there's already a unitofwork

If the unit of work supported the binding interfaces that would do it.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 29-Aug-2006 16:54:07   

arschr wrote:

I'm not trying to drag this out, but ...

there's already a unitofwork

If the unit of work supported the binding interfaces that would do it.

I don't really see that as necessary. You should bind a handler to the EntityRemoved event of the bound collection. In the handler, add the entity to the unitofwork. Form done? Persist the unitofwork. simple_smile No hidden code/collections etc., everything is out in the open and available to you.

Frans Bouma | Lead developer LLBLGen Pro
Answer
User
Posts: 363
Joined: 28-Jun-2004
# Posted on: 29-Aug-2006 18:13:32   

Frans,

i understand the use of the unitofwork, and its a great idea and works wonderfully in scenerariors where you dont really have a business layer , but how is your business logic then suppose to work with it?

Say i have an order with order line items, i work with this order in the client app, then submit the data to a server using remoting,

So i have

public void ModifyOrder(OrderEntity orderToModify, EntityCollection<OrderLineItemEntity> lineItemsToDelete)

Inside this method, i then inspect the lineItemsToDelete, make sure they can be deleted from the order, and then adjust stock etc....how can i do this with a unitofWork? i cant see an immediate way of doing this, and every one of my business methods, is going to have basically the same signature

public  void ModifyOder(UnitOfWork2 workToBeDone)

It seems, i would be better off to host the BL ont eh client, and remote a datalayer?? Only that isnt really a good solution..

i have often thought of modifying the Entitycollection to hold deleted entities so i dont have to have such long method signatures..Adapter, wouldnt automatically delete them, but at least my buiness logic could easily work with the entities that should be deleted.

Something like,

EntityCollection.MarkAsDeleted()

Maybe put a flag on the entitiy collection that Remove() can eqaute to a deletion, or it doesnt have to...this would be usefull in databinding scenarions...

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 29-Aug-2006 20:22:55   

You should provide the data to the processing routine so the processing routine can fabricate a unitofwork which you then commit. You can also add work to the unitofwork beforehand, pass that on as well and do further processing elsewhere, adding work to the unitofwork.

As I said: keeping removed entities still in the entitycollection is an incredibly burden and nightmare and I'll never add that, simply because it promotes ambiguish usage and above all: it's obscure: you call Remove() but it's not really removed, some structure internally keeps a reference, and the removed entity might reference another entity which is the PK side of a relation and that entity is saved somewhere... whoops, your removed entity is saved as well: even though it's removed from the entity collection, you'll keep it around. The bad side is in the obscurity of things: it's not clear where what is held and what's referenced.

I'd advice you not to make the entitycollection just another unitofwork-ish object, which still contains removed entities, it will bring more misery than gain.

(edit). I'd like to add that I see an entitycollection with a combined unitofwork object as a DIFFERENT solution than an entitycollection which does the tracking by itself.

What you all really want IMHO is to have the delete tracking done by something (no matter what) which is then easily committable. Am I correct in this?

Frans Bouma | Lead developer LLBLGen Pro
Answer
User
Posts: 363
Joined: 28-Jun-2004
# Posted on: 30-Aug-2006 03:58:16   

What you all really want IMHO is to have the delete tracking done by something (no matter what) which is then easily committable. Am I correct in this?

Yes, but must be easily inspectable as well to accommodate the situation i described above....UnitofWork2 is not easily inspectable...

This is the one area where Datasets/DataTables really shine in my opinion..

currently i hold seperate collections in the UI, then pass them down to the BL like my previous post suggested. But when saving a large graph at once, this can get kinda out of hand....

EDIT:

I would like to add, that when im speaking of entitycollection "tracking" deletions, i dont mean, Remove() = delete in the database, but Remove(entity, bool moveToDeleted) would add the entity to the collection of deleted entities...AND adapter would not have AUTOMATICALLY delete these, they still could/would be deleted manually...

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 30-Aug-2006 09:28:11   

Answer wrote:

What you all really want IMHO is to have the delete tracking done by something (no matter what) which is then easily committable. Am I correct in this?

Yes, but must be easily inspectable as well to accommodate the situation i described above....UnitofWork2 is not easily inspectable... This is the one area where Datasets/DataTables really shine in my opinion..

I disagree that a UnitOfWork isn't easily inspectable, because you can foreach over the elements inside the unitofwork and thus remove/alter them if you want to.

I also don't see why it's even comparable with a datatable, as that's a completely different type of class (both technically and also philosophically)

currently i hold seperate collections in the UI, then pass them down to the BL like my previous post suggested. But when saving a large graph at once, this can get kinda out of hand....

EDIT: I would like to add, that when im speaking of entitycollection "tracking" deletions, i dont mean, Remove() = delete in the database, but Remove(entity, bool moveToDeleted) would add the entity to the collection of deleted entities...AND adapter would not have AUTOMATICALLY delete these, they still could/would be deleted manually...

That's also what I meant simple_smile So that tracking them in a uow without setting it all up would be what you really wanted, as you then just have to commit the uow and be done with it.

Frans Bouma | Lead developer LLBLGen Pro
omar avatar
omar
User
Posts: 569
Joined: 15-Oct-2004
# Posted on: 31-Aug-2006 15:22:59   

Having started this thread, I have to say that I now see things FRANS's way. My problem is that I always looked at EntityCollection as a counterpart for DataTable. This proved to be wrong because EntityCollection is just a bindable list of business objects. Its the objects that have the samrts and not the list. I am acomedating my BL functions to accept a UOW object that I can inspect to know what to do with the objects its housing. In addtion, UOW makes sure that it serializes its contents when passed from the UI to the BL in a remoting senarion. At the end of the day, I get my single EntityCollection clean and slim (with no extra baggage carrying the deleted objects) and I have my single UOW object housing (among other things) the objects that were deleted from the collection.

Keeping an open mind and trying to see things in a non-MS way, I find this setup tidy and clean

Answer
User
Posts: 363
Joined: 28-Jun-2004
# Posted on: 31-Aug-2006 21:45:34   

Omar, would you mind elaborating a bit on how your using the UoW with your BL? If you can post an example or two that would be even better simple_smile

EDIT:

Frans,

I have played around with the UnitOfWork some more, and i have a better understanding of how to use it ( I would still like to see how your doing it Omar simple_smile ),

But why is there no methods to inspect the actions of

AddDeleteEntitiesDirectlyCall and AddUpdateEntitiesDirectlyCall

Is it possible that you could make the methods virtual, so that i could override them to add events etc??? simple_smile

And one last question,

I notice it does Insert,Update then delete when you commit. Wouldnt it make more sense to do the save or delete in the order that you added it? What if, you have a customer that has multiple addresses. Each address has a Name, the name must be unique. In the UI you AddForDelete() one of the addresses. Then you create a new address entity with the same name as the one you deleted and call AddForSave(). Its not going to work correct???

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 01-Sep-2006 10:50:57   

Answer wrote:

I have played around with the UnitOfWork some more, and i have a better understanding of how to use it ( I would still like to see how your doing it Omar simple_smile ),

But why is there no methods to inspect the actions of

AddDeleteEntitiesDirectlyCall and AddUpdateEntitiesDirectlyCall

Is it possible that you could make the methods virtual, so that i could override them to add events etc??? simple_smile

You can add event handler calls, just add a delegate by calling AddCallback(). simple_smile

And one last question,

I notice it does Insert,Update then delete when you commit. Wouldnt it make more sense to do the save or delete in the order that you added it? What if, you have a customer that has multiple addresses. Each address has a Name, the name must be unique. In the UI you AddForDelete() one of the addresses. Then you create a new address entity with the same name as the one you deleted and call AddForSave(). Its not going to work correct???

It's a tradeoff. To save the user the trouble to track the order, it has to follow the insert/update / delete principle. If you want the uow to process the entities based on the order in which they were added, that also would mean saves. And that can become complicated if the entities are related to eachother.

Frans Bouma | Lead developer LLBLGen Pro
Answer
User
Posts: 363
Joined: 28-Jun-2004
# Posted on: 03-Sep-2006 10:34:50   

So i understand that i need a unitofwork to accomplish what i wish to do, which is edit multiple entities and collections and commit in one transaction, but how do i prevent/work around the situation i described above?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 03-Sep-2006 10:53:05   

The only way is two unitofworks. It's on the todo list to make this more flexible, however in practise it might not be possible. The problem is that if you allow the order of action to be the same as the order in which items are added, the developer thus has to face the consequences of save the entities in the right order as well.

Frans Bouma | Lead developer LLBLGen Pro
Answer
User
Posts: 363
Joined: 28-Jun-2004
# Posted on: 04-Sep-2006 09:04:50   

well, how about just make an overload for commit, that would allow one to specify if deletes take place first, or if they take place last...that would solve most scenarios i would think.

It would be nice thought to be able to inherit from unitofwork and modify it a bit, thats why i wanted the methods etc to be virtual...

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 04-Sep-2006 14:08:18   

Answer wrote:

well, how about just make an overload for commit, that would allow one to specify if deletes take place first, or if they take place last...that would solve most scenarios i would think.

It would be nice thought to be able to inherit from unitofwork and modify it a bit, thats why i wanted the methods etc to be virtual...

I think the problem is more generic. It's also present with inserts and updates. For example a unique constraint field, and you insert a new row with the same value as an existing row but you are updating that row as well to a different value. In theory, this should work, though due to the nature of the system it won't.

It's complicated, because you can't solve all situations by simply reshuffling insert/update/delete. What if you have 2 updates to the same entity type? One updates row X's unique value from A to B, and another one updates row Y's unque value from C to A. If Y's update is scheduled first, it won't work. This won't be detected by the graph sorter as that one sorts entities based on dependencies, not about single value actions.

The consequence of solving this is thus: either manually sorting of work or automatic, but manually means really manually here: either execute the work as it comes in, and don't sort anything whatsoever, as doing that will destroy the manual sorted order (likely), or leave it to the automatic sorter, but it might cause the conflicts discussed so you need two or even more sets of work (which comes down to semi manual anyway).

I'll think more about this, and if it's even solvable. Adding some sort of work-tracker could perhaps solve it, the question then is of course: simply accepting the actions as they come in (like deletes), will that always lead to a proper order? Probably not as a gui often allows activity to take place in any order, as the FK's and UC's are only really checked in the db.

Frans Bouma | Lead developer LLBLGen Pro
ScottCate
User
Posts: 48
Joined: 11-May-2005
# Posted on: 03-Dec-2006 01:31:18   

Frans, do you have any updated to this?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39615
Joined: 17-Aug-2003
# Posted on: 04-Dec-2006 10:23:33   

ScottCate wrote:

Frans, do you have any updated to this?

I've added it to the todolist so it's on the list of features to consider for v2.1, and I'll create the list of features to build into v2.1 from that big list this week.

People want more automatic management for work, so I'll definitely look into that. If a simple 'don't do the sorting for me, (#*$@($#@!' kind of option for UoW will offer more flexibility, it's not that much work, though if it brings what people expect, I'm not so sure, as I've explained above: you've to do the sequencing manually as in: add the actions in the right order.

Frans Bouma | Lead developer LLBLGen Pro
1  /  2  /  3