docs(comments): strip internal task/milestone/bundle bookkeeping from code comments
Remove project bookkeeping citations from shipped code comments across the solution: hyphenated task IDs (WP-14, StoreAndForward-025), milestone/task/ issue refs (M3, Task 4, Audit Log #23, #21), Bundle X task-bundle labels, and C/D/K/S/T phase labels. Comment text only — no code logic, string/log literals, or XML-doc structure changed. Genuine descriptions are preserved (only the citation is stripped), and technical lookalikes are retained (UTF-8, SHA-256, T00:00:00, M365, UTC-5, pre-C4/pre-C5 schema versions). Flagged by the new CommentChecker TaskReferenceInComment / TrackingReferenceInComment checks plus targeted grep passes; full solution builds clean, append-only guard tests pass.
This commit is contained in:
@@ -49,7 +49,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
{
|
||||
// All bundle content deserialization goes through BundleJsonOptions.Default.
|
||||
// IMPORTANT — unknown JSON properties must remain ALLOWED (the default
|
||||
// JsonUnmappedMemberHandling.Skip). Pre-C4 bundles may carry a top-level
|
||||
// JsonUnmappedMemberHandling.Skip). Legacy bundles may carry a top-level
|
||||
// "apiKeys" array and/or "ApprovedApiKeyIds" inside "apiMethods[]" entries.
|
||||
// Setting JsonUnmappedMemberHandling.Disallow here would cause those bundles
|
||||
// to fail deserialization, breaking backward-compat. This tolerance is
|
||||
@@ -74,7 +74,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
private readonly IOptions<TransportOptions> _options;
|
||||
private readonly TimeProvider _timeProvider;
|
||||
private readonly SemanticValidator _semanticValidator;
|
||||
// #16 (M8 D2): optional. Implemented in DeploymentManager (where the flattening
|
||||
// Optional. Implemented in DeploymentManager (where the flattening
|
||||
// pipeline lives) and surfaced via the Commons IStaleInstanceProbe seam. Null
|
||||
// in a host that registers Transport without DeploymentManager — staleness is
|
||||
// then best-effort empty (informational only, never gates the import).
|
||||
@@ -96,12 +96,12 @@ public sealed class BundleImporter : IBundleImporter
|
||||
/// <param name="externalRepo">External system repository for diff and apply.</param>
|
||||
/// <param name="notificationRepo">Notification repository for diff and apply.</param>
|
||||
/// <param name="inboundApiRepo">Inbound API repository for diff and apply.</param>
|
||||
/// <param name="siteRepo">Site repository — supplies the target sites and site-scoped data connections that the preview's site/connection auto-match resolves against (M8 C2).</param>
|
||||
/// <param name="siteRepo">Site repository — supplies the target sites and site-scoped data connections that the preview's site/connection auto-match resolves against.</param>
|
||||
/// <param name="auditService">Audit service for writing per-entity import audit rows.</param>
|
||||
/// <param name="correlationContext">Correlation context that carries the active BundleImportId.</param>
|
||||
/// <param name="dbContext">EF Core context used to commit the import transaction.</param>
|
||||
/// <param name="semanticValidator">Validates template references before applying.</param>
|
||||
/// <param name="staleInstanceProbe">Optional (#16): recomputes a deployed instance's current revision hash so the importer can enumerate instances stale-ed by a template overwrite. Null when no flattening pipeline is registered (Transport-without-DeploymentManager hosts) — staleness is then skipped.</param>
|
||||
/// <param name="staleInstanceProbe">Optional: recomputes a deployed instance's current revision hash so the importer can enumerate instances stale-ed by a template overwrite. Null when no flattening pipeline is registered (Transport-without-DeploymentManager hosts) — staleness is then skipped.</param>
|
||||
/// <param name="logger">Optional logger.</param>
|
||||
public BundleImporter(
|
||||
BundleSerializer bundleSerializer,
|
||||
@@ -371,7 +371,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
}
|
||||
|
||||
// ---- Templates ----
|
||||
// Transport-008: previously this loop iterated GetAllTemplatesAsync()
|
||||
// Previously this loop iterated GetAllTemplatesAsync()
|
||||
// and called GetTemplateWithChildrenAsync(stub.Id) once per matching
|
||||
// name (classic N+1). The bulk variant fetches every matching template
|
||||
// with children eager-loaded in a single round-trip.
|
||||
@@ -429,7 +429,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
items.Add(_diff.CompareSmtpConfiguration(sm, existing));
|
||||
}
|
||||
|
||||
// ---- SmsConfigurations (S10b; no by-AccountSid lookup — scan GetAll) ----
|
||||
// ---- SmsConfigurations (no by-AccountSid lookup — scan GetAll) ----
|
||||
var allSms = await _notificationRepo.GetAllSmsConfigurationsAsync(ct).ConfigureAwait(false);
|
||||
var smsBySid = allSms.ToDictionary(s => s.AccountSid, s => s, StringComparer.Ordinal);
|
||||
foreach (var sms in content.SmsConfigs)
|
||||
@@ -439,8 +439,8 @@ public sealed class BundleImporter : IBundleImporter
|
||||
}
|
||||
|
||||
// ---- ApiKeys ----
|
||||
// Inbound API keys are not transported between environments (re-arch C4).
|
||||
// New bundles never carry a keys section. A pre-C4 bundle may still contain
|
||||
// Inbound API keys are not transported between environments.
|
||||
// New bundles never carry a keys section. A legacy bundle may still contain
|
||||
// one; we do NOT surface those keys as importable preview rows (they would
|
||||
// offer Add/Overwrite actions for keys that can't be meaningfully re-created
|
||||
// from a hash). They are counted, ignored, and reported at apply time.
|
||||
@@ -452,7 +452,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
items.Add(_diff.CompareApiMethod(m, existing));
|
||||
}
|
||||
|
||||
// ---- M8 site/instance-scoped types ----
|
||||
// ---- Site/instance-scoped types ----
|
||||
// Sites/DataConnections/Instances are referenced by stable string identity
|
||||
// (SiteIdentifier / connection Name / UniqueName) and resolved against the
|
||||
// TARGET environment's own surrogate keys. We auto-match the source site by
|
||||
@@ -493,7 +493,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
}
|
||||
|
||||
// ---- DataConnections (site-scoped; matched by name within the auto-matched target site) ----
|
||||
// C2: connection names are unique only WITHIN a site, so the preview item's
|
||||
// Connection names are unique only WITHIN a site, so the preview item's
|
||||
// identity is SITE-QUALIFIED (`{SiteIdentifier}/{Name}`). The diff CONTENT is
|
||||
// unchanged — only the item Name is qualified (after CompareDataConnection
|
||||
// returns) so two sites' same-named connections resolve to distinct items and
|
||||
@@ -543,7 +543,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
instDto, existing, existingTemplateName, existingSiteIdentifier, existingAreaName));
|
||||
}
|
||||
|
||||
// ---- Required site/connection mappings (M8) ----
|
||||
// ---- Required site/connection mappings ----
|
||||
var (requiredSites, requiredConnections) = await BuildRequiredMappingsAsync(
|
||||
content, ResolveTargetSiteAsync, ResolveTargetConnectionsAsync).ConfigureAwait(false);
|
||||
|
||||
@@ -556,7 +556,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
/// <summary>
|
||||
/// Collects the distinct set of source sites and (site, connection) pairs the
|
||||
/// bundle references, auto-matching each against the TARGET environment by
|
||||
/// identity, and returns the operator-facing required-mapping lists (M8 C2).
|
||||
/// identity, and returns the operator-facing required-mapping lists.
|
||||
/// <para>
|
||||
/// Site references are drawn from every instance, every site, and every
|
||||
/// data-connection in the bundle. Connection references are drawn from every
|
||||
@@ -666,7 +666,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
{
|
||||
var blockers = new List<ImportPreviewItem>();
|
||||
|
||||
// ---- M8: instance template + referenced-connection blockers ----
|
||||
// ---- Instance template + referenced-connection blockers ----
|
||||
// The set of template names the import can satisfy = (in-bundle templates)
|
||||
// ∪ (templates already in the target DB). An instance whose TemplateName is
|
||||
// in neither is unresolvable.
|
||||
@@ -735,7 +735,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
if (bundleConnections.Contains((site, name))) continue;
|
||||
var targetConns = await resolveTargetConnections(site).ConfigureAwait(false);
|
||||
if (targetConns.Any(c => string.Equals(c.Name, name, StringComparison.Ordinal))) continue;
|
||||
// C2: site-qualify the blocker Name so two sites' same-named-but-missing
|
||||
// Site-qualify the blocker Name so two sites' same-named-but-missing
|
||||
// connections surface as distinct blockers (a bare name would collide).
|
||||
blockers.Add(new ImportPreviewItem(
|
||||
EntityType: "Instance",
|
||||
@@ -888,7 +888,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
private static bool IsIdentifierChar(char c) => c == '_' || char.IsLetterOrDigit(c);
|
||||
|
||||
/// <summary>
|
||||
/// C2: the site-qualified identity (<c>{siteIdentifier}/{connectionName}</c>) used
|
||||
/// The site-qualified identity (<c>{siteIdentifier}/{connectionName}</c>) used
|
||||
/// for every <c>DataConnection</c> preview item Name + blocker Name, and for the
|
||||
/// matching resolution lookup in <see cref="ApplyDataConnectionsAsync"/>. Connection
|
||||
/// names are unique only WITHIN a site, so a bare-name key would collapse two sites'
|
||||
@@ -939,8 +939,8 @@ public sealed class BundleImporter : IBundleImporter
|
||||
r => r);
|
||||
var summary = new ImportSummary();
|
||||
|
||||
// Inbound API keys are not transported between environments (re-arch C4).
|
||||
// A pre-C4 bundle may still contain a keys section; we ignore those keys
|
||||
// Inbound API keys are not transported between environments.
|
||||
// A legacy bundle may still contain a keys section; we ignore those keys
|
||||
// entirely (never re-create them) but count them so the result can tell
|
||||
// the operator to re-issue keys on this environment.
|
||||
var apiKeysIgnored = content.ApiKeys?.Count ?? 0;
|
||||
@@ -965,7 +965,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
// pre-existing target reads, so it has no ordering dependency on
|
||||
// the Apply* helpers. Failing here means the change tracker is
|
||||
// still empty, which keeps the rollback contract simple on both
|
||||
// the in-memory and relational providers (FU-B: intermediate
|
||||
// the in-memory and relational providers (intermediate
|
||||
// SaveChangesAsync between Apply* and the second-pass rewire
|
||||
// would otherwise prevent the in-memory provider from undoing
|
||||
// already-flushed template rows on validation failure — the
|
||||
@@ -976,7 +976,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
// a Skip on a dependency surfaces as a missing-reference error
|
||||
// rather than silently passing.
|
||||
var validationErrors = await RunSemanticValidationAsync(content, resolutionMap, ct).ConfigureAwait(false);
|
||||
// M8: validate every site / connection / template reference the
|
||||
// Validate every site / connection / template reference the
|
||||
// site-instance payload depends on BEFORE any row is staged. Running
|
||||
// this in the validation phase (not as an apply-pass guard) preserves
|
||||
// the rollback contract: a structurally-unresolvable bundle fails with
|
||||
@@ -992,7 +992,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
throw new SemanticValidationException(validationErrors);
|
||||
}
|
||||
|
||||
// ---- M8 site/instance-scoped apply: sites + connections FIRST ----
|
||||
// ---- Site/instance-scoped apply: sites + connections FIRST ----
|
||||
// Sites are the FK target for both data connections (SiteId) and
|
||||
// instances (SiteId); data connections are the FK target for instance
|
||||
// connection bindings (DataConnectionId) and the rewrite target for
|
||||
@@ -1020,11 +1020,11 @@ public sealed class BundleImporter : IBundleImporter
|
||||
await ApplyNotificationListsAsync(content.NotificationLists, resolutionMap, user, summary, ct).ConfigureAwait(false);
|
||||
await ApplySmtpConfigsAsync(content.SmtpConfigs, resolutionMap, user, summary, ct).ConfigureAwait(false);
|
||||
await ApplySmsConfigsAsync(content.SmsConfigs, resolutionMap, user, summary, ct).ConfigureAwait(false);
|
||||
// Inbound API keys are NOT applied from a bundle (re-arch C4) — any keys
|
||||
// Inbound API keys are NOT applied from a bundle — any keys
|
||||
// in a legacy bundle were counted above (apiKeysIgnored) and are skipped.
|
||||
await ApplyApiMethodsAsync(content.ApiMethods, resolutionMap, user, summary, ct).ConfigureAwait(false);
|
||||
|
||||
// FU-B / #39 + remainder of #37 — second-pass rewire of name-keyed
|
||||
// Second-pass rewire of name-keyed
|
||||
// FKs that can only be resolved AFTER every template's scripts and
|
||||
// child rows have been staged. We flush here so Pass A can look up
|
||||
// the just-persisted scripts by name and Pass B can look up the
|
||||
@@ -1043,7 +1043,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
await ResolveAlarmScriptLinksAsync(content.Templates, resolutionMap, user, ct).ConfigureAwait(false);
|
||||
await ResolveCompositionEdgesAsync(content.Templates, resolutionMap, user, ct).ConfigureAwait(false);
|
||||
|
||||
// ---- M8 site/instance-scoped apply: instances LAST ----
|
||||
// ---- Site/instance-scoped apply: instances LAST ----
|
||||
// The instance pass needs every FK target materialised first:
|
||||
// • template id — resolved by name against the just-flushed central
|
||||
// config (Templates were flushed above for the alarm/composition
|
||||
@@ -1058,9 +1058,9 @@ public sealed class BundleImporter : IBundleImporter
|
||||
content, resolutionMap, siteBySourceIdentifier, connectionMaps,
|
||||
user, summary, ct).ConfigureAwait(false);
|
||||
|
||||
// ---- D2 pre-commit point (#16): stale-instance computation ----
|
||||
// ---- Pre-commit point: stale-instance computation ----
|
||||
// Everything the import will write is staged on the change tracker at
|
||||
// this point but NOT yet committed. D2 computes StaleInstanceIds here
|
||||
// this point but NOT yet committed. This computes StaleInstanceIds here
|
||||
// — target instances whose template was overwritten by this import and
|
||||
// therefore now carry a stale flattened-config revision — and threads
|
||||
// the resulting list into the ImportResult below (replacing the
|
||||
@@ -1088,7 +1088,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
summary.Skipped,
|
||||
summary.Renamed,
|
||||
},
|
||||
// re-arch C4: legacy inbound API keys present in the bundle that
|
||||
// Legacy inbound API keys present in the bundle that
|
||||
// were ignored (not transported / not re-created here).
|
||||
ApiKeysIgnored = apiKeysIgnored,
|
||||
},
|
||||
@@ -1220,7 +1220,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #16 (M8 D2): enumerates the deployed instances stale-ed by this import.
|
||||
/// Enumerates the deployed instances stale-ed by this import.
|
||||
/// Overwriting a template changes the flattened-config hash of every instance
|
||||
/// of that template, so a deployed instance's freshly-computed revision hash
|
||||
/// will no longer match the hash captured in its <c>DeployedConfigSnapshot</c>
|
||||
@@ -1896,7 +1896,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FU-B / remainder of #37 — Pass A of the post-template-flush rewire.
|
||||
/// Pass A of the post-template-flush rewire.
|
||||
/// For every imported template (Add / Overwrite / Rename) whose bundle DTO
|
||||
/// carries any alarm with <c>OnTriggerScriptName</c>, look up the
|
||||
/// persisted alarm + script by name on the same template and set the FK.
|
||||
@@ -1970,7 +1970,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FU-B / #39 — Pass B of the post-template-flush rewire. For every
|
||||
/// Pass B of the post-template-flush rewire. For every
|
||||
/// imported template (Add / Overwrite / Rename) whose bundle DTO carries
|
||||
/// any <c>Compositions</c>, replace the persisted template's existing
|
||||
/// composition rows with new ones whose <c>ComposedTemplateId</c> is
|
||||
@@ -2453,7 +2453,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
return list;
|
||||
}
|
||||
|
||||
// S10: reconstruct a recipient carrying whichever contact(s) the bundle holds.
|
||||
// Reconstruct a recipient carrying whichever contact(s) the bundle holds.
|
||||
// Email-only and SMS-only round-trip exactly; a recipient with both keeps both.
|
||||
// Build via the email ctor when an address is present (so the historical
|
||||
// non-null-email path is unchanged) and additively restore the phone number;
|
||||
@@ -2544,7 +2544,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
target.Credentials = dto.Secrets?.Values.TryGetValue("Credentials", out var cred) == true ? cred : null;
|
||||
}
|
||||
|
||||
// SMS (S10b): mirrors ApplySmtpConfigsAsync exactly. SmsConfiguration is keyed by
|
||||
// SMS: mirrors ApplySmtpConfigsAsync exactly. SmsConfiguration is keyed by
|
||||
// AccountSid (the diff engine's natural key — analogous to SMTP's Host), so a
|
||||
// Rename targets AccountSid and Overwrite matches an existing config by AccountSid.
|
||||
// The provider auth token is decrypted out of the SecretsBlock via the same path
|
||||
@@ -2618,9 +2618,9 @@ public sealed class BundleImporter : IBundleImporter
|
||||
target.AuthToken = dto.Secrets?.Values.TryGetValue("AuthToken", out var token) == true ? token : null;
|
||||
}
|
||||
|
||||
// ApplyApiKeysAsync was removed in re-arch C4: inbound API keys are not
|
||||
// ApplyApiKeysAsync was removed: inbound API keys are not
|
||||
// transported between environments, so a bundle never re-creates keys. Any keys
|
||||
// present in a legacy (pre-C4) bundle are counted and ignored in ApplyAsync.
|
||||
// present in a legacy bundle are counted and ignored in ApplyAsync.
|
||||
|
||||
private async Task ApplyApiMethodsAsync(
|
||||
IReadOnlyList<ApiMethodDto> dtos,
|
||||
@@ -2651,9 +2651,9 @@ public sealed class BundleImporter : IBundleImporter
|
||||
}
|
||||
case ResolutionAction.Overwrite when existing is not null:
|
||||
existing.Script = dto.Script;
|
||||
// Method→key scopes are not transported (re-arch C4) and the
|
||||
// Method→key scopes are not transported and the
|
||||
// ApprovedApiKeyIds column was dropped with the SQL Server ApiKey
|
||||
// entity (re-arch C5): scopes are re-granted per environment in the
|
||||
// entity: scopes are re-granted per environment in the
|
||||
// shared ZB.MOM.WW.Auth.ApiKeys store, never via an imported bundle.
|
||||
existing.ParameterDefinitions = dto.ParameterDefinitions;
|
||||
existing.ReturnDefinition = dto.ReturnDefinition;
|
||||
@@ -2680,9 +2680,9 @@ public sealed class BundleImporter : IBundleImporter
|
||||
|
||||
private static ApiMethod BuildApiMethod(ApiMethodDto dto, string? overrideName)
|
||||
{
|
||||
// Method→key scopes are not transported (re-arch C4); the ApprovedApiKeyIds
|
||||
// column that once carried them was dropped with the SQL Server ApiKey entity
|
||||
// (re-arch C5). Scopes are re-granted per environment in the shared
|
||||
// Method→key scopes are not transported; the ApprovedApiKeyIds
|
||||
// column that once carried them was dropped with the SQL Server ApiKey entity.
|
||||
// Scopes are re-granted per environment in the shared
|
||||
// ZB.MOM.WW.Auth.ApiKeys store.
|
||||
return new ApiMethod(overrideName ?? dto.Name, dto.Script)
|
||||
{
|
||||
@@ -2693,7 +2693,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────
|
||||
// M8 D1 — site/instance-scoped apply.
|
||||
// Site/instance-scoped apply.
|
||||
//
|
||||
// Three passes (sites → connections → instances) resolve every cross-
|
||||
// environment name reference against the TARGET database's own surrogate
|
||||
@@ -2716,7 +2716,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
/// the same identifier resolves to <see cref="MappingAction.MapToExisting"/>,
|
||||
/// otherwise <see cref="MappingAction.CreateNew"/>. <c>CreateNew</c> inserts a
|
||||
/// site from the full <see cref="SiteDto"/> payload (display name, description,
|
||||
/// and the verbatim Node A/B + gRPC Node A/B addresses — D3's "carry full
|
||||
/// and the verbatim Node A/B + gRPC Node A/B addresses — the "carry full
|
||||
/// config" decision). <c>MapToExisting</c> honours the site's
|
||||
/// <see cref="ImportResolution"/>: <see cref="ResolutionAction.Skip"/> leaves
|
||||
/// the target untouched; <see cref="ResolutionAction.Overwrite"/> applies the
|
||||
@@ -2964,7 +2964,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
}
|
||||
|
||||
// MapToExisting — honour the connection's own conflict resolution.
|
||||
// C2: the resolution is keyed by the SITE-QUALIFIED name
|
||||
// The resolution is keyed by the SITE-QUALIFIED name
|
||||
// (`{SiteIdentifier}/{Name}`), matching the qualified preview-item Name —
|
||||
// so a same-named connection under a different site can't pick up the
|
||||
// wrong Skip/Overwrite.
|
||||
@@ -2989,7 +2989,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
targetNameByRef[key] = existing.Name;
|
||||
}
|
||||
|
||||
// ---- C1 Pass 2 — referenced-but-not-carried connections ----
|
||||
// ---- Pass 2 — referenced-but-not-carried connections ----
|
||||
// A binding (or native-alarm-source override) can name a connection that
|
||||
// exists in the TARGET database but was NOT carried in the bundle's
|
||||
// DataConnections (e.g. exported without it). Preview correctly does NOT
|
||||
@@ -3043,7 +3043,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// C1: every distinct <c>(sourceSiteIdentifier, connectionName)</c> pair an instance
|
||||
/// Every distinct <c>(sourceSiteIdentifier, connectionName)</c> pair an instance
|
||||
/// references — via a <see cref="InstanceConnectionBindingDto.ConnectionName"/> or a
|
||||
/// non-null <see cref="InstanceNativeAlarmSourceOverrideDto.ConnectionNameOverride"/>.
|
||||
/// Drives the connection-map Pass 2 that resolves references the bundle didn't carry.
|
||||
@@ -3348,7 +3348,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Resolve ConnectionName → target DataConnectionId. After the C1 Pass-2
|
||||
// Resolve ConnectionName → target DataConnectionId. After Pass 2
|
||||
// in ApplyDataConnectionsAsync, the map carries an entry for every
|
||||
// referenced connection — carried in the bundle OR auto-matched in the
|
||||
// target. A non-empty name that STILL doesn't resolve is a structural
|
||||
@@ -3430,7 +3430,7 @@ public sealed class BundleImporter : IBundleImporter
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// M8: validates every cross-environment reference the site/instance payload
|
||||
/// Validates every cross-environment reference the site/instance payload
|
||||
/// depends on, BEFORE any row is staged — so a structurally-unresolvable
|
||||
/// bundle fails with an empty change tracker (preserving the all-or-nothing
|
||||
/// rollback contract on every EF provider, including the in-memory one whose
|
||||
|
||||
Reference in New Issue
Block a user