diff --git a/XLAB/PsvDataService.cs b/XLAB/PsvDataService.cs index dd3b267..004081d 100644 --- a/XLAB/PsvDataService.cs +++ b/XLAB/PsvDataService.cs @@ -259,7 +259,7 @@ SELECT @@ROWCOUNT;"; } } - public void DeleteSpnmtpItem(int id) + public SpnmtpDeleteResult DeleteSpnmtpItem(int id) { if (id <= 0) { @@ -275,21 +275,42 @@ SELECT @@ROWCOUNT;"; try { using (var connection = CreateConnection()) - using (var command = new SqlCommand(sql, connection)) { connection.Open(); - command.CommandTimeout = 60; - command.Parameters.Add("@Id", SqlDbType.Int).Value = id; - - if (Convert.ToInt32(command.ExecuteScalar()) == 0) + var blockers = LoadSpnmtpDeleteBlockers(connection, id); + if (blockers.Count > 0) { - throw new InvalidOperationException("Запись SPNMTP для удаления не найдена."); + return new SpnmtpDeleteResult + { + IsDeleted = false, + WarningMessage = CreateSpnmtpDeleteBlockedMessage(blockers) + }; } + + using (var command = new SqlCommand(sql, connection)) + { + command.CommandTimeout = 60; + command.Parameters.Add("@Id", SqlDbType.Int).Value = id; + + if (Convert.ToInt32(command.ExecuteScalar()) == 0) + { + throw new InvalidOperationException("Запись SPNMTP для удаления не найдена."); + } + } + + return new SpnmtpDeleteResult + { + IsDeleted = true + }; } } catch (SqlException ex) when (ex.Number == 547) { - throw new InvalidOperationException("Запись справочника используется в связанных данных и не может быть удалена.", ex); + return new SpnmtpDeleteResult + { + IsDeleted = false, + WarningMessage = CreateSpnmtpDeleteBlockedMessage(ex) + }; } } @@ -2337,6 +2358,69 @@ WHERE NMTP = @Name innerException); } + private static List LoadSpnmtpDeleteBlockers(SqlConnection connection, int id) + { + const string sql = @" +SELECT blocker.TableName, blocker.LinkCount +FROM +( + SELECT N'TIPS' AS TableName, COUNT(*) AS LinkCount + FROM dbo.TIPS + WHERE IDSPNMTP = @Id +) blocker +WHERE blocker.LinkCount > 0 +ORDER BY blocker.TableName;"; + + var blockers = new List(); + + using (var command = new SqlCommand(sql, connection)) + { + command.CommandTimeout = 60; + command.Parameters.Add("@Id", SqlDbType.Int).Value = id; + + using (var reader = command.ExecuteReader()) + { + while (reader.Read()) + { + blockers.Add(new DeleteBlockerInfo + { + TableName = GetString(reader, "TableName"), + RowCount = GetInt32(reader, "LinkCount") + }); + } + } + } + + return blockers; + } + + private static string CreateSpnmtpDeleteBlockedMessage(IEnumerable blockers) + { + var blockerList = blockers == null + ? new List() + : blockers.Where(delegate(DeleteBlockerInfo blocker) { return blocker != null; }).ToList(); + var details = blockerList.Count == 0 + ? "TIPS" + : string.Join(", ", blockerList.Select(delegate(DeleteBlockerInfo blocker) + { + return string.Format("{0}: {1}", blocker.TableName, blocker.RowCount); + })); + + return string.Format( + "Запись SPNMTP не может быть удалена, потому что на неё есть ссылки в таблицах: {0}. Подтверждённое ограничение БД: FK_TIPS_SPNMTP (dbo.TIPS.IDSPNMTP -> dbo.SPNMTP.IDSPNMTP).", + details); + } + + private static string CreateSpnmtpDeleteBlockedMessage(SqlException ex) + { + if (ex != null && ex.Message.IndexOf("FK_TIPS_SPNMTP", StringComparison.OrdinalIgnoreCase) >= 0) + { + return "Запись SPNMTP не может быть удалена, потому что на неё есть ссылки в таблице TIPS. Ограничение БД: FK_TIPS_SPNMTP (dbo.TIPS.IDSPNMTP -> dbo.SPNMTP.IDSPNMTP)."; + } + + return "Запись SPNMTP не может быть удалена из-за ограничения ссылочной целостности в БД."; + } + private static bool IsSpnmtpDuplicateNameViolation(SqlException ex) { return ex != null diff --git a/XLAB/PsvModels.cs b/XLAB/PsvModels.cs index 4b44a72..90800cd 100644 --- a/XLAB/PsvModels.cs +++ b/XLAB/PsvModels.cs @@ -353,6 +353,13 @@ namespace XLAB public string SpecialName { get; set; } } + internal sealed class SpnmtpDeleteResult + { + public bool IsDeleted { get; set; } + + public string WarningMessage { get; set; } + } + internal static class SpnmtpDirectoryRules { public const int NameMaxLength = 150; diff --git a/XLAB/SpnmtpDirectoryWindowViewModel.cs b/XLAB/SpnmtpDirectoryWindowViewModel.cs index 6dc4081..5ae5a00 100644 --- a/XLAB/SpnmtpDirectoryWindowViewModel.cs +++ b/XLAB/SpnmtpDirectoryWindowViewModel.cs @@ -132,7 +132,13 @@ namespace XLAB RunMutationOperation(async delegate { - await Task.Run(delegate { _service.DeleteSpnmtpItem(selectedItem.Id); }); + var deleteResult = await Task.Run(delegate { return _service.DeleteSpnmtpItem(selectedItem.Id); }); + if (!deleteResult.IsDeleted) + { + _dialogService.ShowWarning(deleteResult.WarningMessage); + return; + } + await RefreshCoreAsync(null); _dialogService.ShowInfo("Запись справочника удалена."); }); @@ -255,12 +261,28 @@ namespace XLAB private async void RunBusyOperation(Func operation) { - await ExecuteBusyOperationAsync(operation); + try + { + await ExecuteBusyOperationAsync(operation); + } + catch (Exception ex) + { + IsBusy = false; + _dialogService.ShowError(ex.Message); + } } private async void RunMutationOperation(Func operation) { - await ExecuteMutationOperationAsync(operation); + try + { + await ExecuteMutationOperationAsync(operation); + } + catch (Exception ex) + { + IsBusy = false; + _dialogService.ShowError(ex.Message); + } } private void UpdateStatus()