Skip to content

Commit

Permalink
fix dbcontext cleanup on failed add for sqlite
Browse files Browse the repository at this point in the history
  • Loading branch information
georg-jung committed Jun 16, 2020
1 parent 135ed2e commit 0f0152d
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 11 deletions.
46 changes: 36 additions & 10 deletions WoL/Data/HostService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.Data.Sqlite;
using WoL.Models;

namespace WoL.Data
Expand All @@ -13,11 +14,13 @@ public class HostService : IHostService
{
private readonly ApplicationDbContext context;

private static readonly Regex parseDuplicateValue = new Regex(@"The duplicate key value is \(([^)]+)\)");
private static readonly Regex parseIndexName = new Regex(@"with unique index '([^']+)'\.");
private static readonly Regex tsqlParseDuplicateValue = new Regex(@"The duplicate key value is \(([^)]+)\)");
private static readonly Regex tsqlParseIndexName = new Regex(@"with unique index '([^']+)'\.");
// this makes assumptions about the names of the indices which is kind of bad
// on the other hand it sticks to ef's index naming conventions
private static readonly Regex parseIdxField = new Regex(@"_([^_]+)$");
private static readonly Regex tsqlParseIdxField = new Regex(@"_([^_]+)$");

private static readonly Regex sqliteParseParseIdxField = new Regex(@"UNIQUE constraint failed: ([^']+)");

public HostService(ApplicationDbContext context)
{
Expand All @@ -37,20 +40,43 @@ public async Task Add(Host host)
await context.SaveChangesAsync().ConfigureAwait(false);
}
catch (DbUpdateException dbue)
// see https://docs.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors
// i.e. "Cannot insert duplicate key row in object 'dbo.Host' with unique index 'IX_Host_Hostname'. The duplicate key value is (georg-nuc). The statement has been terminated."
when (dbue.InnerException is SqlException sqlEx &&
(sqlEx.Number == 2601 || sqlEx.Number == 2627))
// handle tsql
// see https://docs.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors
// i.e. "Cannot insert duplicate key row in object 'dbo.Host' with unique index 'IX_Host_Hostname'. The duplicate key value is (georg-nuc). The statement has been terminated."
when (dbue.InnerException is SqlException sqlEx &&
(sqlEx.Number == 2601 || sqlEx.Number == 2627))
{
// otherwise this host would be added again when further using the same DbContext
context.Entry(host).State = EntityState.Detached;

var msg = sqlEx.Message;
var duplVal = parseDuplicateValue.Match(msg).Groups[1].Value;
var idxName = parseIndexName.Match(msg).Groups[1].Value;
var fieldName = parseIdxField.Match(idxName).Groups[1].Value;
var duplVal = tsqlParseDuplicateValue.Match(msg).Groups[1].Value;
var idxName = tsqlParseIndexName.Match(msg).Groups[1].Value;
var fieldName = tsqlParseIdxField.Match(idxName).Groups[1].Value;
throw new IHostService.DuplicateEntryException(fieldName, duplVal, nameof(host), dbue);
}
catch (DbUpdateException dbue)
// handle sqlite
// see https://sqlite.org/rescode.html
// i.e. "SQLite Error 19: 'UNIQUE constraint failed: Host.MacAddress'."
when (dbue.InnerException is SqliteException sqlEx &&
(sqlEx.SqliteErrorCode == 19 || sqlEx.SqliteExtendedErrorCode == 2067))
{
// otherwise this host would be added again when further using the same DbContext
context.Entry(host).State = EntityState.Detached;

var msg = sqlEx.Message;
var fieldName = sqliteParseParseIdxField.Match(msg).Groups[1].Value;
if (fieldName.StartsWith("host.", StringComparison.OrdinalIgnoreCase))
fieldName = fieldName.Substring(5);
throw new IHostService.DuplicateEntryException(fieldName, nameof(host), dbue);
}
catch
{
// otherwise this host would be added again when further using the same DbContext
context.Entry(host).State = EntityState.Detached;
throw;
}
}

public async Task Update(Host host)
Expand Down
8 changes: 8 additions & 0 deletions WoL/Data/IHostService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,19 @@ protected DuplicateEntryException(SerializationInfo info, StreamingContext conte
private static string CreateMessage(string field, string value) =>
$"A host entry with {field} '{value}' does already exist.";

private static string CreateMessage(string field) =>
$"A host entry with the same {field} does already exist.";

public DuplicateEntryException(string field, string value, string paramName, Exception innerException) : base(CreateMessage(field, value), paramName, innerException)
{
Field = field;
Value = value;
}

public DuplicateEntryException(string field, string paramName, Exception innerException) : base(CreateMessage(field), paramName, innerException)
{
Field = field;
}
}
}
}
4 changes: 3 additions & 1 deletion WoL/Pages/AddHost.razor
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@
catch (IHostService.DuplicateEntryException duplEx)
{
L.AddHostDuplicateEntryException(host, duplEx);
Alert = $"Creation failed as an entry with {duplEx.Field.ToLower()} '{duplEx.Value}' does already exists.";
Alert = string.IsNullOrEmpty(duplEx.Value)
? $"Creation failed as an entry with this {duplEx.Field.ToLower()} does already exists."
: $"Creation failed as an entry with {duplEx.Field.ToLower()} '{duplEx.Value}' does already exists.";
Creating = false;
return;
}
Expand Down

0 comments on commit 0f0152d

Please sign in to comment.