How to remove related entity from entity in datascope

Posts   
 
    
Puser
User
Posts: 228
Joined: 20-Sep-2012
# Posted on: 11-Dec-2017 17:19:09   

LLBLGen Pro v5.31 RTM, adapter

Hello,

I'm trying to remove a related entity from an entity in a datascope but I think I need some instructions on the right settings in the designer or used code.

I have an entity RoyaltyContract with RoyaltyContract Id. Also an entity Notes which holds notes for different entities. Note is an super type in a an inheritance hierarchy and has a specific discriminator value. One subtype is RoyaltyContractMemo. In RoyaltyContract I have a 1 to 0:1 relationship with RoyaltyContractMemo with a key1 field that is used to reference the RoyaltyContractId (RoyaltyContract is Primary key side).

I want to try to delete the ROyaltyContractMemo (when notes are empty). I load the RoyaltyContract with the ROyaltyContractMemo and put ROyaltyContract in the DataScope. Setting property RoyaltyContractMemo to null doesnt do anything on Commit. Also RoyaltyContractMemo.DetachFromGraph() doesnt do anything. No error either. Maybe I have to setup the Update rule (currently tried NoAction and Cascase) and Delete rule (cascade)?? What do I have to do in my mapping to delete the RoyaltyContractMemo entity from the database?

Walaa avatar
Walaa
Support Team
Posts: 14946
Joined: 21-Aug-2005
# Posted on: 11-Dec-2017 19:05:45   

I'm trying to remove a related entity from an entity in a datascope

I want to try to delete the ROyaltyContractMemo (when notes are empty).

Are you trying to just remove an entity from the in-memory graph? Or Are you trying to delete the entity from the database?

Could you please post a code snippet (in the simplest form)? And explain what are the results and what did you expect...

Thanks,

Puser
User
Posts: 228
Joined: 20-Sep-2012
# Posted on: 11-Dec-2017 21:13:07   

Yes, remove the related entity from the database. I was under the impression that in the datascope I use, you then had to remove the related entity from the entity. But I guess my formulation was not quite clear?

for completeness and not over simplyfied:

    public async Task<object> Post(UpdateRoyaltyContractCommand command)
    {
        var aggregate = (command.RoyaltyContract.RoyaltyContractId == 0) ?
            _repository.CreateRoyaltyContractAggregate() :
            await _repository.GetRoyaltyContractAggregateAsync(command.RoyaltyContract.RoyaltyContractId);

        _unitOfWork.Add(aggregate);
        Update(command.RoyaltyContract, aggregate);

        await _unitOfWork.CommitAsync();
    }

    public void Update(RoyaltyContractRoot dto, RoyaltyContractEntity entity)
    {
        if (string.IsNullOrEmpty(dto.RoyaltyContractMemo) && entity.RoyaltyContractMemo != null)
            entity.RoyaltyContractMemo = null; // <-- remove here
        //_mapper.Map(dto, entity);
    }

_unitOfWork is an extended DataScope.

I expect that RoyaltyContractMemo is removed from the database (the underlying record). That's what I want, maybe it's the wrong way. Point me in the right direction please.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 12-Dec-2017 10:47:01   

You have to call MarkForDeletion(entity) which is a protected method in the DataScope to mark an entity for deletion, which will make it end up in the UoW.

There's no other way it can know that you want to delete something, as the action is ambiguous: you might want to switch an order from customer1 to customer2 as a mistake was made, so you removed the reference to customer1 from the order, but that doesn't mean you want to delete customer1 simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Puser
User
Posts: 228
Joined: 20-Sep-2012
# Posted on: 13-Dec-2017 09:16:32   

There's no other way it can know that you want to delete something, as the action is ambiguous: you might want to switch an order from customer1 to customer2 as a mistake was made, so you removed the reference to customer1 from the order, but that doesn't mean you want to delete customer1 Regular Smiley

That would be true for foreign keys (and its objects as customer). But how about primary keys? RoyaltContractMemo cannot exist without the RoyaltyContract. So I cannot imagine another scenario than deleting by setting it to Null.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 13-Dec-2017 09:57:42   

Puser wrote:

There's no other way it can know that you want to delete something, as the action is ambiguous: you might want to switch an order from customer1 to customer2 as a mistake was made, so you removed the reference to customer1 from the order, but that doesn't mean you want to delete customer1 Regular Smiley

That would be true for foreign keys (and its objects as customer). But how about primary keys? RoyaltContractMemo cannot exist without the RoyaltyContract. So I cannot imagine another scenario than deleting by setting it to Null.

Setting it to null doesn't 'delete' it. It simply resets the reference to it, like in my move order to another customer scenario. To delete the entity you also have to call MarkForDeletion by passing that entity simple_smile So you have to do both actions.

Frans Bouma | Lead developer LLBLGen Pro
Puser
User
Posts: 228
Joined: 20-Sep-2012
# Posted on: 13-Dec-2017 10:11:11   

