diff --git a/src/ScadaLink.ManagementService/ManagementActor.cs b/src/ScadaLink.ManagementService/ManagementActor.cs index 8b69783..e259012 100644 --- a/src/ScadaLink.ManagementService/ManagementActor.cs +++ b/src/ScadaLink.ManagementService/ManagementActor.cs @@ -1838,22 +1838,29 @@ public class ManagementActor : ReceiveActor $"Bundle has {blockers.Count} blocker(s); import aborted. {details}"); } + // Dedupe by (EntityType, Name) -- the preview can emit multiple rows per + // entity (e.g. one row per modified member of a template), but ApplyAsync + // requires a unique resolution per key. Last-write-wins matches the + // Central UI's TransportImport.BuildDefaultResolutions behavior. var renameStamp = DateTime.UtcNow.ToString("yyyyMMdd-HHmmss"); - var resolutions = preview.Items.Select(item => new ImportResolution( - item.EntityType, - item.Name, - item.Kind switch + var resolutionsMap = new Dictionary<(string, string), ImportResolution>(); + foreach (var item in preview.Items) + { + var action = item.Kind switch { ConflictKind.New => ResolutionAction.Add, ConflictKind.Identical => ResolutionAction.Skip, ConflictKind.Modified => policy, _ => ResolutionAction.Skip, - }, - (item.Kind == ConflictKind.Modified && policy == ResolutionAction.Rename) + }; + var renameTo = (item.Kind == ConflictKind.Modified && policy == ResolutionAction.Rename) ? $"{item.Name}-imported-{renameStamp}" - : null)).ToList(); + : null; + resolutionsMap[(item.EntityType, item.Name)] = new ImportResolution( + item.EntityType, item.Name, action, renameTo); + } - return await importer.ApplyAsync(session.SessionId, resolutions, username); + return await importer.ApplyAsync(session.SessionId, resolutionsMap.Values.ToList(), username); } private static byte[] DecodeBundle(string base64)