Refactor SQL exception handling and config structure
Simplify SQL exception tracking by replacing error message mappings with a list of relevant error numbers in appsettings.json. Remove custom error message logic and related classes, introducing SqlExceptionOptions to hold tracked error codes.
This commit is contained in:
@@ -10,10 +10,8 @@
|
||||
},
|
||||
"SqlExceptionTranslator": {
|
||||
"ErrorMessages": {
|
||||
"515": "{Operation} '{Target}' failed because a required field was not provided or was null. Verify mandatory values and retry.",
|
||||
"547": "{Operation} '{Target}' failed because one or more referenced entities do not exist or violate relational rules. Please verify identifiers and constraints.",
|
||||
"2601": "{Operation} '{Target}' failed because the data conflicts with a unique constraint. Remove duplicate values and retry.",
|
||||
"2627": "{Operation} '{Target}' failed because the data conflicts with a unique constraint. Remove duplicate values and retry."
|
||||
// https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlexception.number
|
||||
"SqlExceptionNumber": [ 515 , 547, 2601, 2627]
|
||||
}
|
||||
},
|
||||
"AddedWho": "ReC.API",
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace ReC.Application.Common.Options;
|
||||
|
||||
public class SqlExceptionOptions
|
||||
{
|
||||
public HashSet<int> SqlExceptionNumber { get; set; } = [];
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
namespace ReC.Application.Common.Options;
|
||||
|
||||
public class SqlExceptionTranslatorOptions
|
||||
{
|
||||
public HashSet<int> ErrorNumbers { get; private set; } = [];
|
||||
|
||||
private Dictionary<int, string> _badRequestMessages = new()
|
||||
{
|
||||
[515] = "{Operation} '{Target}' failed because a required field was not provided or was null. Verify mandatory values and retry.",
|
||||
[547] = "{Operation} '{Target}' failed because one or more referenced entities do not exist or violate relational rules. Please verify identifiers and constraints.",
|
||||
[2601] = "{Operation} '{Target}' failed because the data conflicts with a unique constraint. Remove duplicate values and retry.",
|
||||
[2627] = "{Operation} '{Target}' failed because the data conflicts with a unique constraint. Remove duplicate values and retry."
|
||||
};
|
||||
|
||||
public Dictionary<int, string> ErrorMessages
|
||||
{
|
||||
get => _badRequestMessages;
|
||||
set
|
||||
{
|
||||
_badRequestMessages = value;
|
||||
ErrorNumbers = [.. value.Keys];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
using Microsoft.Data.SqlClient;
|
||||
using Microsoft.Extensions.Options;
|
||||
using ReC.Application.Common.Options;
|
||||
|
||||
namespace ReC.Application.Common.Procedures;
|
||||
|
||||
internal interface ISqlExceptionTranslator
|
||||
{
|
||||
bool IsBadRequestDataIssue(SqlException exception);
|
||||
|
||||
string BuildBadRequestMessage(string operation, string? entity, SqlException exception);
|
||||
}
|
||||
|
||||
internal sealed class SqlExceptionTranslator : ISqlExceptionTranslator
|
||||
{
|
||||
private readonly IOptionsMonitor<SqlExceptionTranslatorOptions> _optionsMonitor;
|
||||
|
||||
public SqlExceptionTranslator(IOptionsMonitor<SqlExceptionTranslatorOptions> optionsMonitor)
|
||||
{
|
||||
_optionsMonitor = optionsMonitor;
|
||||
}
|
||||
|
||||
public bool IsBadRequestDataIssue(SqlException exception)
|
||||
{
|
||||
var options = _optionsMonitor.CurrentValue;
|
||||
|
||||
if (options.BadRequestErrorNumbers is { Count: > 0 } &&
|
||||
options.BadRequestErrorNumbers.Contains(exception.Number))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return options.BadRequestMessages?.ContainsKey(exception.Number) ?? false;
|
||||
}
|
||||
|
||||
public string BuildBadRequestMessage(string operation, string? entity, SqlException exception)
|
||||
{
|
||||
var target = string.IsNullOrWhiteSpace(entity) ? "object" : entity;
|
||||
var options = _optionsMonitor.CurrentValue;
|
||||
var template = ResolveTemplate(options, exception.Number);
|
||||
|
||||
return template
|
||||
.Replace("{Operation}", operation)
|
||||
.Replace("{Target}", target)
|
||||
.Replace("{ErrorMessage}", exception.Message);
|
||||
}
|
||||
|
||||
private static string ResolveTemplate(SqlExceptionTranslatorOptions options, int errorNumber)
|
||||
{
|
||||
if (options.BadRequestMessages is not null &&
|
||||
options.BadRequestMessages.TryGetValue(errorNumber, out var template) &&
|
||||
!string.IsNullOrWhiteSpace(template))
|
||||
{
|
||||
return template;
|
||||
}
|
||||
|
||||
return string.IsNullOrWhiteSpace(options.DefaultBadRequestMessage)
|
||||
? SqlExceptionTranslatorOptions.DefaultFallbackMessage
|
||||
: options.DefaultBadRequestMessage;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user