Spaces:
Running
Running
File size: 5,316 Bytes
b9c7f0e 452ab3c b9c7f0e e4f56e2 b9c7f0e e0e601c 1b321bd b9c7f0e 8b1a825 b9c7f0e 8b1a825 b9c7f0e 452ab3c b9c7f0e e4f56e2 b9c7f0e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | using FlowAPI.Application.Interfaces;
using FlowAPI.Domain.Entities;
using FlowAPI.Infrastructure.Data;
using Microsoft.EntityFrameworkCore;
namespace FlowAPI.Infrastructure.Repositories
{
public class GraphRepository : GenericRepository<Graph>, IGraphRepository
{
public GraphRepository(AppDbContext context) : base(context) { }
public async Task<IEnumerable<Graph>> GetAllByUserIdAsync(Guid userId)
{
return await _dbSet
.Where(g => g.UserId == userId)
.Include(g => g.Nodes)
.Include(g => g.Edges)
.AsNoTracking()
.ToListAsync();
}
public async Task<Graph?> GetByIdWithDetailsAsync(Guid id)
{
return await _dbSet
.Include(g => g.Nodes)
.Include(g => g.Edges)
.FirstOrDefaultAsync(g => g.Id == id);
}
public async Task ClearDetailsAsync(Guid id)
{
var graph = await _dbSet
.Include(g => g.Nodes)
.Include(g => g.Edges)
.FirstOrDefaultAsync(g => g.Id == id);
if (graph != null)
{
if (graph.Edges.Any())
{
_context.Edges.RemoveRange(graph.Edges);
}
if (graph.Nodes.Any())
{
_context.TaskNodes.RemoveRange(graph.Nodes);
}
await _context.SaveChangesAsync();
}
}
public async Task<bool> UpsertFullGraphAsync(Guid graphId, IEnumerable<TaskNode> nodes, IEnumerable<Edge> edges)
{
var graph = await _dbSet
.Include(g => g.Nodes)
.Include(g => g.Edges)
.FirstOrDefaultAsync(g => g.Id == graphId);
if (graph == null) return false;
var existingNodes = graph.Nodes.ToList();
var incomingNodes = nodes.ToList();
var existingEdges = graph.Edges.ToList();
var incomingEdges = edges.ToList();
// 1. Identify nodes and edges to delete
var nodesToDelete = existingNodes.Where(e => !incomingNodes.Any(i => i.Id == e.Id)).ToList();
var deletedNodeIds = nodesToDelete.Select(n => n.Id).ToHashSet();
var edgesToDelete = existingEdges
.Where(e => !incomingEdges.Any(i => i.FromNodeId == e.FromNodeId && i.ToNodeId == e.ToNodeId && i.Condition == e.Condition)
|| deletedNodeIds.Contains(e.FromNodeId)
|| deletedNodeIds.Contains(e.ToNodeId))
.ToList();
// 2. Queue deletes (but don't save yet)
if (edgesToDelete.Any())
{
_context.Edges.RemoveRange(edgesToDelete);
}
if (nodesToDelete.Any())
{
_context.TaskNodes.RemoveRange(nodesToDelete);
}
// 3. Update or Create Nodes
foreach (var node in incomingNodes)
{
var existingNode = existingNodes.FirstOrDefault(e => e.Id == node.Id);
if (existingNode != null)
{
existingNode.Label = node.Label;
existingNode.PosX = node.PosX;
existingNode.PosY = node.PosY;
existingNode.Type = node.Type;
existingNode.Decision = node.Decision;
existingNode.State = node.State;
existingNode.Description = node.Description;
existingNode.Color = node.Color;
existingNode.IsPinned = node.IsPinned;
existingNode.Width = node.Width;
existingNode.Height = node.Height;
if (node.Data != null)
{
existingNode.Data = node.Data;
}
}
else
{
node.GraphId = graphId;
_context.TaskNodes.Add(node);
}
}
var incomingNodeIds = incomingNodes.Select(n => n.Id).ToHashSet();
// 4. Update or Create Edges
foreach (var edge in incomingEdges)
{
// Only create/update edge if both FromNode and ToNode exist in the incoming nodes list
if (!incomingNodeIds.Contains(edge.FromNodeId) || !incomingNodeIds.Contains(edge.ToNodeId))
{
continue;
}
var existingEdge = existingEdges.FirstOrDefault(e => e.FromNodeId == edge.FromNodeId && e.ToNodeId == edge.ToNodeId && e.Condition == edge.Condition);
if (existingEdge != null)
{
existingEdge.Condition = edge.Condition;
}
else
{
edge.Id = Guid.NewGuid();
edge.GraphId = graphId;
_context.Edges.Add(edge);
}
}
// 5. Single atomic save to minimize lock duration
await _context.SaveChangesAsync();
return true;
}
}
}
|