< Summary

Information
Class: OzonRepositories.Context.ProdSchemaAlignment
Assembly: OzonRepositories
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonRepositories/Context/ProdSchemaAlignment.cs
Line coverage
99%
Covered lines: 509
Uncovered lines: 2
Coverable lines: 511
Total lines: 640
Line coverage: 99.6%
Branch coverage
97%
Covered branches: 39
Total branches: 40
Branch coverage: 97.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Table()100%11100%
.cctor()100%11100%
Apply(...)100%3636100%
ResolveProperties(...)75%4477.77%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonRepositories/Context/ProdSchemaAlignment.cs

#LineLine coverage
 1using Microsoft.EntityFrameworkCore;
 2using Microsoft.EntityFrameworkCore.Metadata;
 3
 4namespace OzonRepositories.Context;
 5
 6/// <summary>
 7/// Приводит EF-модель к фактической схеме боевой БД ST2.
 8///
 9/// Историческая причина: почти вся схема создавалась не миграциями, а руками —
 10/// скриптами из Scripts/Sql и runtime-DDL в сервисах. Там писали DATETIME, TEXT,
 11/// VARCHAR(N), DECIMAL(18,4) и заводили индексы, о которых модель никогда не знала.
 12/// EF по умолчанию выводит datetime(6), longtext и decimal(65,30), поэтому модель
 13/// и прод разъехались на 139 колонок и 90 индексов, а сгенерированный из модели
 14/// baseline не воспроизводил реальную схему.
 15///
 16/// Здесь модель подгоняется под БД, а не наоборот: правки чисто модельные и ALTER-ов
 17/// на боевых таблицах не требуют. Обратное направление (несколько мест, где БД была
 18/// слабее модели) закрыто миграцией ReconcileProdSchema.
 19///
 20/// Списки сняты из information_schema и должны со временем пустеть: новые колонки и
 21/// индексы задавайте сразу в конфигурации сущности, сюда ничего не дописывайте.
 22/// Полноту списков стережёт ProdSchemaAlignmentTests.
 23/// </summary>
 24internal static class ProdSchemaAlignment
 25{
 142826    internal sealed record LegacyIndex(string Table, string Name, bool IsUnique, params string[] Columns);
 27
 28    /// <summary>Типы колонок, которые в БД отличаются от выводимых EF по умолчанию.</summary>
 129    internal static readonly Dictionary<(string Table, string Column), string> ColumnTypes = new()
 130    {
 131        [("AssemblyListAccessKeys", "AllowedOzonClientIds")] = "varchar(1000)",
 132        [("AssemblyListAccessKeys", "AllowedStatuses")] = "varchar(1000)",
 133        [("AssemblyListAccessKeys", "AllowedWarehouses")] = "varchar(500)",
 134        [("AssemblyListRunItems", "ImageUrl")] = "text",
 135        [("AssemblyListRunItems", "LabelError")] = "text",
 136        [("AssemblyListRunItems", "LabelUrl")] = "text",
 137        [("AssemblyListRunItems", "OneCBalance")] = "decimal(18,3)",
 138        [("AssemblyListRunItems", "OneCOtherStock")] = "text",
 139        [("AssemblyListRunItems", "OneCReceiptDoc")] = "varchar(64)",
 140        [("AssemblyListRunItems", "ProductName")] = "text",
 141        [("AssemblyListRunItems", "SupplierName")] = "varchar(255)",
 142        [("AssemblyListRuns", "ErrorMessage")] = "text",
 143        [("BackgroundJobExecutions", "CompletedAt")] = "datetime",
 144        [("BackgroundJobExecutions", "ErrorMessage")] = "text",
 145        [("BackgroundJobExecutions", "ErrorStackTrace")] = "text",
 146        [("BackgroundJobExecutions", "StartedAt")] = "datetime",
 147        [("BackgroundJobSchedules", "CreatedAt")] = "datetime",
 148        [("BackgroundJobSchedules", "LastRunTime")] = "datetime",
 149        [("BackgroundJobSchedules", "NextRunTime")] = "datetime",
 150        [("BackgroundJobSchedules", "UpdatedAt")] = "datetime",
 151        [("BackgroundJobSteps", "CompletedAt")] = "datetime",
 152        [("BackgroundJobSteps", "ErrorMessage")] = "text",
 153        [("BackgroundJobSteps", "StartedAt")] = "datetime",
 154        [("BackgroundOrderMatches", "CreatedAt")] = "datetime",
 155        [("BackgroundReportLoads", "CreatedAt")] = "datetime",
 156        [("BackgroundReportLoads", "ErrorMessage")] = "text",
 157        [("BackgroundReportLoads", "FileUrl")] = "text",
 158        [("BackgroundReportLoads", "ReportPeriodFrom")] = "datetime",
 159        [("BackgroundReportLoads", "ReportPeriodTo")] = "datetime",
 160        [("BackgroundTransactionResults", "CreatedAt")] = "datetime",
 161        [("BackgroundTransactionResults", "ErrorMessage")] = "text",
 162        [("BitrixStockArchiveExecutions", "CompletedAt")] = "datetime",
 163        [("BitrixStockArchiveExecutions", "ErrorMessage")] = "text",
 164        [("BitrixStockArchiveExecutions", "StartedAt")] = "datetime",
 165        [("BitrixStockArchiveItems", "ErrorMessage")] = "text",
 166        [("BitrixStockArchiveItems", "ProcessedAt")] = "datetime",
 167        [("BitrixStockAutoScheduleRuns", "CompletedAt")] = "datetime",
 168        [("BitrixStockAutoScheduleRuns", "ErrorMessage")] = "text",
 169        [("BitrixStockAutoScheduleRuns", "RunLog")] = "text",
 170        [("BitrixStockAutoScheduleRuns", "StartedAt")] = "datetime",
 171        [("BitrixStockAutoSchedules", "CreatedAt")] = "datetime",
 172        [("BitrixStockAutoSchedules", "LastRunAt")] = "datetime",
 173        [("BitrixStockAutoSchedules", "NextRunAt")] = "datetime",
 174        [("BitrixStockAutoSchedules", "UpdatedAt")] = "datetime",
 175        [("BitrixStockClearExecutions", "CompletedAt")] = "datetime",
 176        [("BitrixStockClearExecutions", "ErrorMessage")] = "text",
 177        [("BitrixStockClearExecutions", "StartedAt")] = "datetime",
 178        [("BitrixStockClearItems", "ErrorMessage")] = "text",
 179        [("BitrixStockClearItems", "ProcessedAt")] = "datetime",
 180        [("BitrixStockPushExecutions", "CompletedAt")] = "datetime",
 181        [("BitrixStockPushExecutions", "ErrorMessage")] = "text",
 182        [("BitrixStockPushExecutions", "StartedAt")] = "datetime",
 183        [("BitrixStockPushItems", "ErrorMessage")] = "text",
 184        [("BitrixStockPushItems", "ProcessedAt")] = "datetime",
 185        [("BitrixStockSavedFilter", "CreatedAt")] = "datetime",
 186        [("BitrixStockSavedFilter", "UpdatedAt")] = "datetime",
 187        [("BitrixStockUnarchiveExecutions", "CompletedAt")] = "datetime",
 188        [("BitrixStockUnarchiveExecutions", "ErrorMessage")] = "text",
 189        [("BitrixStockUnarchiveExecutions", "StartedAt")] = "datetime",
 190        [("BitrixStockUnarchiveItems", "ErrorMessage")] = "text",
 191        [("BitrixStockUnarchiveItems", "ProcessedAt")] = "datetime",
 192        [("ExcelMappings", "ColumnMappingsJson")] = "text",
 193        [("ExcelMappings", "CreatedBy")] = "varchar(255)",
 194        [("ExcelMappings", "CreatedDate")] = "datetime",
 195        [("ExcelMappings", "ModifiedDate")] = "datetime",
 196        [("FileUploadRecords", "Date")] = "datetime",
 197        [("FileUploadRecords", "Result")] = "text",
 198        [("ItemStatuses", "Name")] = "text",
 199        [("MlAutoCartItems", "Quantity")] = "decimal(18,3)",
 1100        [("MlAutoCartItems", "SourcePrice")] = "decimal(18,4)",
 1101        [("MlAutoOrderItems", "Price")] = "decimal(18,4)",
 1102        [("MlAutoOrderItems", "Quantity")] = "decimal(18,3)",
 1103        [("MlAutoRuCartItems", "Quantity")] = "decimal(18,3)",
 1104        [("MlAutoRuCartItems", "SourcePrice")] = "decimal(18,4)",
 1105        [("MlAutoRuOrderItems", "Price")] = "decimal(18,4)",
 1106        [("MlAutoRuOrderItems", "Quantity")] = "decimal(18,3)",
 1107        [("OrderArchives", "ArchivedAt")] = "datetime",
 1108        [("OrderCarts", "CreatedAt")] = "datetime",
 1109        [("OrderExtraExpenses", "CreatedAt")] = "datetime",
 1110        [("OrderHistoryOneCPostings", "CreatedAt")] = "datetime",
 1111        [("OrderHistoryOneCPostings", "PostedAt")] = "datetime",
 1112        [("OrderReturns", "ProductCommission")] = "decimal(18,2)",
 1113        [("OrderReturns", "ProductPrice")] = "decimal(18,2)",
 1114        [("OrderReturns", "StorageSumPrice")] = "decimal(18,2)",
 1115        [("OrderReturns", "UtilizationSumPrice")] = "decimal(18,2)",
 1116        [("OrderShipmentTotals", "CreatedAt")] = "datetime",
 1117        [("OrderShipmentTotals", "UpdatedAt")] = "datetime",
 1118        [("Orders", "CostPrice")] = "decimal(65,2)",
 1119        [("Orders", "MaxDiscount")] = "decimal(65,2)",
 1120        [("Orders", "MaxOzonCommission")] = "decimal(65,2)",
 1121        [("Orders", "MaxProfit")] = "decimal(65,2)",
 1122        [("Orders", "MinDiscount")] = "decimal(65,2)",
 1123        [("Orders", "MinOzonCommission")] = "decimal(65,2)",
 1124        [("Orders", "MinProfit")] = "decimal(65,2)",
 1125        [("Orders", "OriginalPurchasePrice")] = "decimal(65,2)",
 1126        [("Orders", "Price")] = "decimal(65,2)",
 1127        [("Orders", "PurchasePrice")] = "decimal(65,2)",
 1128        [("Orders", "ShipmentAmount")] = "decimal(65,2)",
 1129        [("OzonChatOrders", "CreatedAt")] = "datetime",
 1130        [("OzonChatOrders", "LastMessageDate")] = "datetime",
 1131        [("OzonChatOrders", "UpdatedAt")] = "datetime",
 1132        [("OzonChatResponseTimes", "CreatedAt")] = "datetime",
 1133        [("OzonChatResponseTimes", "RecordedAt")] = "datetime",
 1134        [("OzonPostingCancellations", "ReasonMessage")] = "text",
 1135        [("PriceHistories", "Article")] = "varchar(100)",
 1136        [("PriceHistories", "Author")] = "varchar(100)",
 1137        [("PriceHistories", "CreateDateTime")] = "datetime",
 1138        [("PriceHistories", "Currency")] = "varchar(10)",
 1139        [("PriceHistories", "PriceValue")] = "decimal(18,2)",
 1140        [("PriceHistories", "RegistrationPrice")] = "decimal(18,2)",
 1141        [("PriceHistories", "ValueSource")] = "varchar(100)",
 1142        [("StatusTransitionHistory", "Comment")] = "text",
 1143        [("StatusTransitions", "Conditions")] = "text",
 1144        [("SupplierQuotationOffers", "SourcePrice")] = "decimal(18,4)",
 1145        [("Suppliers", "CsvUrl")] = "varchar(250)",
 1146        [("WarehouseMapping", "BitrixWarehouseName")] = "varchar(200)",
 1147        [("WarehouseMapping", "DeliveryType")] = "varchar(100)",
 1148        [("WarehouseMapping", "OzonWarehouseId")] = "varchar(32)",
 1149        [("WarehouseMapping", "OzonWarehouseName")] = "varchar(200)",
 1150        [("WarehouseMapping", "ReturnBitrixWarehouseName")] = "varchar(255)",
 1151        [("WarehouseMapping", "TransitBitrixWarehouseName")] = "varchar(255)",
 1152        [("WarehouseStockSnapshot", "AccountingPriceUnit")] = "decimal(18,4)",
 1153        [("WarehouseStockSnapshot", "B1Qty")] = "decimal(18,4)",
 1154        [("WarehouseStockSnapshot", "B2Qty")] = "decimal(18,4)",
 1155        [("WarehouseStockSnapshot", "C0")] = "decimal(18,4)",
 1156        [("WarehouseStockSnapshot", "C1ExistMinus5Percent")] = "decimal(18,4)",
 1157        [("WarehouseStockSnapshot", "C1PercentToC0")] = "decimal(18,4)",
 1158        [("WarehouseStockSnapshot", "ExistPrice")] = "decimal(18,4)",
 1159        [("WarehouseStockSnapshot", "M1Qty")] = "decimal(18,4)",
 1160        [("WarehouseStockSnapshot", "M2Qty")] = "decimal(18,4)",
 1161        [("WarehouseStockSnapshot", "M3Qty")] = "decimal(18,4)",
 1162        [("WarehouseStockSnapshot", "PriceDiffAccountingMinusSale")] = "decimal(18,4)",
 1163        [("WarehouseStockSnapshot", "SalePriceUnit")] = "decimal(18,4)",
 1164        [("WarehouseStockSnapshot", "StockAmountByC0")] = "decimal(18,4)",
 1165        [("WarehouseStockSnapshot", "TotalCost")] = "decimal(18,4)",
 1166        [("WarehouseStockSnapshot", "UnitCost")] = "decimal(18,4)",
 1167        [("WarehouseStockSyncRuns", "ErrorMessage")] = "text",
 1168        [("order_extensions", "CreatedAt")] = "datetime",
 1169        [("orders_history", "NewValue")] = "text",
 1170        [("orders_history", "OldValue")] = "text",
 1171    };
 172
 173    /// <summary>Колонки, объявленные в БД как NOT NULL, но опциональные в модели.</summary>
 1174    internal static readonly HashSet<(string Table, string Column)> RequiredColumns = new()
 1175    {
 1176        ("ExcludedWarehouses", "Name"),
 1177        ("orders_history", "ColumnDisplayName"),
 1178        ("orders_history", "ColumnName"),
 1179    };
 180
 181    /// <summary>
 182    /// Имена индексов, которые на проде отличаются от выводимых EF.
 183    /// Ключ — таблица и колонки через запятую в порядке следования в индексе.
 184    /// Нужны, чтобы будущая миграция, удаляющая или переименовывающая индекс,
 185    /// обращалась к нему по тому имени, которое реально есть в боевой БД.
 186    /// </summary>
 1187    internal static readonly Dictionary<(string Table, string Columns), string> IndexNames = new()
 1188    {
 1189        [("order_extensions", "OrderId,Key")] = "UX_OrderExtensions_OrderId_Key",
 1190        [("ApiSupplierSettings", "ProviderKey")] = "UX_ApiSupplierSettings_ProviderKey",
 1191        [("AppStatuses", "StatusColorId")] = "FK_AppStatuses_StatusColors",
 1192        [("ArmtekCartItems", "ProviderKey,ActiveStudioOrderId")] = "UX_ArmtekCartItems_Provider_ActiveOrder",
 1193        [("ArmtekCartItems", "ProviderKey,CreatedAtUtc")] = "IX_ArmtekCartItems_Provider_Created",
 1194        [("ArmtekCartItems", "ProviderKey,StudioOrderId,Status")] = "IX_ArmtekCartItems_Provider_Order_Status",
 1195        [("ArmtekCartItems", "StudioOrderId")] = "FK_ArmtekCartItems_Orders_StudioOrderId",
 1196        [("ArmtekDeliveryAddressMappings", "ProviderKey,ShipmentWarehouseId")] = "UX_ArmtekDeliveryAddressMappings_Provi
 1197        [("ArmtekDeliveryAddressMappings", "ShipmentWarehouseId")] = "FK_ArmtekDeliveryAddressMappings_Warehouses_Shipme
 1198        [("ArmtekOrderItems", "ArmtekCartItemId")] = "FK_ArmtekOrderItems_ArmtekCartItems",
 1199        [("ArmtekOrderItems", "ArmtekOrderId")] = "IX_ArmtekOrderItems_Order",
 1200        [("ArmtekOrderItems", "StudioOrderId")] = "FK_ArmtekOrderItems_Orders_StudioOrderId",
 1201        [("ArmtekOrders", "ProviderKey,Vbeln")] = "UX_ArmtekOrders_Provider_Vbeln",
 1202        [("AssemblyListRunItems", "RunId")] = "idx_alri_run",
 1203        [("BackgroundJobSchedules", "OzonClientId")] = "fk_schedule_ozon_client",
 1204        [("BackgroundOrderMatches", "ExecutionId")] = "idx_execution",
 1205        [("BackgroundOrderMatches", "OrderId")] = "idx_order",
 1206        [("BackgroundOrderMatches", "ReportLoadId")] = "idx_report_load",
 1207        [("BackgroundOrderMatches", "StepId")] = "fk_match_step",
 1208        [("BackgroundReportLoads", "ExecutionId")] = "idx_execution",
 1209        [("BackgroundReportLoads", "OzonClientId")] = "idx_ozon_client",
 1210        [("BackgroundReportLoads", "StepId")] = "fk_report_step",
 1211        [("BackgroundTransactionResults", "ExecutionId")] = "idx_execution",
 1212        [("BackgroundTransactionResults", "OrderId")] = "idx_order",
 1213        [("BackgroundTransactionResults", "OrderMatchId")] = "idx_order_match",
 1214        [("BackgroundTransactionResults", "StepId")] = "fk_result_step",
 1215        [("BackgroundTransactionResults", "TransactionId")] = "idx_transaction",
 1216        [("BergCartItems", "ActiveStudioOrderId")] = "UX_BergCartItems_ActiveStudioOrderId",
 1217        [("BergCartItems", "CreatedAtUtc")] = "IX_BergCartItems_Created",
 1218        [("BergCartItems", "StudioOrderId,Status")] = "IX_BergCartItems_Order_Status",
 1219        [("BergDeliveryAddressMappings", "ShipmentWarehouseId")] = "UX_BergDeliveryAddressMappings_Warehouse",
 1220        [("BergOrderItems", "BergCartItemId")] = "IX_BergOrderItems_CartItem",
 1221        [("BergOrderItems", "BergOrderId")] = "IX_BergOrderItems_Order",
 1222        [("BergOrderItems", "StudioOrderId")] = "IX_BergOrderItems_StudioOrder",
 1223        [("BergOrders", "ExternalOrderId")] = "UX_BergOrders_ExternalOrderId",
 1224        [("BergOrders", "Reference")] = "UX_BergOrders_Reference",
 1225        [("ClientStatuses", "StatusColorId")] = "FK_clientstatuses_StatusColors_StatusColorId",
 1226        [("Deliveries", "ProviderId")] = "ProviderId",
 1227        [("EmailMessageInboxes", "MessageExternalId")] = "UX_EmailMessageInboxes_MessageExternalId",
 1228        [("FavoritPartsCartItems", "ProviderKey,ActiveStudioOrderId")] = "UX_FavoritPartsCartItems_Provider_ActiveOrder"
 1229        [("FavoritPartsCartItems", "ProviderKey,CreatedAtUtc")] = "IX_FavoritPartsCartItems_Provider_Created",
 1230        [("FavoritPartsCartItems", "ProviderKey,StudioOrderId,Status")] = "IX_FavoritPartsCartItems_Provider_Order_Statu
 1231        [("FavoritPartsCartItems", "StudioOrderId")] = "FK_FavoritPartsCartItems_Orders_StudioOrderId",
 1232        [("FavoritPartsOrderItems", "FavoritPartsCartItemId")] = "FK_FavoritPartsOrderItems_FavoritPartsCartItems",
 1233        [("FavoritPartsOrderItems", "FavoritPartsOrderId")] = "IX_FavoritPartsOrderItems_Order",
 1234        [("FavoritPartsOrderItems", "StudioOrderId")] = "FK_FavoritPartsOrderItems_Orders_StudioOrderId",
 1235        [("FavoritPartsOrders", "ProviderKey,ExternalOrderId")] = "UX_FavoritPartsOrders_Provider_ExternalOrderId",
 1236        [("FidSupplierLinks", "FidSupplierName")] = "UX_FidSupplierLinks_FidSupplierName",
 1237        [("ForumAutoCartItems", "ProviderKey,ActiveStudioOrderId")] = "UX_ForumAutoCartItems_Provider_ActiveOrder",
 1238        [("ForumAutoCartItems", "ProviderKey,CreatedAtUtc")] = "IX_ForumAutoCartItems_Provider_Created",
 1239        [("ForumAutoCartItems", "ProviderKey,StudioOrderId,Status")] = "IX_ForumAutoCartItems_Provider_Order_Status",
 1240        [("ForumAutoCartItems", "StudioOrderId")] = "FK_ForumAutoCartItems_Orders_StudioOrderId",
 1241        [("ForumAutoOrderItems", "ForumAutoCartItemId")] = "FK_ForumAutoOrderItems_ForumAutoCartItems",
 1242        [("ForumAutoOrderItems", "ForumAutoOrderId")] = "IX_ForumAutoOrderItems_Order",
 1243        [("ForumAutoOrderItems", "StudioOrderId")] = "FK_ForumAutoOrderItems_Orders_StudioOrderId",
 1244        [("ForumAutoOrders", "ProviderKey,Did")] = "UX_ForumAutoOrders_Provider_Did",
 1245        [("ForumAutoOrders", "ProviderKey,Eoid")] = "IX_ForumAutoOrders_Provider_Eoid",
 1246        [("InterpartsWarehouseLogins", "WarehouseId")] = "UX_InterpartsWarehouseLogins_WarehouseId",
 1247        [("ItemStatuses", "StatusColorId")] = "FK_ItemStatuses_StatusColors",
 1248        [("MikadoCartItems", "ActiveStudioOrderId")] = "UX_MikadoCartItems_ActiveStudioOrderId",
 1249        [("MikadoCartItems", "StudioOrderId")] = "FK_MikadoCartItems_Orders",
 1250        [("MlAutoDeliveryAddressMappings", "ShipmentWarehouseId")] = "UX_MlAutoDeliveryAddressMappings_Warehouse",
 1251        [("MlAutoOrderItems", "MlAutoOrderId")] = "IX_MlAutoOrderItems_Order",
 1252        [("MlAutoRuDeliveryAddressMappings", "ShipmentWarehouseId")] = "UX_MlAutoRuDeliveryAddressMappings_Warehouse",
 1253        [("MlAutoRuOrderItems", "MlAutoRuOrderId")] = "IX_MlAutoRuOrderItems_Order",
 1254        [("MoskvorechieIstraCartItems", "ActiveStudioOrderId")] = "UX_MoskvorechieIstraCartItems_ActiveStudioOrderId",
 1255        [("MoskvorechieIstraCartItems", "StudioOrderId")] = "FK_MoskvorechieIstraCartItems_StudioOrder",
 1256        [("MoskvorechieIstraOrderItems", "MoskvorechieIstraOrderId,StudioOrderId,Article")] = "UX_MoskvorechieIstraOrder
 1257        [("MoskvorechieIstraOrderItems", "StudioOrderId")] = "FK_MoskvorechieIstraOrderItems_StudioOrder",
 1258        [("MoskvorechieIstraOrders", "ExternalOrderNumber")] = "UX_MoskvorechieIstraOrders_ExternalOrderNumber",
 1259        [("MotexCartItems", "ActiveStudioOrderId")] = "UX_MotexCartItems_ActiveStudioOrderId",
 1260        [("MotexCartItems", "StudioOrderId,Status")] = "IX_MotexCartItems_StudioOrder_Status",
 1261        [("MotexOrderItems", "MotexOrderId,StudioOrderId,Code")] = "UX_MotexOrderItems_Order_StudioOrder_Code",
 1262        [("MotexOrders", "ClientOrderNum")] = "UX_MotexOrders_ClientOrderNum",
 1263        [("OrderArchives", "OrderId")] = "UK_OrderArchives_OrderId",
 1264        [("OrderCalculationSnapshots", "OrderId,OperationId")] = "UX_OrderCalculationSnapshots_OrderId_OperationId",
 1265        [("OrderCarts", "CartStatusId")] = "ix_ordercarts_cartstatusid",
 1266        [("OrderControlRules", "Code")] = "UX_OrderControlRules_Code",
 1267        [("OrderControlRules", "IsActive,RuleType")] = "IX_OrderControlRules_Active_Type",
 1268        [("OrderControlRules", "TargetAppRoleId")] = "FK_OrderControlRules_AppRoles_TargetAppRoleId",
 1269        [("OrderControlTasks", "DeduplicationKey")] = "UX_OrderControlTasks_DeduplicationKey",
 1270        [("OrderControlTasks", "OrderId")] = "FK_OrderControlTasks_Orders_OrderId",
 1271        [("OrderControlTasks", "RuleId")] = "FK_OrderControlTasks_OrderControlRules_RuleId",
 1272        [("OrderHistoryOneCPostingLinks", "PostingId,OrderHistoryId")] = "UX_OrderHistoryOneCPostingLinks",
 1273        [("OrderItems", "ItemStatusId")] = "ix_orderitems_itemstatusid",
 1274        [("OrderItems", "OrderCartId")] = "ix_orderitems_ordercartid",
 1275        [("OrderSupplierStateHistory", "OrderSupplierStateId")] = "IX_OrderSupplierStateHistory_StateId",
 1276        [("OrderSupplierStates", "ProviderKey,ActiveOrderId")] = "UX_OrderSupplierStates_Provider_ActiveOrderId",
 1277        [("OrderSupplierStates", "ProviderKey,Stage,LastSyncedAt")] = "IX_OrderSupplierStates_Provider_Stage_LastSyncedA
 1278        [("OzonChatOrders", "ClientId")] = "FK_OzonChatOrders_OzonClient",
 1279        [("OzonChatOrders", "OrderId")] = "FK_OzonChatOrders_Order",
 1280        [("OzonChatResponseStatistics", "OrderId")] = "FK_OzonChatResponseStatistics_Order",
 1281        [("OzonPostingCancellations", "EventFingerprint")] = "UX_OzonPostingCancellations_EventFingerprint",
 1282        [("OzonPostingCancellations", "OzonClientId,PostingNumber")] = "IX_OzonPostingCancellations_Client_Posting",
 1283        [("ProfitLigaCartItems", "ActiveStudioOrderId")] = "UX_ProfitLigaCartItems_ActiveOrder",
 1284        [("ProfitLigaCartItems", "CreatedAtUtc")] = "IX_ProfitLigaCartItems_Created",
 1285        [("ProfitLigaCartItems", "StudioOrderId,Status")] = "IX_ProfitLigaCartItems_Order_Status",
 1286        [("ProfitLigaOrderItems", "ProfitLigaCartItemId")] = "FK_ProfitLigaOrderItems_ProfitLigaCartItems",
 1287        [("ProfitLigaOrderItems", "ProfitLigaOrderId")] = "IX_ProfitLigaOrderItems_Order",
 1288        [("ProfitLigaOrderItems", "StudioOrderId")] = "FK_ProfitLigaOrderItems_Orders_StudioOrderId",
 1289        [("ProfitLigaOrders", "OrderNumber")] = "UX_ProfitLigaOrders_Number",
 1290        [("ProfitLigaPointMappings", "ShipmentWarehouseId")] = "UX_ProfitLigaPointMappings_Warehouse",
 1291        [("ShateMCartItems", "ActiveStudioOrderId")] = "UX_ShateMCartItems_ActiveStudioOrderId",
 1292        [("ShateMCartItems", "StudioOrderId")] = "FK_ShateMCartItems_Orders_StudioOrderId",
 1293        [("ShateMOrderItems", "ShateMOrderId,StudioOrderId,PriceId")] = "UX_ShateMOrderItems_Order_StudioOrder_Price",
 1294        [("ShateMOrders", "ExternalOrderId")] = "UX_ShateMOrders_ExternalOrderId",
 1295        [("StatusTransitionHistory", "EntityType,EntityId")] = "IX_Entity",
 1296        [("StatusTransitionHistory", "FromStatusId")] = "IX_FromStatusId",
 1297        [("StatusTransitionHistory", "PerformedAt")] = "IX_PerformedAt",
 1298        [("StatusTransitionHistory", "StatusTransitionId")] = "IX_StatusTransitionId",
 1299        [("StatusTransitionHistory", "ToStatusId")] = "IX_ToStatusId",
 1300        [("StatusTransitions", "FromStatusId,ToStatusId,TransactionType")] = "UQ_StatusTransitions_UniqueTransition",
 1301        [("SupplierQuotationRequests", "OrderId,RequestedAtUtc")] = "IX_SupplierQuotationRequests_Order_RequestedAtUtc",
 1302        [("SupplierQuotationRequests", "ProductId,RequestedAtUtc")] = "IX_SupplierQuotationRequests_Product_RequestedAtU
 1303        [("SupplierStatuses", "ProviderKey,StatusKind,ExternalCode")] = "IX_SupplierStatuses_Provider_Kind_ExternalCode"
 1304        [("SupplierStatuses", "ProviderKey,StatusKind,Name")] = "UX_SupplierStatuses_Provider_Kind_Name",
 1305        [("TransactionTimeTrackingResults", "TrackingKey")] = "UX_TransactionTimeTrackingResults_TrackingKey",
 1306        [("TransactionTimeTrackingResults", "TrackingSessionId")] = "FK_TransactionTimeTrackingResults_SessionId",
 1307        [("TransactionTimeTrackingSessions", "IsCompleted,IsAbandoned,StartedAtUtc")] = "IX_TransactionTimeTrackingSessi
 1308        [("TransactionTimeTrackingSessions", "TrackingKey")] = "UX_TransactionTimeTrackingSessions_TrackingKey",
 1309        [("UserDataScopes", "DataScopeId")] = "FK_UserDataScopes_DataScopes_DataScopeId",
 1310        [("WarehouseMapping", "OzonClientId")] = "FK_WarehouseMapping_OzonClient",
 1311        [("WarehouseStockSnapshot", "RowKey")] = "UX_WarehouseStockSnapshot_RowKey",
 1312        [("WarehouseStockSnapshot", "SyncRunId")] = "FK_WarehouseStockSnapshot_SyncRun",
 1313        [("orders_history", "OrderId")] = "IX_OrdersHistory_OrderId",
 1314    };
 315
 316    /// <summary>
 317    /// Имена внешних ключей, которые на проде отличаются от выводимых EF.
 318    /// Ключ — таблица и колонки FK через запятую. Как и с индексами, нужны, чтобы
 319    /// миграции обращались к ограничениям по их фактическим именам в боевой БД.
 320    /// </summary>
 1321    internal static readonly Dictionary<(string Table, string Columns), string> ForeignKeyNames = new()
 1322    {
 1323        [("ApiSupplierWarehouses", "WarehouseId")] = "FK_ApiSupplierWarehouses_Warehouses",
 1324        [("AppStatuses", "StatusColorId")] = "FK_AppStatuses_StatusColors",
 1325        [("ArmtekOrderItems", "ArmtekCartItemId")] = "FK_ArmtekOrderItems_ArmtekCartItems",
 1326        [("ArmtekOrderItems", "ArmtekOrderId")] = "FK_ArmtekOrderItems_ArmtekOrders",
 1327        [("BackgroundJobExecutions", "ScheduleId")] = "fk_execution_schedule",
 1328        [("BackgroundJobSchedules", "OzonClientId")] = "fk_schedule_ozon_client",
 1329        [("BackgroundJobSteps", "ExecutionId")] = "fk_step_execution",
 1330        [("BackgroundOrderMatches", "ExecutionId")] = "fk_match_execution",
 1331        [("BackgroundOrderMatches", "OrderId")] = "fk_match_order",
 1332        [("BackgroundOrderMatches", "ReportLoadId")] = "fk_match_report",
 1333        [("BackgroundOrderMatches", "StepId")] = "fk_match_step",
 1334        [("BackgroundReportLoads", "ExecutionId")] = "fk_report_execution",
 1335        [("BackgroundReportLoads", "OzonClientId")] = "fk_report_ozon_client",
 1336        [("BackgroundReportLoads", "StepId")] = "fk_report_step",
 1337        [("BackgroundTransactionResults", "ExecutionId")] = "fk_result_execution",
 1338        [("BackgroundTransactionResults", "OrderId")] = "fk_result_order",
 1339        [("BackgroundTransactionResults", "OrderMatchId")] = "fk_result_match",
 1340        [("BackgroundTransactionResults", "StepId")] = "fk_result_step",
 1341        [("BackgroundTransactionResults", "TransactionId")] = "fk_result_transaction",
 1342        [("BergDeliveryAddressMappings", "ShipmentWarehouseId")] = "FK_BergDeliveryAddressMappings_Warehouses",
 1343        [("BergOrderItems", "BergCartItemId")] = "FK_BergOrderItems_BergCartItems",
 1344        [("BergOrderItems", "BergOrderId")] = "FK_BergOrderItems_BergOrders",
 1345        [("BitrixStockArchiveItems", "ExecutionId")] = "fk_archive_item_execution",
 1346        [("BitrixStockAutoScheduleRuns", "ScheduleId")] = "fk_auto_run_schedule",
 1347        [("BitrixStockClearItems", "ExecutionId")] = "fk_clear_item_execution",
 1348        [("BitrixStockPushItems", "ExecutionId")] = "fk_push_item_execution",
 1349        [("BitrixStockUnarchiveItems", "ExecutionId")] = "fk_unarchive_item_execution",
 1350        [("ClientStatuses", "StatusColorId")] = "FK_clientstatuses_StatusColors_StatusColorId",
 1351        [("DataScopeSetItems", "DataScopeSetId")] = "FK_DataScopeSetItems_DataScopeSets",
 1352        [("Deliveries", "ProviderId")] = "Deliveries_ibfk_1",
 1353        [("EmailAttachmentInboxes", "EmailMessageInboxId")] = "FK_EmailAttachmentInboxes_EmailMessageInboxes",
 1354        [("EmailAttachmentProcessingResults", "EmailAttachmentInboxId")] = "FK_EmailAttachmentProcessingResults_EmailAtt
 1355        [("EmailAttachmentProcessingResults", "EmailMessageInboxId")] = "FK_EmailAttachmentProcessingResults_EmailMessag
 1356        [("EmailAttachmentProcessingResults", "EmailRoutingRuleId")] = "FK_EmailAttachmentProcessingResults_EmailRouting
 1357        [("ExcludedArticles", "OzonClientId")] = "FK_ExcludedArticles_OzonClients",
 1358        [("FavoritPartsOrderItems", "FavoritPartsCartItemId")] = "FK_FavoritPartsOrderItems_FavoritPartsCartItems",
 1359        [("FavoritPartsOrderItems", "FavoritPartsOrderId")] = "FK_FavoritPartsOrderItems_FavoritPartsOrders",
 1360        [("FidStatItems", "SnapshotId")] = "FK_FidStatItems_Snapshot",
 1361        [("ForumAutoOrderItems", "ForumAutoCartItemId")] = "FK_ForumAutoOrderItems_ForumAutoCartItems",
 1362        [("ForumAutoOrderItems", "ForumAutoOrderId")] = "FK_ForumAutoOrderItems_ForumAutoOrders",
 1363        [("ItemStatuses", "StatusColorId")] = "FK_ItemStatuses_StatusColors",
 1364        [("MikadoCartItems", "StudioOrderId")] = "FK_MikadoCartItems_Orders",
 1365        [("MlAutoCartItems", "StudioOrderId")] = "FK_MlAutoCartItems_Orders",
 1366        [("MlAutoDeliveryAddressMappings", "ShipmentWarehouseId")] = "FK_MlAutoDeliveryAddressMappings_Warehouses",
 1367        [("MlAutoOrderItems", "MlAutoOrderId")] = "FK_MlAutoOrderItems_Orders",
 1368        [("MlAutoRuCartItems", "StudioOrderId")] = "FK_MlAutoRuCartItems_Orders",
 1369        [("MlAutoRuDeliveryAddressMappings", "ShipmentWarehouseId")] = "FK_MlAutoRuDeliveryAddressMappings_Warehouses",
 1370        [("MlAutoRuOrderItems", "MlAutoRuOrderId")] = "FK_MlAutoRuOrderItems_Orders",
 1371        [("MoskvorechieIstraCartItems", "StudioOrderId")] = "FK_MoskvorechieIstraCartItems_StudioOrder",
 1372        [("MoskvorechieIstraOrderItems", "MoskvorechieIstraOrderId")] = "FK_MoskvorechieIstraOrderItems_Order",
 1373        [("MoskvorechieIstraOrderItems", "StudioOrderId")] = "FK_MoskvorechieIstraOrderItems_StudioOrder",
 1374        [("OrderCarts", "CartStatusId")] = "ordercarts_ibfk_1",
 1375        [("OrderExtraExpenses", "ExpenseTypeId")] = "FK_OrderExtraExpenses_ExpenseType",
 1376        [("OrderExtraExpenses", "OrderId")] = "FK_OrderExtraExpenses_Order",
 1377        [("OrderHistoryOneCPostingLinks", "OrderHistoryId")] = "FK_OrderHistoryOneCPostingLinks_History",
 1378        [("OrderHistoryOneCPostingLinks", "PostingId")] = "FK_OrderHistoryOneCPostingLinks_Posting",
 1379        [("OrderItems", "ItemStatusId")] = "orderitems_ibfk_1",
 1380        [("OrderItems", "OrderCartId")] = "orderitems_ibfk_2",
 1381        [("OrderOzonPromotionConditions", "SnapshotId")] = "FK_OzonPromoCondition_Snapshot",
 1382        [("OrderReturns", "OrderId")] = "FK_OrderReturns_Orders",
 1383        [("OrderReturns", "OzonClientId")] = "FK_OrderReturns_OzonClients",
 1384        [("OrderSupplierStateHistory", "OrderHistoryId")] = "FK_OrderSupplierStateHistory_OrderHistory_OrderHistoryId",
 1385        [("OrderSupplierStateHistory", "OrderSupplierStateId")] = "FK_OrderSupplierStateHistory_States_OrderSupplierStat
 1386        [("OzonChatOrders", "ClientId")] = "FK_OzonChatOrders_OzonClient",
 1387        [("OzonChatOrders", "OrderId")] = "FK_OzonChatOrders_Order",
 1388        [("OzonChatResponseStatistics", "ClientId")] = "FK_OzonChatResponseStatistics_OzonClient",
 1389        [("OzonChatResponseStatistics", "OrderId")] = "FK_OzonChatResponseStatistics_Order",
 1390        [("OzonPostingCancellationOrders", "CancellationId")] = "FK_OzonPostingCancellationOrders_Cancellations_Cancella
 1391        [("ProfitLigaOrderItems", "ProfitLigaCartItemId")] = "FK_ProfitLigaOrderItems_ProfitLigaCartItems",
 1392        [("ProfitLigaOrderItems", "ProfitLigaOrderId")] = "FK_ProfitLigaOrderItems_ProfitLigaOrders",
 1393        [("ProfitLigaPointMappings", "ShipmentWarehouseId")] = "FK_ProfitLigaPointMappings_Warehouses",
 1394        [("ShateMOrderItems", "ShateMOrderId")] = "FK_ShateMOrderItems_Orders",
 1395        [("StatusTransitionHistory", "FromStatusId")] = "StatusTransitionHistory_ibfk_2",
 1396        [("StatusTransitionHistory", "StatusTransitionId")] = "StatusTransitionHistory_ibfk_1",
 1397        [("StatusTransitionHistory", "ToStatusId")] = "StatusTransitionHistory_ibfk_3",
 1398        [("StatusTransitions", "FromStatusId")] = "StatusTransitions_ibfk_1",
 1399        [("StatusTransitions", "ToStatusId")] = "StatusTransitions_ibfk_2",
 1400        [("SupplierQuotationOffers", "RequestId")] = "FK_SupplierQuotationOffers_Requests",
 1401        [("SupplierQuotationRequests", "OrderId")] = "FK_SupplierQuotationRequests_Orders",
 1402        [("SupplierQuotationRequests", "ProductId")] = "FK_SupplierQuotationRequests_Products",
 1403        [("SupplierQuotationRequests", "SupplierId")] = "FK_SupplierQuotationRequests_Suppliers",
 1404        [("TransactionTimeTrackingResults", "TrackingSessionId")] = "FK_TransactionTimeTrackingResults_SessionId",
 1405        [("WarehouseMapping", "OzonClientId")] = "FK_WarehouseMapping_OzonClient",
 1406        [("WarehouseStockSnapshot", "SyncRunId")] = "FK_WarehouseStockSnapshot_SyncRun",
 1407        [("order_extensions", "OrderId")] = "FK_OrderExtensions_Orders",
 1408        [("orders_history", "OrderId")] = "FK_OrdersHistory_Orders_OrderId",
 1409    };
 410
 411    /// <summary>
 412    /// Индексы, которые EF выводит уникальными из связи «один к одному», тогда как
 413    /// в боевой БД они неуникальны.
 414    ///
 415    /// OrderPrices.OrderId: связь Order ↔ OrderPrice описана как 1:1, но в проде
 416    /// 16 234 заказа имеют несколько строк цен. Уникальный индекс там создать нельзя,
 417    /// и раньше именно поэтому схему из модели невозможно было развернуть.
 418    /// Связь в C# остаётся 1:1 — уникальность на уровне БД просто не заявляется.
 419    /// </summary>
 1420    internal static readonly HashSet<(string Table, string Columns)> NonUniqueIndexes = new()
 1421    {
 1422        ("OrderPrices", "OrderId"),
 1423        ("PriceIndexes", "OrderPriceId"),
 1424    };
 425
 426    /// <summary>Индексы, заведённые на проде скриптами в обход модели.</summary>
 1427    internal static readonly LegacyIndex[] LegacyIndexes =
 1428    {
 1429        new("AssemblyListAccessKeys", "idx_alak_active", false, "IsActive"),
 1430        new("AssemblyListAccessKeys", "idx_alak_key", true, "AccessKey"),
 1431        new("AssemblyListRuns", "idx_alr_client", false, "OzonClientId"),
 1432        new("AssemblyListRuns", "idx_alr_started", false, "StartedAtUtc"),
 1433        new("BackgroundJobExecutions", "idx_job_type_started", false, "JobType", "StartedAt"),
 1434        new("BackgroundJobExecutions", "idx_schedule_started", false, "ScheduleId", "StartedAt"),
 1435        new("BackgroundJobExecutions", "idx_status_started", false, "Status", "StartedAt"),
 1436        new("BackgroundJobSchedules", "idx_job_type_active", false, "JobType", "IsActive"),
 1437        new("BackgroundJobSchedules", "idx_next_run", false, "NextRunTime", "IsActive"),
 1438        new("BackgroundJobSteps", "idx_execution_step", false, "ExecutionId", "StepNumber"),
 1439        new("BackgroundJobSteps", "idx_status", false, "Status"),
 1440        new("BackgroundOrderMatches", "idx_eligibility", false, "IsMatchedInDb", "IsEligibleForProcessing"),
 1441        new("BackgroundOrderMatches", "idx_order_number_article", false, "OrderNumber", "Article"),
 1442        new("BackgroundReportLoads", "idx_report_code", false, "ReportCode"),
 1443        new("BackgroundTransactionResults", "idx_status", false, "Status"),
 1444        new("BitrixStockArchiveExecutions", "idx_archive_started_at", false, "StartedAt"),
 1445        new("BitrixStockArchiveExecutions", "idx_archive_status_started", false, "Status", "StartedAt"),
 1446        new("BitrixStockArchiveExecutions", "uk_archive_job_id", true, "JobId"),
 1447        new("BitrixStockArchiveItems", "idx_archive_execution_sort", false, "ExecutionId", "SortOrder"),
 1448        new("BitrixStockArchiveItems", "idx_archive_execution_status", false, "ExecutionId", "Status"),
 1449        new("BitrixStockAutoScheduleRuns", "idx_run_schedule_started", false, "ScheduleId", "StartedAt"),
 1450        new("BitrixStockAutoSchedules", "idx_auto_active", false, "IsActive"),
 1451        new("BitrixStockAutoSchedules", "idx_auto_next_run", false, "NextRunAt"),
 1452        new("BitrixStockClearExecutions", "idx_started_at", false, "StartedAt"),
 1453        new("BitrixStockClearExecutions", "idx_status_started", false, "Status", "StartedAt"),
 1454        new("BitrixStockClearExecutions", "uk_job_id", true, "JobId"),
 1455        new("BitrixStockClearItems", "idx_execution_sort", false, "ExecutionId", "SortOrder"),
 1456        new("BitrixStockClearItems", "idx_execution_status", false, "ExecutionId", "Status"),
 1457        new("BitrixStockPushExecutions", "idx_push_started_at", false, "StartedAt"),
 1458        new("BitrixStockPushExecutions", "idx_push_status_started", false, "Status", "StartedAt"),
 1459        new("BitrixStockPushExecutions", "uk_push_job_id", true, "JobId"),
 1460        new("BitrixStockPushItems", "idx_push_execution_sort", false, "ExecutionId", "SortOrder"),
 1461        new("BitrixStockPushItems", "idx_push_execution_status", false, "ExecutionId", "Status"),
 1462        new("BitrixStockSavedFilter", "IX_BitrixStockSavedFilter_CreatedBy", false, "CreatedBy"),
 1463        new("BitrixStockSavedFilter", "IX_BitrixStockSavedFilter_Name", false, "Name"),
 1464        new("BitrixStockUnarchiveExecutions", "idx_unarchive_started_at", false, "StartedAt"),
 1465        new("BitrixStockUnarchiveExecutions", "idx_unarchive_status_started", false, "Status", "StartedAt"),
 1466        new("BitrixStockUnarchiveExecutions", "uk_unarchive_job_id", true, "JobId"),
 1467        new("BitrixStockUnarchiveItems", "idx_unarchive_execution_sort", false, "ExecutionId", "SortOrder"),
 1468        new("BitrixStockUnarchiveItems", "idx_unarchive_execution_status", false, "ExecutionId", "Status"),
 1469        new("ClientStatuses", "IX_ClientStatuses_ClientTypeId", false, "ClientTypeId"),
 1470        new("ClientStatuses", "IX_ClientStatuses_Name", true, "Name"),
 1471        new("DeliveryProviders", "Name", true, "Name"),
 1472        new("ExcelMappings", "IX_ExcelMappings_CreatedBy", false, "CreatedBy"),
 1473        new("ExcelMappings", "IX_ExcelMappings_CreatedDate", false, "CreatedDate"),
 1474        new("ExcelMappings", "IX_ExcelMappings_Name", false, "Name"),
 1475        new("FileUploadRecords", "IX_FileUploadRecords_FileName", true, "FileName"),
 1476        new("InterpartsSutCurrencies", "IX_InterpartsSutCurrencies_Name", false, "Name"),
 1477        new("Manufacturers", "IX_manufacturers_EtProduserId", false, "EtProduserId"),
 1478        new("MlAutoCartItems", "IX_MlAutoCartItems_Order_Status", false, "StudioOrderId", "Status"),
 1479        new("MlAutoCartItems", "UX_MlAutoCartItems_ActiveOrder", true, "ActiveStudioOrderId"),
 1480        new("MlAutoOrders", "UX_MlAutoOrders_Number", true, "OrderNumber"),
 1481        new("MlAutoRuCartItems", "IX_MlAutoRuCartItems_Order_Status", false, "StudioOrderId", "Status"),
 1482        new("MlAutoRuCartItems", "UX_MlAutoRuCartItems_ActiveOrder", true, "ActiveStudioOrderId"),
 1483        new("MlAutoRuOrders", "UX_MlAutoRuOrders_Number", true, "OrderNumber"),
 1484        new("MotexOrderItems", "IX_MotexOrderItems_MotexOrderId", false, "MotexOrderId"),
 1485        new("OrderArchives", "IX_OrderArchives_ArchivedBy", false, "ArchivedBy"),
 1486        new("OrderHistoryOneCPostings", "IX_OrderHistoryOneCPostings_HistoryBatchKey", false, "HistoryBatchKey"),
 1487        new("OrderHistoryOneCPostings", "IX_OrderHistoryOneCPostings_OrderId", false, "OrderId"),
 1488        new("OrderHistoryOneCPostings", "IX_OrderHistoryOneCPostings_OrderId_PostedAt", false, "OrderId", "PostedAt"),
 1489        new("OrderHistoryOneCPostings", "IX_OrderHistoryOneCPostings_TransactionId", false, "TransactionId"),
 1490        new("Orders", "IX_Orders_AppStatusId_LastStatusChangeDate", false, "AppStatusId", "LastStatusChangeDate"),
 1491        new("Orders", "IX_Orders_CartId", false, "CartItemId"),
 1492        new("Orders", "IX_Orders_LastStatusChangeDate", false, "LastStatusChangeDate"),
 1493        new("Orders", "IX_Orders_ProcessingDate", false, "ProcessingDate"),
 1494        new("Orders", "IX_Orders_ProductKey", false, "ProductKey"),
 1495        new("Orders", "IX_Orders_ShipmentNumber", false, "ShipmentNumber"),
 1496        new("OzonChatResponseStatistics", "IX_OzonChatResponseStatistics_ChatId", false, "ChatId"),
 1497        new("OzonChatResponseStatistics", "IX_OzonChatResponseStatistics_ClientId_Date", false, "ClientId", "ClientMessa
 1498        new("OzonChatResponseTimes", "IX_OzonChatResponseTimes_ChatId", false, "ChatId"),
 1499        new("OzonChatResponseTimes", "IX_OzonChatResponseTimes_UserName", false, "UserName"),
 1500        new("OzonPushNotifications", "IX_OzonPushNotifications_MessageType", false, "MessageType"),
 1501        new("OzonPushNotifications", "IX_OzonPushNotifications_PostingNumber", false, "PostingNumber"),
 1502        new("OzonPushNotifications", "IX_OzonPushNotifications_ReceivedAt", false, "ReceivedAt"),
 1503        new("OzonPushNotifications", "IX_OzonPushNotifications_SellerId", false, "SellerId"),
 1504        new("PriceHistories", "IX_PriceHistories_Article", false, "Article"),
 1505        new("PriceHistories", "IX_PriceHistories_Article_CreateDateTime", false, "Article", "CreateDateTime"),
 1506        new("PriceHistories", "IX_PriceHistories_BitrixId", false, "BitrixId"),
 1507        new("PriceHistories", "IX_PriceHistories_CreateDateTime", false, "CreateDateTime"),
 1508        new("Shipments", "UX_Shipments_ShipmentNumber", true, "ShipmentNumber"),
 1509        new("StatusTransitionHistory", "IX_TransactionType", false, "TransactionType"),
 1510        new("StatusTransitions", "IX_StatusTransitions_DisplayOrder", false, "DisplayOrder"),
 1511        new("StatusTransitions", "IX_StatusTransitions_FromStatusId", false, "FromStatusId"),
 1512        new("StatusTransitions", "IX_StatusTransitions_IsActive", false, "IsActive"),
 1513        new("TransactionTimeTrackingResults", "IX_TransactionTimeTrackingResults_AccessProfileId", false, "AccessProfile
 1514        new("TransactionTimeTrackingSessions", "IX_TransactionTimeTrackingSessions_AccessProfileId", false, "AccessProfi
 1515        new("order_extensions", "IX_OrderExtensions_OrderId", false, "OrderId"),
 1516        new("order_extensions", "UX_OrderExtensions_OrderId_Key", true, "OrderId", "Key"),
 1517        new("orders_history", "IX_OrdersHistory_ChangedAt", false, "ChangedAt"),
 1518        new("orders_history", "IX_orders_history_OperationId", false, "OperationId"),
 1519    };
 520
 521    /// <summary>
 522    /// Вызывается последней строкой OnModelCreating — после того, как все сущности
 523    /// сконфигурированы и таблицы получили финальные имена.
 524    /// </summary>
 525    public static void Apply(ModelBuilder modelBuilder)
 526    {
 4527        var entityTypesByTable = new Dictionary<string, List<IMutableEntityType>>();
 528
 1116529        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
 530        {
 554531            string? table = entityType.GetTableName();
 554532            if (table is null)
 533            {
 534                continue;
 535            }
 536
 554537            if (!entityTypesByTable.TryGetValue(table, out var bucket))
 538            {
 554539                bucket = new List<IMutableEntityType>();
 554540                entityTypesByTable[table] = bucket;
 541            }
 542
 554543            bucket.Add(entityType);
 544
 13648545            foreach (var property in entityType.GetProperties())
 546            {
 6270547                var key = (table, property.GetColumnName());
 548
 6270549                if (ColumnTypes.TryGetValue(key, out string? columnType))
 550                {
 420551                    property.SetColumnType(columnType);
 552                }
 553
 6270554                if (RequiredColumns.Contains(key))
 555                {
 9556                    property.IsNullable = false;
 557                }
 558            }
 559        }
 560
 728561        foreach (var legacyIndex in LegacyIndexes)
 562        {
 360563            if (!entityTypesByTable.TryGetValue(legacyIndex.Table, out var candidates))
 564            {
 565                continue;
 566            }
 567
 816568            foreach (var entityType in candidates)
 569            {
 270570                var properties = ResolveProperties(entityType, legacyIndex.Columns);
 270571                if (properties is null || entityType.FindIndex(properties) is not null)
 572                {
 573                    continue;
 574                }
 575
 264576                var index = entityType.AddIndex(properties);
 264577                index.IsUnique = legacyIndex.IsUnique;
 264578                index.SetDatabaseName(legacyIndex.Name);
 264579                break;
 580            }
 581        }
 582
 1116583        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
 584        {
 554585            string? table = entityType.GetTableName();
 554586            if (table is null)
 587            {
 588                continue;
 589            }
 590
 3444591            foreach (var index in entityType.GetDeclaredIndexes())
 592            {
 2720593                string columns = string.Join(',', index.Properties.Select(p => p.GetColumnName()));
 594
 1168595                if (IndexNames.TryGetValue((table, columns), out string? name))
 596                {
 375597                    index.SetDatabaseName(name);
 598                }
 599
 1168600                if (NonUniqueIndexes.Contains((table, columns)))
 601                {
 6602                    index.IsUnique = false;
 603                }
 604            }
 605
 2136606            foreach (var foreignKey in entityType.GetDeclaredForeignKeys())
 607            {
 1028608                string columns = string.Join(',', foreignKey.Properties.Select(p => p.GetColumnName()));
 609
 514610                if (ForeignKeyNames.TryGetValue((table, columns), out string? name))
 611                {
 259612                    foreignKey.SetConstraintName(name);
 613                }
 614            }
 615        }
 4616    }
 617
 618    /// <summary>
 619    /// Возвращает свойства сущности по именам колонок или null, если сопоставить удалось не все.
 620    /// </summary>
 621    internal static List<IMutableProperty>? ResolveProperties(IMutableEntityType entityType, IReadOnlyList<string> colum
 622    {
 270623        var resolved = new List<IMutableProperty>(columns.Count);
 624
 1248625        foreach (string column in columns)
 626        {
 354627            var property = entityType.GetProperties()
 3246628                .FirstOrDefault(p => string.Equals(p.GetColumnName(), column, StringComparison.Ordinal));
 629
 354630            if (property is null)
 631            {
 0632                return null;
 633            }
 634
 354635            resolved.Add(property);
 636        }
 637
 270638        return resolved;
 0639    }
 640}