using System.Collections.Immutable; using HermesSocketLibrary.db; namespace HermesSocketServer.Store.Internal { public abstract class AutoSavedStore : GroupSaveStore where K : class where V : class { private readonly GroupSaveSqlGenerator _generator; private readonly DatabaseTable _table; private readonly Database _database; private readonly Serilog.ILogger _logger; public AutoSavedStore(DatabaseTable table, Database database, Serilog.ILogger logger) : base() { if (table.TypeMapping == null) _generator = new GroupSaveSqlGenerator(table.PropertyMapping, logger); else _generator = new GroupSaveSqlGenerator(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(); 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 GenerateQuery(_deleted, (size) => _generator.GeneratePreparedDeleteSql(_table.TableName, size, _table.KeyColumns, typeMapping), async (query, keys, _) => await _generator.DoPreparedStatementRaw(_database, query, keys, _table.KeyColumns)); } private async Task GenerateQuery(IList keys, Func generate, Func, IEnumerable, Task> execute) { ImmutableList? list = null; _rwls.EnterUpgradeableReadLock(); try { if (!keys.Any()) return; _rwls.EnterWriteLock(); try { list = keys.ToImmutableList(); keys.Clear(); } finally { _rwls.ExitWriteLock(); } } finally { _rwls.ExitUpgradeableReadLock(); } 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}"); } } } }