Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 8, 2026

LiftService is a singleton (to avoid reinitializing Sldr and prevent LanguageTag data leaks) but was injecting transient ISpeakerRepository and ISemanticDomainRepository via constructor. This violates DI best practices—the singleton would hold these transient instances for its lifetime.

Changes

  • LiftService constructor: Removed ISemanticDomainRepository and ISpeakerRepository parameters
  • Method signatures: Updated LiftExport() and CreateLiftRanges() to accept repositories as parameters
  • LiftController: Injects repositories and passes them to LiftService methods
  • ILiftService interface: Updated to match new signatures

This follows the existing pattern where IWordRepository and IProjectRepository are passed as method parameters, allowing per-request resolution of transient services.

Before:

public LiftService(ISemanticDomainRepository semDomRepo, ISpeakerRepository speakerRepo)
public async Task<string> LiftExport(string projectId, IWordRepository wordRepo, IProjectRepository projRepo)

After:

public LiftService()
public async Task<string> LiftExport(string projectId, IWordRepository wordRepo, IProjectRepository projRepo,
    ISpeakerRepository speakerRepo, ISemanticDomainRepository semDomRepo)
Original prompt

This section details on the original issue you should resolve

<issue_title>Fix singleton LiftService's dependency on Transient repos</issue_title>
<issue_description>LiftService is intentionally a Singleton in Backend/Startup.cs:

services.AddSingleton<ILiftService, LiftService>();

However, it depends on two Repositories that are (and should be) transient. In Backend/Services/LiftService.cs:
public LiftService(ISemanticDomainRepository semDomRepo, ISpeakerRepository speakerRepo)

Refactor the LiftController and LiftService so that the Controller has those Repositories injected instead of the Service, and they are passed in as parameters to any Service methods that require them.</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.


This change is Reviewable

Summary by CodeRabbit

  • Refactor

    • Restructured lift export service to handle additional semantic domain and speaker data during export operations.
    • Updated the creation of lift ranges to accept expanded semantic domain information for improved data processing.
  • Tests

    • Updated test configurations to align with refactored service dependencies.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

📝 Walkthrough

Walkthrough

LiftService transitions from constructor dependency injection to parameter-based injection by removing ISemanticDomainRepository and ISpeakerRepository from its constructor, making it a singleton with no transient dependencies. LiftController assumes these dependencies and passes them as method parameters.

Changes

Cohort / File(s) Summary
Interface Update
Backend/Interfaces/ILiftService.cs
Updated LiftExport to accept ISemanticDomainRepository and ISpeakerRepository as parameters; updated CreateLiftRanges to include allDoms (List) parameter for broader domain access.
Service Implementation
Backend/Services/LiftService.cs
Removed constructor parameters (ISemanticDomainRepository, ISpeakerRepository) to parameterless constructor. Method signatures now accept repositories as parameters. Logic refactored to use parameter-supplied repositories instead of injected fields.
Controller
Backend/Controllers/LiftController.cs
Converted to primary constructor syntax with expanded dependencies (added IProjectRepository, ISemanticDomainRepository, ISpeakerRepository). Updated CreateLiftExport to pass all repositories to LiftExport method.
Test Updates
Backend.Tests/Controllers/LiftControllerTests.cs, Backend.Tests/Services/LiftServiceTests.cs
Adjusted test setup: LiftService instantiated with parameterless constructor; LiftController constructor call reordered with new dependencies; mock dependencies configured accordingly.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A singleton that stood so tall,
Yet carried transients through the hall—
Now Controller takes the load to bear,
Passing repos with utmost care,
The dependencies dance with grace,
Each in its proper, rightful place!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: removing transient repository dependencies from a singleton LiftService to fix DI violations.
Linked Issues check ✅ Passed The PR successfully implements all coding requirements from issue #4090: removes transient repositories from LiftService constructor, adds them as parameters to LiftExport and CreateLiftRanges, injects them in LiftController, updates ILiftService interface, and adjusts tests accordingly.
Out of Scope Changes check ✅ Passed All changes are directly related to the stated objective of fixing singleton LiftService's transient repository dependencies; no out-of-scope modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

- Remove ISemanticDomainRepository and ISpeakerRepository from LiftService constructor
- Pass these repositories as method parameters to LiftExport and CreateLiftRanges
- Update ILiftService interface method signatures
- Update LiftController to inject and pass repositories to LiftService methods
- Update all test files to use parameterless LiftService constructor
- This follows the existing pattern used for IWordRepository and IProjectRepository

