Fixed AutoSavedStore's deletion of data when table was using composite keys.

This commit is contained in:
Tom
2025-01-09 03:38:23 +00:00
parent 3429c8f8dc
commit 467c3cf0b0
15 changed files with 202 additions and 221 deletions

View File

@ -1,3 +1,4 @@
using HermesSocketServer.Messages;
using HermesSocketServer.Models; using HermesSocketServer.Models;
using ILogger = Serilog.ILogger; using ILogger = Serilog.ILogger;
@ -22,7 +23,7 @@ namespace HermesSocketServer.Requests
int count = int.Parse(data["count"].ToString()!); int count = int.Parse(data["count"].ToString()!);
int span = int.Parse(data["span"].ToString()!); int span = int.Parse(data["span"].ToString()!);
var policy = new PolicyMessage() var policy = new Policy()
{ {
Id = id, Id = id,
UserId = channel.Id, UserId = channel.Id,

View File

@ -1,3 +1,4 @@
using HermesSocketServer.Messages;
using HermesSocketServer.Models; using HermesSocketServer.Models;
using ILogger = Serilog.ILogger; using ILogger = Serilog.ILogger;
@ -22,7 +23,7 @@ namespace HermesSocketServer.Requests
int count = int.Parse(data["count"].ToString()!); int count = int.Parse(data["count"].ToString()!);
int span = int.Parse(data["span"].ToString()!); int span = int.Parse(data["span"].ToString()!);
bool result = channel.Policies.Set(id.ToString(), new PolicyMessage() bool result = channel.Policies.Set(id.ToString(), new Policy()
{ {
Id = id, Id = id,
UserId = channel.Id, UserId = channel.Id,

View File

@ -1,4 +1,3 @@
using HermesSocketLibrary.Requests.Messages;
using HermesSocketServer.Models; using HermesSocketServer.Models;
using ILogger = Serilog.ILogger; using ILogger = Serilog.ILogger;
@ -26,17 +25,7 @@ namespace HermesSocketServer.Requests
return Task.FromResult(RequestResult.Failed("Order must be an integer.")); return Task.FromResult(RequestResult.Failed("Order must be an integer."));
bool state = data["state"].ToString()?.ToLower() == "true"; bool state = data["state"].ToString()?.ToLower() == "true";
var redemption = new Redemption() bool result = channel.Redemptions.Modify(id, r =>
{
Id = id,
UserId = channel.Id,
RedemptionId = redemptionId,
ActionName = actionName,
Order = order,
State = true,
};
bool result = channel.Redemptions.Modify(id.ToString(), r =>
{ {
if (r.UserId != channel.Id) if (r.UserId != channel.Id)
return; return;
@ -50,7 +39,7 @@ namespace HermesSocketServer.Requests
var r = channel.Redemptions.Get(id); var r = channel.Redemptions.Get(id);
if (result) if (result)
{ {
_logger.Information($"Added redemption to channel [id: {id}][redemption id: {redemptionId}][action: {actionName}][order: {order}][channel: {channel.Id}]"); _logger.Information($"Updated redemption to channel [id: {id}][redemption id: {redemptionId}][action: {actionName}][order: {order}][channel: {channel.Id}]");
return Task.FromResult(RequestResult.Successful(r)); return Task.FromResult(RequestResult.Successful(r));
} }

View File

@ -5,7 +5,7 @@ using HermesSocketServer.Store.Internal;
namespace HermesSocketServer.Store namespace HermesSocketServer.Store
{ {
public class ActionStore : AutoSavedStore<string, RedeemableAction> public class ActionStore : ComplexAutoSavedStore<string, RedeemableAction>
{ {
private readonly string _userId; private readonly string _userId;
private readonly Database _database; private readonly Database _database;
@ -45,9 +45,5 @@ namespace HermesSocketServer.Store
protected override void OnInitialModify(string key, RedeemableAction value) protected override void OnInitialModify(string key, RedeemableAction value)
{ {
} }
protected override void OnInitialRemove(string key)
{
}
} }
} }

View File

@ -4,7 +4,7 @@ using HermesSocketServer.Store.Internal;
namespace HermesSocketServer.Store namespace HermesSocketServer.Store
{ {
public class ChatterStore : AutoSavedStore<string, ChatterVoice> public class ChatterStore : ComplexAutoSavedStore<string, ChatterVoice>
{ {
private readonly string _userId; private readonly string _userId;
private readonly Database _database; private readonly Database _database;
@ -43,9 +43,5 @@ namespace HermesSocketServer.Store
protected override void OnInitialModify(string key, ChatterVoice value) protected override void OnInitialModify(string key, ChatterVoice value)
{ {
} }
protected override void OnInitialRemove(string key)
{
}
} }
} }

View File

@ -14,9 +14,12 @@ namespace HermesSocketServer.Store.Internal
public AutoSavedStore(DatabaseTable table, Database database, Serilog.ILogger logger) public AutoSavedStore(DatabaseTable table, Database database, Serilog.ILogger logger)
: base(logger) : base()
{ {
_generator = new GroupSaveSqlGenerator<V>(table.PropertyMapping, logger); if (table.TypeMapping == null)
_generator = new GroupSaveSqlGenerator<V>(table.PropertyMapping, logger);
else
_generator = new GroupSaveSqlGenerator<V>(table.PropertyMapping, table.TypeMapping, logger);
_table = table; _table = table;
_database = database; _database = database;
_logger = logger; _logger = logger;
@ -25,21 +28,22 @@ namespace HermesSocketServer.Store.Internal
public override async Task Save() public override async Task Save()
{ {
var allColumns = _table.KeyColumns.Union(_table.DataColumns).ToArray(); var allColumns = _table.KeyColumns.Union(_table.DataColumns).ToArray();
var typeMapping = _table.TypeMapping ?? new Dictionary<string, string>();
await GenerateQuery(_added, await GenerateQuery(_added,
(size) => _generator.GeneratePreparedInsertSql(_table.TableName, size, allColumns), (size) => _generator.GeneratePreparedInsertSql(_table.TableName, size, allColumns, typeMapping),
async (query, _, values) => await _generator.DoPreparedStatement(_database, query, values, allColumns));
await GenerateQuery(_modified,
(size) => _generator.GeneratePreparedUpdateSql(_table.TableName, size, _table.KeyColumns, _table.DataColumns),
async (query, _, values) => await _generator.DoPreparedStatement(_database, query, values, allColumns)); async (query, _, values) => await _generator.DoPreparedStatement(_database, query, values, allColumns));
await GenerateQuery(_deleted, await GenerateQuery(_modified,
(size) => _generator.GeneratePreparedDeleteSql(_table.TableName, size, _table.KeyColumns), (size) => _generator.GeneratePreparedUpdateSql(_table.TableName, size, _table.KeyColumns, _table.DataColumns, typeMapping),
async (query, _, values) => await _generator.DoPreparedStatement(_database, query, values, allColumns));
await GenerateQuery(_deleted,
(size) => _generator.GeneratePreparedDeleteSql(_table.TableName, size, _table.KeyColumns, typeMapping),
async (query, keys, _) => await _generator.DoPreparedStatementRaw(_database, query, keys, _table.KeyColumns)); async (query, keys, _) => await _generator.DoPreparedStatementRaw(_database, query, keys, _table.KeyColumns));
} }
private async Task GenerateQuery(IList<K> keys, Func<int, string> generate, Func<string, IEnumerable<K>, IEnumerable<V>, Task> execute) private async Task GenerateQuery(IList<K> keys, Func<int, string> generate, Func<string, IEnumerable<K>, IEnumerable<V>, Task<int>> execute)
{ {
ImmutableList<K>? list = null; ImmutableList<K>? list = null;
lock (_lock) lock (_lock)
@ -52,10 +56,17 @@ namespace HermesSocketServer.Store.Internal
} }
var query = generate(list.Count); var query = generate(list.Count);
_logger.Debug($"{_table.TableName} - Adding {list.Count} rows to database: {query}");
var values = list.Select(id => _store[id]).Where(v => v != null); var values = list.Select(id => _store[id]).Where(v => v != null);
await execute(query, list, values); int rowsAffected = await execute(query, list, values);
if (rowsAffected != list.Count)
{
_logger.Error($"Rows affected in database ({rowsAffected}) and the number of items that should be modified ({list.Count}) do not match: {query}");
}
else
{
_logger.Information($"{rowsAffected} rows were affected by this query: {query}");
}
} }
} }
} }

View File

@ -0,0 +1,106 @@
using System.Collections.Immutable;
using HermesSocketLibrary.db;
namespace HermesSocketServer.Store.Internal
{
public abstract class ComplexAutoSavedStore<K, V> : GroupSaveStore<K, V> where K : class where V : class
{
private readonly IList<V> _removedValues;
private readonly GroupSaveSqlGenerator<V> _generator;
private readonly DatabaseTable _table;
private readonly Database _database;
private readonly Serilog.ILogger _logger;
public ComplexAutoSavedStore(DatabaseTable table, Database database, Serilog.ILogger logger)
: base()
{
if (table.KeyColumns.Length <= 1)
throw new InvalidOperationException($"Use AutoSavedStore instead due to a single key column [table name: {table.TableName}]");
_removedValues = new List<V>();
if (table.TypeMapping == null)
_generator = new GroupSaveSqlGenerator<V>(table.PropertyMapping, logger);
else
_generator = new GroupSaveSqlGenerator<V>(table.PropertyMapping, table.TypeMapping, logger);
_table = table;
_database = database;
_logger = logger;
}
public override async Task Save()
{
var allColumns = _table.KeyColumns.Union(_table.DataColumns).ToArray();
var typeMapping = _table.TypeMapping ?? new Dictionary<string, string>();
await GenerateQuery(_added,
(size) => _generator.GeneratePreparedInsertSql(_table.TableName, size, allColumns, typeMapping),
async (query, _, values) => await _generator.DoPreparedStatement(_database, query, values, allColumns));
await GenerateQuery(_modified,
(size) => _generator.GeneratePreparedUpdateSql(_table.TableName, size, _table.KeyColumns, _table.DataColumns, typeMapping),
async (query, _, values) => await _generator.DoPreparedStatement(_database, query, values, allColumns));
await GenerateDeleteQuery(_deleted, _removedValues,
(size) => _generator.GeneratePreparedDeleteSql(_table.TableName, size, _table.KeyColumns, typeMapping),
async (query, list) => await _generator.DoPreparedStatement(_database, query, list, _table.KeyColumns));
}
private async Task GenerateQuery(IList<K> keys, Func<int, string> generate, Func<string, IEnumerable<K>, IEnumerable<V>, Task<int>> execute)
{
ImmutableList<K>? list = null;
lock (_lock)
{
if (!keys.Any())
return;
list = keys.ToImmutableList();
keys.Clear();
}
var query = generate(list.Count);
var values = list.Select(id => _store[id]).Where(v => v != null);
int rowsAffected = await execute(query, list, values);
if (rowsAffected != list.Count)
{
_logger.Error($"Rows affected in database ({rowsAffected}) and the number of items that should be modified ({list.Count}) do not match: {query}");
}
else
{
_logger.Information($"{rowsAffected} rows were affected by this query: {query}");
}
}
private async Task GenerateDeleteQuery(IList<K> keys, IList<V> values, Func<int, string> generate, Func<string, IEnumerable<V>, Task<int>> execute)
{
ImmutableList<V>? list = null;
lock (_lock)
{
if (!keys.Any() || !values.Any()) {
return;
}
list = values.ToImmutableList();
values.Clear();
keys.Clear();
}
var query = generate(list.Count);
int rowsAffected = await execute(query, list);
if (rowsAffected != list.Count)
{
_logger.Error($"Rows affected in database ({rowsAffected}) and the number of items that should be modified ({list.Count}) do not match: {query}");
}
else
{
_logger.Information($"{rowsAffected} rows were affected by this query: {query}");
}
}
protected override void OnPostRemove(K key, V value) => _removedValues.Add(value);
}
}

View File

@ -2,22 +2,21 @@ using System.Reflection;
using System.Text; using System.Text;
using System.Text.Json; using System.Text.Json;
using HermesSocketLibrary.db; using HermesSocketLibrary.db;
using NpgsqlTypes;
namespace HermesSocketServer.Store.Internal namespace HermesSocketServer.Store.Internal
{ {
public class GroupSaveSqlGenerator<T> public class GroupSaveSqlGenerator<T>
{ {
private readonly IDictionary<string, PropertyInfo?> _columnPropertyRelations; private readonly IDictionary<string, PropertyInfo?> _columnPropertyRelations;
private readonly IDictionary<string, NpgsqlDbType> _columnTypes; private readonly IDictionary<string, string> _columnTypes;
private readonly Serilog.ILogger _logger; private readonly Serilog.ILogger _logger;
public GroupSaveSqlGenerator(IDictionary<string, string> columnsToProperties, Serilog.ILogger logger) public GroupSaveSqlGenerator(IDictionary<string, string> columnsToProperties, Serilog.ILogger logger)
: this(columnsToProperties, new Dictionary<string, NpgsqlDbType>(), logger) : this(columnsToProperties, new Dictionary<string, string>(), logger)
{ {
} }
public GroupSaveSqlGenerator(IDictionary<string, string> columnsToProperties, IDictionary<string, NpgsqlDbType> columnTypes, Serilog.ILogger logger) public GroupSaveSqlGenerator(IDictionary<string, string> columnsToProperties, IDictionary<string, string> columnTypes, Serilog.ILogger logger)
{ {
_columnPropertyRelations = columnsToProperties.ToDictionary(p => p.Key, p => typeof(T).GetProperty(p.Value)); _columnPropertyRelations = columnsToProperties.ToDictionary(p => p.Key, p => typeof(T).GetProperty(p.Value));
_columnTypes = columnTypes; _columnTypes = columnTypes;
@ -29,11 +28,11 @@ namespace HermesSocketServer.Store.Internal
throw new ArgumentException("Some properties do not exist on the values given: " + string.Join(", ", nullProperties)); throw new ArgumentException("Some properties do not exist on the values given: " + string.Join(", ", nullProperties));
} }
public async Task DoPreparedStatement<V>(Database database, string sql, IEnumerable<V> values, string[] columns) public async Task<int> DoPreparedStatement<V>(Database database, string sql, IEnumerable<V> values, string[] columns)
{ {
try try
{ {
await database.Execute(sql, (c) => return await database.Execute(sql, (c) =>
{ {
var valueCounter = 0; var valueCounter = 0;
foreach (var value in values) foreach (var value in values)
@ -43,12 +42,10 @@ namespace HermesSocketServer.Store.Internal
var propValue = _columnPropertyRelations[column]!.GetValue(value); var propValue = _columnPropertyRelations[column]!.GetValue(value);
if (_columnTypes.Any() && _columnTypes.TryGetValue(column, out var type)) if (_columnTypes.Any() && _columnTypes.TryGetValue(column, out var type))
{ {
if (type == NpgsqlDbType.Jsonb) if (type == "jsonb")
propValue = JsonSerializer.Serialize(propValue); propValue = JsonSerializer.Serialize(propValue);
c.Parameters.AddWithValue(column.ToLower() + valueCounter, type, propValue ?? DBNull.Value);
} }
else c.Parameters.AddWithValue(column.ToLower() + valueCounter, propValue ?? DBNull.Value);
c.Parameters.AddWithValue(column.ToLower() + valueCounter, propValue ?? DBNull.Value);
} }
valueCounter++; valueCounter++;
} }
@ -57,14 +54,15 @@ namespace HermesSocketServer.Store.Internal
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Failed to execute a prepared statement: " + sql); _logger.Error(ex, "Failed to execute a prepared statement: " + sql);
return -1;
} }
} }
public async Task DoPreparedStatementRaw<V>(Database database, string sql, IEnumerable<V> values, string[] columns) public async Task<int> DoPreparedStatementRaw<V>(Database database, string sql, IEnumerable<V> values, string[] columns)
{ {
try try
{ {
await database.Execute(sql, (c) => return await database.Execute(sql, (c) =>
{ {
var valueCounter = 0; var valueCounter = 0;
foreach (var value in values) foreach (var value in values)
@ -81,44 +79,11 @@ namespace HermesSocketServer.Store.Internal
catch (Exception ex) catch (Exception ex)
{ {
_logger.Error(ex, "Failed to execute a prepared statement: " + sql); _logger.Error(ex, "Failed to execute a prepared statement: " + sql);
return -1;
} }
} }
public string GenerateInsertSql(string table, IEnumerable<T> values, IEnumerable<string> columns) public string GeneratePreparedInsertSql(string table, int rows, IEnumerable<string> columns, IDictionary<string, string> typeMapping)
{
if (string.IsNullOrWhiteSpace(table))
throw new ArgumentException("Value is either null or whitespace-filled.", nameof(table));
if (values == null)
throw new ArgumentNullException(nameof(values));
if (!values.Any())
throw new ArgumentException("Empty list given.", nameof(values));
if (columns == null)
throw new ArgumentNullException(nameof(columns));
if (!columns.Any())
throw new ArgumentException("Empty list given.", nameof(columns));
var ctp = columns.ToDictionary(c => c, c => _columnPropertyRelations[c]);
var sb = new StringBuilder();
sb.Append($"INSERT INTO \"{table}\" (\"{string.Join("\", \"", columns)}\") VALUES ");
foreach (var value in values)
{
sb.Append("(");
foreach (var column in columns)
{
var propValue = _columnPropertyRelations[column]!.GetValue(value);
var propType = _columnPropertyRelations[column]!.PropertyType;
WriteValue(sb, propValue ?? DBNull.Value, propType);
sb.Append(",");
}
sb.Remove(sb.Length - 1, 1)
.Append("),");
}
sb.Remove(sb.Length - 1, 1)
.Append(';');
return sb.ToString();
}
public string GeneratePreparedInsertSql(string table, int rows, IEnumerable<string> columns)
{ {
if (string.IsNullOrWhiteSpace(table)) if (string.IsNullOrWhiteSpace(table))
throw new ArgumentException("Value is either null or whitespace-filled.", nameof(table)); throw new ArgumentException("Value is either null or whitespace-filled.", nameof(table));
@ -138,8 +103,14 @@ namespace HermesSocketServer.Store.Internal
{ {
sb.Append('@') sb.Append('@')
.Append(column) .Append(column)
.Append(row) .Append(row);
.Append(", ");
if (typeMapping.TryGetValue(column, out var type))
sb.Append("::\"")
.Append(type)
.Append("\"");
sb.Append(", ");
} }
sb.Remove(sb.Length - 2, 2) sb.Remove(sb.Length - 2, 2)
.Append("),"); .Append("),");
@ -149,49 +120,7 @@ namespace HermesSocketServer.Store.Internal
return sb.ToString(); return sb.ToString();
} }
public string GenerateUpdateSql(string table, IEnumerable<T> values, IEnumerable<string> keyColumns, IEnumerable<string> updateColumns) public string GeneratePreparedUpdateSql(string table, int rows, IEnumerable<string> keyColumns, IEnumerable<string> updateColumns, IDictionary<string, string> typeMapping)
{
if (string.IsNullOrWhiteSpace(table))
throw new ArgumentException("Value is either null or whitespace-filled.", nameof(table));
if (values == null)
throw new ArgumentNullException(nameof(values));
if (!values.Any())
throw new ArgumentException("Empty list given.", nameof(values));
if (keyColumns == null)
throw new ArgumentNullException(nameof(keyColumns));
if (!keyColumns.Any())
throw new ArgumentException("Empty list given.", nameof(keyColumns));
if (updateColumns == null)
throw new ArgumentNullException(nameof(updateColumns));
if (!updateColumns.Any())
throw new ArgumentException("Empty list given.", nameof(updateColumns));
var columns = keyColumns.Union(updateColumns);
var ctp = columns.ToDictionary(c => c, c => _columnPropertyRelations[c]);
var sb = new StringBuilder();
sb.Append($"UPDATE \"{table}\" as t SET {string.Join(", ", updateColumns.Select(c => "\"" + c + "\" = c.\"" + c + "\""))} FROM (VALUES ");
foreach (var value in values)
{
sb.Append("(");
foreach (var column in columns)
{
var propValue = _columnPropertyRelations[column]!.GetValue(value);
var propType = _columnPropertyRelations[column]!.PropertyType;
WriteValue(sb, propValue, propType);
sb.Append(",");
}
sb.Remove(sb.Length - 1, 1)
.Append("),");
}
sb.Remove(sb.Length - 1, 1)
.Append($") AS c(\"{string.Join("\", \"", columns)}\") WHERE ")
.Append(string.Join(" AND ", keyColumns.Select(c => "t.\"" + c + "\" = c.\"" + c + "\"")))
.Append(";");
return sb.ToString();
}
public string GeneratePreparedUpdateSql(string table, int rows, IEnumerable<string> keyColumns, IEnumerable<string> updateColumns)
{ {
if (string.IsNullOrWhiteSpace(table)) if (string.IsNullOrWhiteSpace(table))
throw new ArgumentException("Value is either null or whitespace-filled.", nameof(table)); throw new ArgumentException("Value is either null or whitespace-filled.", nameof(table));
@ -215,8 +144,14 @@ namespace HermesSocketServer.Store.Internal
{ {
sb.Append('@') sb.Append('@')
.Append(column) .Append(column)
.Append(row) .Append(row);
.Append(", ");
if (typeMapping.TryGetValue(column, out var type))
sb.Append("::\"")
.Append(type)
.Append("\"");
sb.Append(", ");
} }
sb.Remove(sb.Length - 2, 2) sb.Remove(sb.Length - 2, 2)
.Append("),"); .Append("),");
@ -229,41 +164,7 @@ namespace HermesSocketServer.Store.Internal
return sb.ToString(); return sb.ToString();
} }
public string GenerateDeleteSql(string table, IEnumerable<string> keys, IEnumerable<string> keyColumns) public string GeneratePreparedDeleteSql(string table, int rows, IEnumerable<string> keyColumns, IDictionary<string, string> typeMapping)
{
if (string.IsNullOrWhiteSpace(table))
throw new ArgumentException("Value is either null or whitespace-filled.", nameof(table));
if (keys == null)
throw new ArgumentNullException(nameof(keys));
if (!keys.Any())
throw new ArgumentException("Empty list given.", nameof(keys));
if (keyColumns == null)
throw new ArgumentNullException(nameof(keyColumns));
if (!keyColumns.Any())
throw new ArgumentException("Empty list given.", nameof(keyColumns));
var ctp = keyColumns.ToDictionary(c => c, c => _columnPropertyRelations[c]);
var sb = new StringBuilder();
sb.Append($"DELETE FROM \"{table}\" WHERE (\"{string.Join("\", \"", keyColumns)}\") IN (");
foreach (var k in keys)
{
sb.Append("(");
foreach (var column in keyColumns)
{
var propType = _columnPropertyRelations[column]!.PropertyType;
WriteValue(sb, k, propType);
sb.Append(",");
}
sb.Remove(sb.Length - 1, 1)
.Append("),");
}
sb.Remove(sb.Length - 1, 1)
.Append(");");
return sb.ToString();
}
public string GeneratePreparedDeleteSql(string table, int rows, IEnumerable<string> keyColumns)
{ {
if (string.IsNullOrWhiteSpace(table)) if (string.IsNullOrWhiteSpace(table))
throw new ArgumentException("Value is either null or whitespace-filled.", nameof(table)); throw new ArgumentException("Value is either null or whitespace-filled.", nameof(table));
@ -282,8 +183,14 @@ namespace HermesSocketServer.Store.Internal
{ {
sb.Append('@') sb.Append('@')
.Append(column) .Append(column)
.Append(row) .Append(row);
.Append(", ");
if (typeMapping.TryGetValue(column, out var type))
sb.Append("::\"")
.Append(type)
.Append("\"");
sb.Append(", ");
} }
sb.Remove(sb.Length - 2, 2) sb.Remove(sb.Length - 2, 2)
.Append("),"); .Append("),");
@ -293,26 +200,5 @@ namespace HermesSocketServer.Store.Internal
return sb.ToString(); return sb.ToString();
} }
private void WriteValue(StringBuilder sb, object? value, Type type)
{
if (type == typeof(string))
sb.Append("'")
.Append(value)
.Append("'");
else if (type == typeof(Guid))
sb.Append("uuid('")
.Append(value?.ToString())
.Append("')");
else if (type == typeof(TimeSpan))
{
if (value == null)
sb.Append("0");
else
sb.Append(((TimeSpan)value).TotalMilliseconds);
}
else
sb.Append(value);
}
} }
} }

View File

@ -6,7 +6,6 @@ namespace HermesSocketServer.Store.Internal
{ {
public abstract class GroupSaveStore<K, V> : IStore<K, V> where K : class where V : class public abstract class GroupSaveStore<K, V> : IStore<K, V> where K : class where V : class
{ {
private readonly Serilog.ILogger _logger;
protected readonly IDictionary<K, V> _store; protected readonly IDictionary<K, V> _store;
protected readonly IList<K> _added; protected readonly IList<K> _added;
protected readonly IList<K> _modified; protected readonly IList<K> _modified;
@ -14,9 +13,8 @@ namespace HermesSocketServer.Store.Internal
protected readonly object _lock; protected readonly object _lock;
public GroupSaveStore(Serilog.ILogger logger) public GroupSaveStore()
{ {
_logger = logger;
_store = new Dictionary<K, V>(); _store = new Dictionary<K, V>();
_added = new List<K>(); _added = new List<K>();
_modified = new List<K>(); _modified = new List<K>();
@ -27,7 +25,7 @@ namespace HermesSocketServer.Store.Internal
public abstract Task Load(); public abstract Task Load();
protected abstract void OnInitialAdd(K key, V value); protected abstract void OnInitialAdd(K key, V value);
protected abstract void OnInitialModify(K key, V value); protected abstract void OnInitialModify(K key, V value);
protected abstract void OnInitialRemove(K key); protected abstract void OnPostRemove(K key, V value);
public abstract Task Save(); public abstract Task Save();
public V? Get(K key) public V? Get(K key)
@ -52,7 +50,7 @@ namespace HermesSocketServer.Store.Internal
{ {
if (key == null) if (key == null)
return false; return false;
lock (_lock) lock (_lock)
{ {
if (_store.TryGetValue(key, out V? value)) if (_store.TryGetValue(key, out V? value))
@ -65,7 +63,6 @@ namespace HermesSocketServer.Store.Internal
if (!_added.Contains(key) && !_modified.Contains(key)) if (!_added.Contains(key) && !_modified.Contains(key))
{ {
_modified.Add(key); _modified.Add(key);
_logger.Information($"added key to _modified {key}");
} }
return true; return true;
} }
@ -80,21 +77,21 @@ namespace HermesSocketServer.Store.Internal
lock (_lock) lock (_lock)
{ {
OnInitialRemove(key); if (_store.TryGetValue(key, out var value))
if (_store.Remove(key))
{ {
_logger.Information($"removed key from _deleted {key}"); if (_store.Remove(key))
if (!_added.Remove(key))
{ {
_modified.Remove(key); OnPostRemove(key, value);
_logger.Information($"removed key from _added & _modified {key}"); if (!_added.Remove(key))
if (!_deleted.Contains(key))
{ {
_deleted.Add(key); _modified.Remove(key);
_logger.Information($"added key to _deleted {key}"); if (!_deleted.Contains(key))
{
_deleted.Add(key);
}
} }
return true;
} }
return true;
} }
} }
return false; return false;
@ -116,7 +113,6 @@ namespace HermesSocketServer.Store.Internal
if (!_added.Contains(key) && !_modified.Contains(key)) if (!_added.Contains(key) && !_modified.Contains(key))
{ {
_modified.Add(key); _modified.Add(key);
_logger.Information($"added key to _modified {key}");
} }
return true; return true;
} }
@ -128,7 +124,6 @@ namespace HermesSocketServer.Store.Internal
if (!_deleted.Remove(key) && !_added.Contains(key)) if (!_deleted.Remove(key) && !_added.Contains(key))
{ {
_added.Add(key); _added.Add(key);
_logger.Information($"added key to _added {key}");
} }
return true; return true;
} }

View File

@ -1,10 +1,10 @@
using HermesSocketLibrary.db; using HermesSocketLibrary.db;
using HermesSocketServer.Models; using HermesSocketServer.Messages;
using HermesSocketServer.Store.Internal; using HermesSocketServer.Store.Internal;
namespace HermesSocketServer.Store namespace HermesSocketServer.Store
{ {
public class PolicyStore : AutoSavedStore<string, PolicyMessage> public class PolicyStore : AutoSavedStore<string, Policy>
{ {
private readonly string _userId; private readonly string _userId;
private readonly Database _database; private readonly Database _database;
@ -26,7 +26,7 @@ namespace HermesSocketServer.Store
await _database.Execute(sql, data, (reader) => await _database.Execute(sql, data, (reader) =>
{ {
var id = reader.GetGuid(0); var id = reader.GetGuid(0);
_store.Add(id.ToString(), new PolicyMessage() _store.Add(id.ToString(), new Policy()
{ {
Id = id, Id = id,
UserId = _userId, UserId = _userId,
@ -39,15 +39,15 @@ namespace HermesSocketServer.Store
_logger.Information($"Loaded {_store.Count} policies from database."); _logger.Information($"Loaded {_store.Count} policies from database.");
} }
protected override void OnInitialAdd(string key, PolicyMessage value) protected override void OnInitialAdd(string key, Policy value)
{ {
} }
protected override void OnInitialModify(string key, PolicyMessage value) protected override void OnInitialModify(string key, Policy value)
{ {
} }
protected override void OnInitialRemove(string key) protected override void OnPostRemove(string key, Policy value)
{ {
} }
} }

View File

@ -47,7 +47,7 @@ namespace HermesSocketServer.Store
{ {
} }
protected override void OnInitialRemove(string key) protected override void OnPostRemove(string key, Redemption value)
{ {
} }
} }

View File

@ -44,7 +44,7 @@ namespace HermesSocketServer.Store
{ {
} }
protected override void OnInitialRemove(string key) protected override void OnPostRemove(string key, TTSWordFilter value)
{ {
} }
} }

View File

@ -43,7 +43,7 @@ namespace HermesSocketServer.Store
{ {
} }
protected override void OnInitialRemove(string key) protected override void OnPostRemove(string key, User value)
{ {
} }
} }

View File

@ -5,7 +5,7 @@ using HermesSocketServer.Validators;
namespace HermesSocketServer.Store namespace HermesSocketServer.Store
{ {
public class VoiceStateStore : AutoSavedStore<string, TTSVoiceState> public class VoiceStateStore : ComplexAutoSavedStore<string, TTSVoiceState>
{ {
private readonly string _userId; private readonly string _userId;
private readonly VoiceIdValidator _idValidator; private readonly VoiceIdValidator _idValidator;
@ -48,7 +48,7 @@ namespace HermesSocketServer.Store
{ {
} }
protected override void OnInitialRemove(string key) protected override void OnPostRemove(string key, TTSVoiceState value)
{ {
} }
} }

View File

@ -48,7 +48,7 @@ namespace HermesSocketServer.Store
_nameValidator.Check(value.Name); _nameValidator.Check(value.Name);
} }
protected override void OnInitialRemove(string key) protected override void OnPostRemove(string key, TTSVoice value)
{ {
} }
} }