GraphDiff

repository·master·Indexed 43 days ago

https://github.com/zzzprojects/graphdiff

A DbContext extension library for Entity Framework (EF6+) that simplifies updating entire graphs of detached entities. It provides the UpdateGraph<T> method to automate the synchronization of complex object trees, allowing developers to define owned and associated entities and collections to manage how changes are persisted to the database.

Tokens
6.4K
Snippets
19
Records
30
Agent score
54%

What's inside GraphDiff

  1. How owned entities work in GraphDiff

    master

    In GraphDiff, an owned entity is treated as being "part of" its parent. When you update an entity graph, GraphDiff automatically synchronizes the state of these owned entities (adding, updating, or removing them) based on their presence in the parent's navigational properties.

    To enable this behavior, you must explicitly register the owned entity or collection using the OwnedEntity() or OwnedCollection() methods within the UpdateGraph mapping configuration.

  2. Merge detached entity graphs with UpdateGraph<T>

    master

    GraphDiff provides extension methods for DbContext that allow you to save an entire detached model/entity graph (including child entities and lists) to the database automatically.

    The primary method is DbContext.UpdateGraph<T>(). This method handles the complexity of merging changes, ensuring concurrency is maintained for all child entities in the graph, and allows for configuration mappings to restrict persistence to only the changes within the defined graph.

  3. Note: Changes to associated entities are not saved

    master

    When an entity is configured via AssociatedEntity or AssociatedCollection, any modifications to the properties of those associated objects will be ignored by context.SaveChanges() during the UpdateGraph process. GraphDiff only manages the link (the relation) between the parent and the child, not the child's data.

    // Even if we change the Title here...
    node.OneToOneAssociated.Title = "Updated Associated Entity";
    
    using (var context = new TestDbContext())
    {
        // ...GraphDiff will only update the relation, NOT the Title property.
        context.UpdateGraph(node, map => map
            .AssociatedEntity(p => p.OneToOneAssociated));
    
        context.SaveChanges();
    }
  4. How associated entities work in GraphDiff

    master

    By default, when updating an entity graph, GraphDiff treats associated entities as 'detached'. This means that while GraphDiff can manage the relationship (the navigation property) between a parent and its child, it will not track or save any changes made to the properties of the child entity itself.

    To use this behavior, you must explicitly specify the associated entity or collection in your mapping configuration using either .AssociatedEntity() or .AssociatedCollection().

    Key behaviors:

    • For single entities: The parent's navigation property is updated to point to the new child, but changes to the child's internal state are ignored.
    • For collections: The relationship (the collection itself) is updated, but changes to the items within the collection are not saved.
  5. Understand Owned vs Associated Entities in GraphDiff

    master

    GraphDiff distinguishes between two types of entity relations to control how updates propagate through a graph:

    Owned Entity

    An owned entity is considered part of the parent aggregate. When updating the graph, GraphDiff will automatically update, add, or remove these child entities based on changes in the parent's navigational property. Use .OwnedCollection() or .OwnedEntity() in your mapping to define this behavior.

    Associated Entity

    An associated entity is not part of the aggregate. While the parent's navigation property will be updated to point to the child, GraphDiff will not save any changes made to the child entity itself. This is useful for entities that exist independently of the parent.

  6. Add a single detached entity using UpdateGraph

    master

    To add a new, detached entity to the database, pass the entity instance to DbContext.UpdateGraph(). GraphDiff treats this entity as the root of the graph. The method returns the tracked version of the entity, which you should use for subsequent operations within that context.

    var node = new TestNode
    {
        Title = "Hello"
    };
    
    using (var context = new TestDbContext())
    {
        node = context.UpdateGraph(node);
        context.SaveChanges();
    }
  7. Add a single owned entity using OwnedEntity()

    master

    To add a new owned entity to a parent, assign the new entity to the parent's navigational property and call UpdateGraph using the OwnedEntity() method to map that property.

    var node = new TestNode { Title = "Parent Node" };
    
    using (var context = new TestDbContext())
    {
        context.Nodes.Add(node);
        context.SaveChanges();
    } 
    
    // Assign the new owned entity to the parent
    node.OneToOneOwned = new OneToOneOwnedModel { Title = "Owned Entity" };
    
    using (var context = new TestDbContext())
    {
        // Map the owned entity property
        context.UpdateGraph(node, map => map
            .OwnedEntity(p => p.OneToOneOwned));
    
        context.SaveChanges();
    }
    var node = new TestNode { Title = "Parent Node" };
    
    using (var context = new TestDbContext())
    {
        context.Nodes.Add(node);
        context.SaveChanges();
    } 
    
    // Simulate detach
    node.OneToOneOwned = new OneToOneOwnedModel { Title = "Owned Entity" };
    using (var context = new TestDbContext())
    {
        // Setup mapping
        context.UpdateGraph(node, map => map
            .OwnedEntity(p => p.OneToOneOwned));
    
        context.SaveChanges();
    }
  8. Remove an owned entity

    master

    To remove an owned entity from the parent and delete it from the database, set the parent's navigational property for that owned entity to null. You must use OwnedEntity() in the mapping for the removal to be processed.

    var node = new TestNode
    {
        Title = "Parent Node",
        OneToOneOwned = new OneToOneOwnedModel
        {
            Title = "Owned Entity"
        }
    };
    
    using (var context = new TestDbContext())
    {
        context.Nodes.Add(node);
        context.SaveChanges();
    } 
    
    // Remove the owned entity by setting it to null
    node.OneToOneOwned = null;
    
    using (var context = new TestDbContext())
    {
        context.UpdateGraph(node, map => map
            .OwnedEntity(p => p.OneToOneOwned));
    
        context.SaveChanges();
    }
    var node = new TestNode
    {
        Title = "Parent Node",
        OneToOneOwned = new OneToOneOwnedModel
        {
            Title = "Owned Entity"
        }
    };
    
    using (var context = new TestDbContext())
    {
        context.Nodes.Add(node);
        context.SaveChanges();
    } 
    
    // Simulate detach
    node.OneToOneOwned = null;
    using (var context = new TestDbContext())
    {
        // Setup mapping
        context.UpdateGraph(node, map => map
            .OwnedEntity(p => p.OneToOneOwned));
    
        context.SaveChanges();
    }
  9. Update a detached graph using UpdateGraph()

    master

    GraphDiff provides extension methods for DbContext that allow you to save an entire detached model/entity graph (including child entities and lists) to the database automatically. Instead of manually writing code to track changes, add, or remove child entities, you can use UpdateGraph to merge the detached state into the database.

    To use it, call context.UpdateGraph(rootEntity, mappingAction) where the mapping action defines how relationships are treated.

    using (var context = new TestDbContext())  
    {
        // Update the company and state that the company 'owns' the collection Contacts.
        context.UpdateGraph(company, map => map
            .OwnedCollection(p => p.Contacts)
        );
    
        context.SaveChanges();
    }
  10. Update a single detached entity using UpdateGraph

    master

    To update an existing entity that was modified in a different DbContext instance, use DbContext.UpdateGraph(). This method merges the changes from the detached entity graph into the current database context by traversing the graph and applying updates.

    var node = new TestNode
    {
        Title = "Hello"
    };
    
    using (var context = new TestDbContext())
    {
        context.Nodes.Add(node);
        context.SaveChanges();
    }
    
    node.Title = "Updated Title";
    
    using (var context = new TestDbContext())
    {
        context.UpdateGraph(node);
        context.SaveChanges();
    }