HenkKin / EntityCloner.Microsoft.EntityFrameworkCore

Cloning entities using Microsoft.EntityFrameworkCore
21 stars 3 forks source link

proof of concept - graph visitor delegate enhancement #9

Closed vigouredelaruse closed 1 year ago

vigouredelaruse commented 1 year ago

hello again from utc - 4

i've been experimenting with a delegate api surface that visits each entity in the cloned graph and permits modification of the visited entities

the api surface is exercised by

notes

i appreciate your consideratoin on the matter thanks

HenkKin commented 1 year ago

I've looked at your changes, but it's not clear to me what you really want to achieve.

Separating the filter and the action limits the visitor's options. Suppose you want to update something in several places in the tree, then it will be difficult to solve that with 1 filter and 1 action. Then I see more value in 1 action in which the type info of the entity is also given, so that you can create an action in the following way:

 var cloneResult = await contentCollectionOperator
                .Clone( i => 
                i.Include(o => o.AccessControlEntries)
                .Include(i => i.HorselessContents)
                .Include( i => i.HorselessViews)
                .ThenInclude(t => t.Placeholders),
                 (TypeInfo typeInfo, object entityClone) =>
                 {
                       // just an example to filter for multiple types
                        switch (typeInfo.Name)
                        {
                              case nameof(AccessControlEntry):
                                   ((AccessControlEntry)entityClone).Name = "Updated AccessControlEntry name";
                                   break;
                             case nameof(Placeholder):
                                  ((Placeholder)entityClone).Title = "Updated Placeholder title";
                                  break;
                        }

                        // example to react to global interface type
                        if (typeof(IContentRowLevelSecured).IsAssignableFrom(type))
                        {
                                 ((IContentRowLevelSecured)entityClone).UpdatedAt = DateTime.UtcNow;
                                 ((IContentRowLevelSecured)entityClone).CreatedAt = DateTime.UtcNow;
                                 ((IContentRowLevelSecured)entityClone).ObjectId = $"{entityClone.GetType().Name}-{Guid.NewGuid().ToString()}";
                        }

                        return entityClone;
                 },
                primaryKey: testContentCollection.Id);

But if you have the same type nested in the graph then it is not possible to make clear which relationship you want to adjust.

But I think that this functionality can also be performed outside the clone functionality. The question is whether this belongs in this library. An external visitor can also arrange this for you. Then it separates the concerns of cloning and adjusting. I am not convinced this should be part of this library.

vigouredelaruse commented 1 year ago

howdee; thanks for the feedback

Separating the filter and the action limits the visitor's options. agreed - separating the filter and the action adds complexity to the api and can cause the action to duplicate the work of the filter in the worst case of a highly heterogeneous graph.

the situation gets better if the entities are tagged with metadata to differentiate duplicated entity type instances, which is the kind of graph i'm working with at the moment.

as well the implementaters of the filter and the implementers of the action might be on different sides of the security fence. in this scenario the action might be exposed to the final consumer, but the filter may not, which i like a lot

no matter which mechanism i use i don't anticipate being able to express the filter rules and the actions without using code that's tightly coupled to the data structure represented

i think the separate filter shows most benefit if it can be described by an interface, or if the user wants to externalize specification of the rule filters, which is something i'd like to do with the target application

to be as clear as one can be with text diagrams, the workflow i want to enable is the below retrieve template graph -> clone graph -> apply mutations of arbitrary complexity to the graph -> human step -> persist updated graph

if you're still not convinced yet feel free to retire the issue and i will continue to think it through in its various aspects thanks

HenkKin commented 1 year ago

I think this feature should not be part of this library. The responsibility of this library is just clone a graph of entities by EFC configurations/relationships. Adjusting entities can be done after cloning and before saving.