Hello Frans, I understand that. Setting to Null was only to try to delete it, but I was looking for something in the lines of Order.OrderItems.Remove(orderItem), where OrderItem cannot exist without Order. Although this is for collections, it's automatically picked up in the DataScope without having to call an explicit MarkForDeletion. My personal opinion is that a 1-1 relation like RoyaltyContract with RoyaltyContractMemo where the latter cannot exist without the former is closer to Order=OrderItems that Order=Customer where both can exist without each other. From that opinion I expected some method on RoyaltyContract that would be picked up by the DataScope automatically (be it setting to Null, Remove or something else). That would make sense to me. I don't want to argue my opinion or your design choices, but it would be nice if some method would exist on RoyaltyContract that the DAtaScope is aware of so you can do your work on an aggregate without having to resort on extra methods on the DataScope, I think it's wonderful it exists! smile

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 13-Dec-2017 17:49:47   

Puser wrote:

Hello Frans, I understand that. Setting to Null was only to try to delete it, but I was looking for something in the lines of Order.OrderItems.Remove(orderItem), where OrderItem cannot exist without Order. Although this is for collections, it's automatically picked up in the DataScope without having to call an explicit MarkForDeletion. My personal opinion is that a 1-1 relation like RoyaltyContract with RoyaltyContractMemo where the latter cannot exist without the former is closer to Order=OrderItems that Order=Customer where both can exist without each other. From that opinion I expected some method on RoyaltyContract that would be picked up by the DataScope automatically (be it setting to Null, Remove or something else). That would make sense to me.

Ah ok simple_smile The main reason the OrderItems.Remove(orderItem) is automatically tracked is that it's otherwise very hard to do so for you: say you bind the OrderItems collection to a grid and in the grid you remove a row. To be able to track that removal, you'd need to inject a tracker collection, which is what the scope does for you.

The reference setting to null isn't trackable like that, so we require you to perform the explicit action to call the method to delete it. The main issue that it's not tracked automatically is that by setting the reference to null doesn't mark the entity as 'deleted' so nothing can pick that up. This is a deliberate choice, as it's an ambiguous call so we erred on the safe side here. simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Puser
User
Posts: 228
Joined: 20-Sep-2012
# Posted on: 13-Dec-2017 20:52:45   

I understand. Setting to null was only an example, a try, if another more easy way can be achieved then nice. I have multiple scenarios where the notes field is linked to a (sub)collection like Order-OrderItem-SomeMemo. In these cases deletion/removal of an OrderItem is tracked, but on existing OrderItems with the memo field where someone wants to clear some, it cannot be done without also calling into the DataScope. Maybe with some method like OrderItem.SomeMemo.Remove() or Clear() you could take care of that in the datascope also?

PS: For your information, I use automapper to map from dto to entity, and have created this with a custom llblgen template. But this is the only thing that would force me to call into the datascope , the rest is as simple as calling:

_mapper.Map(dto, entity);
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39588
Joined: 17-Aug-2003
# Posted on: 15-Dec-2017 10:20:26   

Puser wrote:

I understand. Setting to null was only an example, a try, if another more easy way can be achieved then nice. I have multiple scenarios where the notes field is linked to a (sub)collection like Order-OrderItem-SomeMemo. In these cases deletion/removal of an OrderItem is tracked, but on existing OrderItems with the memo field where someone wants to clear some, it cannot be done without also calling into the DataScope. Maybe with some method like OrderItem.SomeMemo.Remove() or Clear() you could take care of that in the datascope also?

Ok, but what does 'Remove' mean in this case? Mark for delete? Remove from the graph? Delete immediately? That's a bit unclear. simple_smile Adapter is designed around the idea that you give it commands to do things, either through a Unit of work or through the adapter. So there's no DB activity code in the entities. Also, there's no reference to the scope in the entity, so if there's a method in the entity, how does it get to the scope?

PS: For your information, I use automapper to map from dto to entity, and have created this with a custom llblgen template. But this is the only thing that would force me to call into the datascope , the rest is as simple as calling:

_mapper.Map(dto, entity);

At the moment that's the only way. The main problem for you is that the context used by the scope is internal. So even though you can for instance add a 'Remove()' method to a partial class of CommonEntityBase(), and obtain the Context (which is of type DataScopeContext in this case) of the entity there, you can't call the AddForDelete() of the DataScopeContext as it's internal. So adding this method yourself would solve your problem but it isn't going to work at the moment due to the nature of the context class...

I don't want to make it public as it's not a type that's to be used as a public type (as it then might be used as a Context which shouldn't be done).

I'll ponder a bit about this, adding a protected method perhaps to EntityBase/EntityBase2 which adds it to an active context for deletion if the context is a scope context. That won't help you today, but perhaps in the future.

Frans Bouma | Lead developer LLBLGen Pro