Co-authored-by: imnasnainaec <[email protected]>
Copilot AI changed the title [WIP] Fix LiftService's dependency on transient repositories Fix singleton LiftService dependency on transient repositories Jan 8, 2026
Copilot AI requested a review from imnasnainaec January 8, 2026 20:43
@github-actions
Copy link

github-actions bot commented Jan 8, 2026

⚠️ Commit Message Format Issues ⚠️
commit 17cd458cf6:
3: B1 Line exceeds max length (86>80): "- Remove ISemanticDomainRepository and ISpeakerRepository from LiftService constructor"
4: B1 Line exceeds max length (81>80): "- Pass these repositories as method parameters to LiftExport and CreateLiftRanges"
8: B1 Line exceeds max length (83>80): "- This follows the existing pattern used for IWordRepository and IProjectRepository"

@imnasnainaec imnasnainaec marked this pull request as ready for review January 8, 2026 21:18
@imnasnainaec imnasnainaec requested a review from Copilot January 8, 2026 21:18
@imnasnainaec
Copy link
Collaborator

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 8, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@codecov
Copy link

codecov bot commented Jan 8, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.58%. Comparing base (d2f2aa3) to head (e308be0).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff             @@
##           master    #4091       +/-   ##
===========================================
+ Coverage   74.57%   85.58%   +11.00%     
===========================================
  Files         295       54      -241     
  Lines       10938     4772     -6166     
  Branches     1372      588      -784     
===========================================
- Hits         8157     4084     -4073     
+ Misses       2385      544     -1841     
+ Partials      396      144      -252     
Flag Coverage Δ
backend 85.58% <100.00%> (-0.02%) ⬇️
frontend ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request refactors LiftService to eliminate direct dependency injection of repository services, moving them to method parameters instead. This change aligns with the existing pattern for handling repositories and ensures proper dependency injection for the singleton LiftService.

Key Changes:

  • Removed ISemanticDomainRepository and ISpeakerRepository from LiftService constructor
  • Updated LiftExport() and CreateLiftRanges() method signatures to accept repositories as parameters
  • Modified LiftController to inject repositories via primary constructor and pass them to service methods
  • Updated interface and test files to match new signatures

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
Backend/Services/LiftService.cs Removed repository fields from class, updated constructor to be parameterless, modified LiftExport and CreateLiftRanges methods to accept repositories as parameters
Backend/Interfaces/ILiftService.cs Updated interface method signatures to match implementation changes
Backend/Controllers/LiftController.cs Converted to primary constructor syntax, added ISemanticDomainRepository and ISpeakerRepository injection, updated service method calls to pass repositories
Backend.Tests/Services/LiftServiceTests.cs Updated LiftService instantiation to use parameterless constructor
Backend.Tests/Controllers/LiftControllerTests.cs Updated LiftService instantiation and LiftController constructor call to match new signatures

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
Backend.Tests/Services/LiftServiceTests.cs (1)

11-12: Consider removing unused mock fields.

