Skip to content

Commit 0f0152d

Browse files
committed
fix dbcontext cleanup on failed add for sqlite
1 parent 135ed2e commit 0f0152d

File tree

3 files changed

+47
-11
lines changed

3 files changed

+47
-11
lines changed

WoL/Data/HostService.cs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Linq;
66
using System.Text.RegularExpressions;
77
using System.Threading.Tasks;
8+
using Microsoft.Data.Sqlite;
89
using WoL.Models;
910

1011
namespace WoL.Data
@@ -13,11 +14,13 @@ public class HostService : IHostService
1314
{
1415
private readonly ApplicationDbContext context;
1516

16-
private static readonly Regex parseDuplicateValue = new Regex(@"The duplicate key value is \(([^)]+)\)");
17-
private static readonly Regex parseIndexName = new Regex(@"with unique index '([^']+)'\.");
17+
private static readonly Regex tsqlParseDuplicateValue = new Regex(@"The duplicate key value is \(([^)]+)\)");
18+
private static readonly Regex tsqlParseIndexName = new Regex(@"with unique index '([^']+)'\.");
1819
// this makes assumptions about the names of the indices which is kind of bad
1920
// on the other hand it sticks to ef's index naming conventions
20-
private static readonly Regex parseIdxField = new Regex(@"_([^_]+)$");
21+
private static readonly Regex tsqlParseIdxField = new Regex(@"_([^_]+)$");
22+
23+
private static readonly Regex sqliteParseParseIdxField = new Regex(@"UNIQUE constraint failed: ([^']+)");
2124

2225
public HostService(ApplicationDbContext context)
2326
{
@@ -37,20 +40,43 @@ public async Task Add(Host host)
3740
await context.SaveChangesAsync().ConfigureAwait(false);
3841
}
3942
catch (DbUpdateException dbue)
40-
// see https://docs.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors
41-
// 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."
42-
when (dbue.InnerException is SqlException sqlEx &&
43-
(sqlEx.Number == 2601 || sqlEx.Number == 2627))
43+
// handle tsql
44+
// see https://docs.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors
45+
// 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."
46+
when (dbue.InnerException is SqlException sqlEx &&
47+
(sqlEx.Number == 2601 || sqlEx.Number == 2627))
4448
{
4549
// otherwise this host would be added again when further using the same DbContext
4650
context.Entry(host).State = EntityState.Detached;
4751

4852
var msg = sqlEx.Message;
49-
var duplVal = parseDuplicateValue.Match(msg).Groups[1].Value;
50-
var idxName = parseIndexName.Match(msg).Groups[1].Value;
51-
var fieldName = parseIdxField.Match(idxName).Groups[1].Value;
53+
var duplVal = tsqlParseDuplicateValue.Match(msg).Groups[1].Value;
54+
var idxName = tsqlParseIndexName.Match(msg).Groups[1].Value;
55+
var fieldName = tsqlParseIdxField.Match(idxName).Groups[1].Value;
5256
throw new IHostService.DuplicateEntryException(fieldName, duplVal, nameof(host), dbue);
5357
}
58+
catch (DbUpdateException dbue)
59+
// handle sqlite
60+
// see https://sqlite.org/rescode.html
61+
// i.e. "SQLite Error 19: 'UNIQUE constraint failed: Host.MacAddress'."
62+
when (dbue.InnerException is SqliteException sqlEx &&
63+
(sqlEx.SqliteErrorCode == 19 || sqlEx.SqliteExtendedErrorCode == 2067))
64+
{
65+
// otherwise this host would be added again when further using the same DbContext
66+
context.Entry(host).State = EntityState.Detached;
67+
68+
var msg = sqlEx.Message;
69+
var fieldName = sqliteParseParseIdxField.Match(msg).Groups[1].Value;
70+
if (fieldName.StartsWith("host.", StringComparison.OrdinalIgnoreCase))
71+
fieldName = fieldName.Substring(5);
72+
throw new IHostService.DuplicateEntryException(fieldName, nameof(host), dbue);
73+
}
74+
catch
75+
{
76+
// otherwise this host would be added again when further using the same DbContext
77+
context.Entry(host).State = EntityState.Detached;
78+
throw;
79+
}
5480
}
5581

5682
public async Task Update(Host host)

WoL/Data/IHostService.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,19 @@ protected DuplicateEntryException(SerializationInfo info, StreamingContext conte
3838
private static string CreateMessage(string field, string value) =>
3939
$"A host entry with {field} '{value}' does already exist.";
4040

41+
private static string CreateMessage(string field) =>
42+
$"A host entry with the same {field} does already exist.";
43+
4144
public DuplicateEntryException(string field, string value, string paramName, Exception innerException) : base(CreateMessage(field, value), paramName, innerException)
4245
{
4346
Field = field;
4447
Value = value;
4548
}
49+
50+
public DuplicateEntryException(string field, string paramName, Exception innerException) : base(CreateMessage(field), paramName, innerException)
51+
{
52+
Field = field;
53+
}
4654
}
4755
}
4856
}

WoL/Pages/AddHost.razor

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,9 @@
150150
catch (IHostService.DuplicateEntryException duplEx)
151151
{
152152
L.AddHostDuplicateEntryException(host, duplEx);
153-
Alert = $"Creation failed as an entry with {duplEx.Field.ToLower()} '{duplEx.Value}' does already exists.";
153+
Alert = string.IsNullOrEmpty(duplEx.Value)
154+
? $"Creation failed as an entry with this {duplEx.Field.ToLower()} does already exists."
155+
: $"Creation failed as an entry with {duplEx.Field.ToLower()} '{duplEx.Value}' does already exists.";
154156
Creating = false;
155157
return;
156158
}

0 commit comments

Comments
 (0)