diff --git a/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Services/InstanceLocator.cs b/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Services/InstanceLocator.cs index 18b1eaea..ed0dbcf6 100644 --- a/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Services/InstanceLocator.cs +++ b/src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Services/InstanceLocator.cs @@ -22,15 +22,13 @@ public class InstanceLocator : IInstanceLocator string instanceUniqueName, CancellationToken cancellationToken = default) { - var instance = await _context.Set() - .FirstOrDefaultAsync(i => i.UniqueName == instanceUniqueName, cancellationToken); - - if (instance == null) - return null; - - var site = await _context.Set() - .FindAsync(new object[] { instance.SiteId }, cancellationToken); - - return site?.SiteIdentifier; + // One untracked projected join: this sits on the inbound-API Route.To hot path and + // only needs a single string, so it must not materialise (and track) two entities. + return await ( + from i in _context.Set().AsNoTracking() + join s in _context.Set().AsNoTracking() on i.SiteId equals s.Id + where i.UniqueName == instanceUniqueName + select s.SiteIdentifier) + .FirstOrDefaultAsync(cancellationToken); } } diff --git a/tests/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests/RepositoryCoverageTests.cs b/tests/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests/RepositoryCoverageTests.cs index 8efd568e..40915e3c 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests/RepositoryCoverageTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests/RepositoryCoverageTests.cs @@ -1486,6 +1486,24 @@ public class InstanceLocatorTests : IDisposable Assert.Equal("SITE-001", result); } + /// The lookup must not track entities (single untracked projection, review 08/P7). + /// A task that represents the asynchronous operation. + [Fact] + public async Task GetSiteIdForInstance_DoesNotTrackEntities() + { + var site = new Site("Site1", "SITE-001"); + var template = new Template("T1"); + _context.Sites.Add(site); + _context.Templates.Add(template); + await _context.SaveChangesAsync(); + _context.Instances.Add(new Instance("Pump1") { SiteId = site.Id, TemplateId = template.Id }); + await _context.SaveChangesAsync(); + _context.ChangeTracker.Clear(); + + Assert.Equal("SITE-001", await _locator.GetSiteIdForInstanceAsync("Pump1")); + Assert.Empty(_context.ChangeTracker.Entries()); + } + /// Verifies that getting the site id for a missing instance returns null. /// A task that represents the asynchronous operation. [Fact]