After the refactor, _semDomRepo and _speakerRepo are initialized in Setup() but never used in any test. These fields are now dead code since the tests in this file only exercise export/import storage functionality (which doesn't require these repositories).

♻️ Suggested cleanup
-        private ISemanticDomainRepository _semDomRepo = null!;
-        private ISpeakerRepository _speakerRepo = null!;
         private ILiftService _liftService = null!;

And in Setup():

         [SetUp]
         public void Setup()
         {
-            _semDomRepo = new SemanticDomainRepositoryMock();
-            _speakerRepo = new SpeakerRepositoryMock();
             _liftService = new LiftService();
         }

Also applies to: 28-30

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2f2aa3 and e308be0.

📒 Files selected for processing (5)
  • Backend.Tests/Controllers/LiftControllerTests.cs
  • Backend.Tests/Services/LiftServiceTests.cs
  • Backend/Controllers/LiftController.cs
  • Backend/Interfaces/ILiftService.cs
  • Backend/Services/LiftService.cs
🧰 Additional context used
🧬 Code graph analysis (5)
Backend.Tests/Services/LiftServiceTests.cs (1)
Backend/Services/LiftService.cs (1)
  • LiftService (120-129)
Backend/Interfaces/ILiftService.cs (3)
Backend/Helper/Language.cs (3)
  • List (15-20)
  • List (23-39)
  • List (44-55)
Backend/Models/SemanticDomain.cs (6)
  • SemanticDomainTreeNode (133-180)
  • SemanticDomainTreeNode (171-179)
  • SemanticDomainFull (82-128)
  • SemanticDomainFull (96-101)
  • SemanticDomainFull (103-116)
  • SemanticDomainFull (119-127)
src/api/models/semantic-domain-full.ts (1)
  • SemanticDomainFull (20-81)
Backend/Controllers/LiftController.cs (2)
Backend/Helper/CombineHub.cs (1)
  • ExportHub (12-15)
src/components/ProjectExport/ExportHub.tsx (1)
  • ExportHub (13-26)
Backend.Tests/Controllers/LiftControllerTests.cs (4)
Backend/Services/LiftService.cs (1)
  • LiftService (120-129)
Backend.Tests/Mocks/SemanticDomainRepositoryMock.cs (1)
  • SemanticDomainRepositoryMock (8-36)
Backend.Tests/Mocks/HubContextMock.cs (1)
  • HubContextMock (12-17)
src/components/ProjectExport/ExportHub.tsx (1)
  • ExportHub (13-26)
Backend/Services/LiftService.cs (2)
Backend/Interfaces/ILiftService.cs (6)
  • Task (12-12)
  • Task (13-14)
  • Task (15-16)
  • Task (36-36)
  • List (34-34)
  • List (35-35)
Backend/Models/SemanticDomain.cs (6)
  • SemanticDomainTreeNode (133-180)
  • SemanticDomainTreeNode (171-179)
  • SemanticDomainFull (82-128)
  • SemanticDomainFull (96-101)
  • SemanticDomainFull (103-116)
  • SemanticDomainFull (119-127)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: CodeQL analysis (csharp)
  • GitHub Check: Agent
  • GitHub Check: docker_build
🔇 Additional comments (8)
Backend/Interfaces/ILiftService.cs (1)

13-16: Interface changes align with DI pattern fix.

The updated method signatures correctly support per-request resolution of transient repositories by accepting them as parameters rather than relying on constructor injection. The CreateLiftRanges change to accept pre-fetched allDoms data avoids redundant repository calls and keeps the method self-contained.

Backend.Tests/Controllers/LiftControllerTests.cs (1)

51-55: Test setup correctly updated for new DI pattern.

The test initialization properly reflects the updated LiftController constructor signature and the parameterless LiftService constructor. The dependency wiring aligns with the production code changes.

Backend/Controllers/LiftController.cs (2)

25-37: Controller refactoring follows established DI patterns.

The controller now correctly owns the transient repository dependencies (ISemanticDomainRepository, ISpeakerRepository) and passes them to LiftService methods as parameters. This aligns with the existing pattern used for IWordRepository and IProjectRepository, ensuring per-request resolution of transient services.


426-429: Export call correctly passes all required repositories.

The CreateLiftExport method properly forwards all repository dependencies to _liftService.LiftExport, matching the updated interface signature.

Backend/Services/LiftService.cs (4)

120-129: Constructor correctly simplified to parameterless form.

Removing the repository dependencies from the constructor resolves the singleton-holds-transient anti-pattern. The Sldr initialization and concurrent dictionary setup remain appropriately in the constructor as singleton-scoped state.


256-263: Method signature and project retrieval updated correctly.

The expanded LiftExport signature accepts repositories as parameters, enabling per-request dependency resolution. The null-coalescing throw pattern (?? throw new MissingProjectException(...)) provides clear error handling for missing projects.


309-309: Repository usage correctly migrated to parameters.

Speaker and semantic domain data are now fetched using the injected repository parameters. The null-coalescing to empty list (?? []) on line 316 provides safe fallback when no semantic domains exist for English.

Also applies to: 316-317


445-446: CreateLiftRanges refactored to use pre-fetched data.

The method now accepts allDoms as a parameter rather than fetching internally, which:

  1. Eliminates redundant repository calls (data already fetched in LiftExport)
  2. Removes the implicit async repository dependency
  3. Makes the method's data requirements explicit

The Find on line 470 correctly handles the case where a parent domain may not exist by using null-conditional access (?.Name).

Also applies to: 461-461, 470-470

Copy link
Contributor

@jasonleenaylor jasonleenaylor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:lgtm:

@jasonleenaylor reviewed 5 files and all commit messages, and made 1 comment.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on @imnasnainaec).

@imnasnainaec imnasnainaec merged commit ae3826b into master Jan 9, 2026
21 of 22 checks passed
@imnasnainaec imnasnainaec deleted the copilot/refactor-liftservice-dependencies branch January 9, 2026 19:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix singleton LiftService's dependency on transient repositories

3 participants