using Microsoft.EntityFrameworkCore.Migrations; #nullable disable namespace ScadaLink.ConfigurationDatabase.Migrations { /// public partial class MigrateCompositionsToDerived : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) { // Re-shape every pre-Phase-2 TemplateComposition so it points at a // newly created derived template (".") that inherits // from the original base. Attribute and script rows are copied with // IsInherited=1; the composition's ComposedTemplateId is repointed. // // Idempotent: only rows whose target is still IsDerived=0 are touched. // Aborts the migration if any derived name would collide with an // existing template, so the operator can resolve manually. migrationBuilder.Sql(@" SET NOCOUNT ON; DECLARE @collisions NVARCHAR(MAX) = ( SELECT STRING_AGG(owner.Name + N'.' + c.InstanceName, N', ') FROM TemplateCompositions c INNER JOIN Templates base_t ON base_t.Id = c.ComposedTemplateId INNER JOIN Templates owner ON owner.Id = c.TemplateId INNER JOIN Templates existing ON existing.Name = owner.Name + N'.' + c.InstanceName WHERE base_t.IsDerived = 0 ); IF @collisions IS NOT NULL BEGIN DECLARE @msg NVARCHAR(MAX) = N'MigrateCompositionsToDerived: cannot create derived templates — these names already exist: ' + @collisions + N'. Rename the conflicting templates and retry the migration.'; THROW 50000, @msg, 1; END DECLARE @CompId INT, @BaseId INT, @OwnerName NVARCHAR(200), @SlotName NVARCHAR(200); DECLARE @NewId INT, @NewName NVARCHAR(200); DECLARE map_cursor CURSOR FAST_FORWARD FOR SELECT c.Id, c.ComposedTemplateId, owner.Name, c.InstanceName FROM TemplateCompositions c INNER JOIN Templates base_t ON base_t.Id = c.ComposedTemplateId INNER JOIN Templates owner ON owner.Id = c.TemplateId WHERE base_t.IsDerived = 0; OPEN map_cursor; FETCH NEXT FROM map_cursor INTO @CompId, @BaseId, @OwnerName, @SlotName; WHILE @@FETCH_STATUS = 0 BEGIN SET @NewName = @OwnerName + N'.' + @SlotName; INSERT INTO Templates (Name, Description, ParentTemplateId, FolderId, IsDerived, OwnerCompositionId) SELECT @NewName, b.Description, b.Id, NULL, 1, @CompId FROM Templates b WHERE b.Id = @BaseId; SET @NewId = SCOPE_IDENTITY(); INSERT INTO TemplateAttributes (TemplateId, Name, Value, DataType, IsLocked, Description, DataSourceReference, IsInherited, LockedInDerived) SELECT @NewId, a.Name, a.Value, a.DataType, a.IsLocked, a.Description, a.DataSourceReference, 1, 0 FROM TemplateAttributes a WHERE a.TemplateId = @BaseId; INSERT INTO TemplateScripts (TemplateId, Name, Code, IsLocked, TriggerType, TriggerConfiguration, ParameterDefinitions, ReturnDefinition, MinTimeBetweenRuns, IsInherited, LockedInDerived) SELECT @NewId, s.Name, s.Code, s.IsLocked, s.TriggerType, s.TriggerConfiguration, s.ParameterDefinitions, s.ReturnDefinition, s.MinTimeBetweenRuns, 1, 0 FROM TemplateScripts s WHERE s.TemplateId = @BaseId; UPDATE TemplateCompositions SET ComposedTemplateId = @NewId WHERE Id = @CompId; FETCH NEXT FROM map_cursor INTO @CompId, @BaseId, @OwnerName, @SlotName; END CLOSE map_cursor; DEALLOCATE map_cursor; "); } /// protected override void Down(MigrationBuilder migrationBuilder) { // Reverse: repoint each composition back to the derived template's // base, then drop the derived templates (with their copied rows). migrationBuilder.Sql(@" SET NOCOUNT ON; UPDATE c SET c.ComposedTemplateId = d.ParentTemplateId FROM TemplateCompositions c INNER JOIN Templates d ON d.Id = c.ComposedTemplateId WHERE d.IsDerived = 1 AND d.OwnerCompositionId = c.Id AND d.ParentTemplateId IS NOT NULL; DELETE a FROM TemplateAttributes a INNER JOIN Templates t ON t.Id = a.TemplateId WHERE t.IsDerived = 1; DELETE s FROM TemplateScripts s INNER JOIN Templates t ON t.Id = s.TemplateId WHERE t.IsDerived = 1; DELETE FROM Templates WHERE IsDerived = 1; "); } } }