diff --git a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetProjectById/GetProjectByIdQueryHandler.cs b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetProjectById/GetProjectByIdQueryHandler.cs index 4c2ec17..9cdba37 100644 --- a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetProjectById/GetProjectByIdQueryHandler.cs +++ b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetProjectById/GetProjectByIdQueryHandler.cs @@ -17,7 +17,8 @@ public sealed class GetProjectByIdQueryHandler(IProjectRepository projectReposit public async Task Handle(GetProjectByIdQuery request, CancellationToken cancellationToken) { - var project = await _projectRepository.GetByIdAsync( + // Use read-only method for query (AsNoTracking for better performance) + var project = await _projectRepository.GetProjectByIdReadOnlyAsync( ProjectId.From(request.ProjectId), cancellationToken); diff --git a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetProjects/GetProjectsQueryHandler.cs b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetProjects/GetProjectsQueryHandler.cs index 2eee592..261e68e 100644 --- a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetProjects/GetProjectsQueryHandler.cs +++ b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetProjects/GetProjectsQueryHandler.cs @@ -15,7 +15,8 @@ public sealed class GetProjectsQueryHandler(IProjectRepository projectRepository public async Task> Handle(GetProjectsQuery request, CancellationToken cancellationToken) { - var projects = await _projectRepository.GetAllAsync(cancellationToken); + // Use read-only method for query (AsNoTracking for better performance) + var projects = await _projectRepository.GetAllProjectsReadOnlyAsync(cancellationToken); return projects.Select(MapToDto).ToList(); } diff --git a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetStoriesByProjectId/GetStoriesByProjectIdQueryHandler.cs b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetStoriesByProjectId/GetStoriesByProjectIdQueryHandler.cs index 722ffcb..790997a 100644 --- a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetStoriesByProjectId/GetStoriesByProjectIdQueryHandler.cs +++ b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetStoriesByProjectId/GetStoriesByProjectIdQueryHandler.cs @@ -16,9 +16,9 @@ public sealed class GetStoriesByProjectIdQueryHandler(IProjectRepository project public async Task> Handle(GetStoriesByProjectIdQuery request, CancellationToken cancellationToken) { - // Get the project + // Use read-only method for query (AsNoTracking for better performance) var projectId = ProjectId.From(request.ProjectId); - var project = await _projectRepository.GetByIdAsync(projectId, cancellationToken); + var project = await _projectRepository.GetProjectWithFullHierarchyReadOnlyAsync(projectId, cancellationToken); if (project == null) throw new NotFoundException("Project", request.ProjectId); diff --git a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetTasksByAssignee/GetTasksByAssigneeQueryHandler.cs b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetTasksByAssignee/GetTasksByAssigneeQueryHandler.cs index b8158f3..c2bca41 100644 --- a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetTasksByAssignee/GetTasksByAssigneeQueryHandler.cs +++ b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetTasksByAssignee/GetTasksByAssigneeQueryHandler.cs @@ -14,8 +14,8 @@ public sealed class GetTasksByAssigneeQueryHandler(IProjectRepository projectRep public async Task> Handle(GetTasksByAssigneeQuery request, CancellationToken cancellationToken) { - // Get all projects - var allProjects = await _projectRepository.GetAllAsync(cancellationToken); + // Use read-only method for query (AsNoTracking for better performance) + var allProjects = await _projectRepository.GetAllProjectsReadOnlyAsync(cancellationToken); // Get all tasks assigned to the user across all projects var userTasks = allProjects diff --git a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetTasksByProjectId/GetTasksByProjectIdQueryHandler.cs b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetTasksByProjectId/GetTasksByProjectIdQueryHandler.cs index e92fa54..836f27f 100644 --- a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetTasksByProjectId/GetTasksByProjectIdQueryHandler.cs +++ b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Queries/GetTasksByProjectId/GetTasksByProjectIdQueryHandler.cs @@ -16,9 +16,9 @@ public sealed class GetTasksByProjectIdQueryHandler(IProjectRepository projectRe public async Task> Handle(GetTasksByProjectIdQuery request, CancellationToken cancellationToken) { - // Get the project with all its tasks + // Use read-only method for query (AsNoTracking for better performance) var projectId = ProjectId.From(request.ProjectId); - var project = await _projectRepository.GetByIdAsync(projectId, cancellationToken); + var project = await _projectRepository.GetProjectWithFullHierarchyReadOnlyAsync(projectId, cancellationToken); if (project == null) throw new NotFoundException("Project", request.ProjectId); diff --git a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Domain/Repositories/IProjectRepository.cs b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Domain/Repositories/IProjectRepository.cs index d7d7da8..a768c84 100644 --- a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Domain/Repositories/IProjectRepository.cs +++ b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Domain/Repositories/IProjectRepository.cs @@ -93,4 +93,19 @@ public interface IProjectRepository /// Gets all tasks for a story (read-only, AsNoTracking) /// Task> GetTasksByStoryIdAsync(StoryId storyId, CancellationToken cancellationToken = default); + + /// + /// Gets project by ID with all epics/stories/tasks (read-only, AsNoTracking) + /// + Task GetProjectByIdReadOnlyAsync(ProjectId projectId, CancellationToken cancellationToken = default); + + /// + /// Gets all projects (read-only, AsNoTracking) + /// + Task> GetAllProjectsReadOnlyAsync(CancellationToken cancellationToken = default); + + /// + /// Gets project with all epics/stories/tasks hierarchy (read-only, AsNoTracking) + /// + Task GetProjectWithFullHierarchyReadOnlyAsync(ProjectId projectId, CancellationToken cancellationToken = default); } diff --git a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Infrastructure/Repositories/ProjectRepository.cs b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Infrastructure/Repositories/ProjectRepository.cs index dfdaea8..d162976 100644 --- a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Infrastructure/Repositories/ProjectRepository.cs +++ b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Infrastructure/Repositories/ProjectRepository.cs @@ -145,4 +145,30 @@ public class ProjectRepository(PMDbContext context) : IProjectRepository .OrderBy(t => t.CreatedAt) .ToListAsync(cancellationToken); } + + public async Task GetProjectByIdReadOnlyAsync(ProjectId projectId, CancellationToken cancellationToken = default) + { + return await _context.Projects + .AsNoTracking() + .Include(p => p.Epics) + .FirstOrDefaultAsync(p => p.Id == projectId, cancellationToken); + } + + public async Task> GetAllProjectsReadOnlyAsync(CancellationToken cancellationToken = default) + { + return await _context.Projects + .AsNoTracking() + .OrderByDescending(p => p.CreatedAt) + .ToListAsync(cancellationToken); + } + + public async Task GetProjectWithFullHierarchyReadOnlyAsync(ProjectId projectId, CancellationToken cancellationToken = default) + { + return await _context.Projects + .AsNoTracking() + .Include(p => p.Epics) + .ThenInclude(e => e.Stories) + .ThenInclude(s => s.Tasks) + .FirstOrDefaultAsync(p => p.Id == projectId, cancellationToken); + } }