Spaces:
Running
Running
| 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; | |
| } | |
| } | |
| } | |