Refactor PostgreSQL provider: multi-schema & async prep
- Refactor migrations and provider to use multiple PostgreSQL schemas, each matching a legacy SQLite database (activitylog, authentication, displaypreferences, library, users). - All tables, foreign keys, and indexes are now schema-qualified; Down migration drops tables by schema. - Provider ensures schemas exist before migrations; entities are mapped to correct schemas in OnModelCreating. - Add support for max-pool-size, min-pool-size, and multiplexing connection options; update logging accordingly. - VACUUM ANALYZE now runs per schema during scheduled optimization. - TruncateAllTablesAsync now truncates tables with schema qualification. - README updated with schema structure, new options, and multiplexing warnings. - CacheDecorator now calls async repository methods using .GetAwaiter().GetResult(), with documentation. - Lays groundwork for full async/await and multiplexing support in the database layer.
This commit is contained in:
@@ -176,12 +176,23 @@ public sealed class BaseItemRepository
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<Guid> GetItemIdsList(InternalItemsQuery filter)
|
||||
{
|
||||
return GetItemIdsListAsync(filter, CancellationToken.None)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<Guid>> GetItemIdsListAsync(InternalItemsQuery filter, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(filter);
|
||||
PrepareFilterQuery(filter);
|
||||
|
||||
using var context = _dbProvider.CreateDbContext();
|
||||
return ApplyQueryFilter(context.BaseItems.AsNoTracking().Where(e => e.Id != EF.Constant(PlaceholderId)), context, filter).Select(e => e.Id).ToArray();
|
||||
await using var context = _dbProvider.CreateDbContext();
|
||||
return await ApplyQueryFilter(context.BaseItems.AsNoTracking().Where(e => e.Id != EF.Constant(PlaceholderId)), context, filter)
|
||||
.Select(e => e.Id)
|
||||
.ToArrayAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -252,11 +263,19 @@ public sealed class BaseItemRepository
|
||||
|
||||
/// <inheritdoc />
|
||||
public QueryResult<BaseItemDto> GetItems(InternalItemsQuery filter)
|
||||
{
|
||||
return GetItemsAsync(filter, CancellationToken.None)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<QueryResult<BaseItemDto>> GetItemsAsync(InternalItemsQuery filter, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(filter);
|
||||
if (!filter.EnableTotalRecordCount || ((filter.Limit ?? 0) == 0 && (filter.StartIndex ?? 0) == 0))
|
||||
{
|
||||
var returnList = GetItemList(filter);
|
||||
var returnList = await GetItemListAsync(filter, cancellationToken).ConfigureAwait(false);
|
||||
return new QueryResult<BaseItemDto>(
|
||||
filter.StartIndex,
|
||||
returnList.Count,
|
||||
@@ -266,7 +285,7 @@ public sealed class BaseItemRepository
|
||||
PrepareFilterQuery(filter);
|
||||
var result = new QueryResult<BaseItemDto>();
|
||||
|
||||
using var context = _dbProvider.CreateDbContext();
|
||||
await using var context = _dbProvider.CreateDbContext();
|
||||
|
||||
IQueryable<BaseItemEntity> dbQuery = PrepareItemQuery(context, filter);
|
||||
|
||||
@@ -275,24 +294,42 @@ public sealed class BaseItemRepository
|
||||
|
||||
if (filter.EnableTotalRecordCount)
|
||||
{
|
||||
result.TotalRecordCount = dbQuery.Count();
|
||||
result.TotalRecordCount = await dbQuery
|
||||
.CountAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
dbQuery = ApplyQueryPaging(dbQuery, filter);
|
||||
dbQuery = ApplyNavigations(dbQuery, filter);
|
||||
|
||||
result.Items = dbQuery.AsEnumerable().Where(e => e is not null).Select(w => DeserializeBaseItem(w, filter.SkipDeserialization)).Where(dto => dto is not null).ToArray()!;
|
||||
var items = await dbQuery
|
||||
.ToListAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
result.Items = items
|
||||
.Where(e => e is not null)
|
||||
.Select(w => DeserializeBaseItem(w, filter.SkipDeserialization))
|
||||
.Where(dto => dto is not null)
|
||||
.ToArray()!;
|
||||
result.StartIndex = filter.StartIndex ?? 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<BaseItemDto> GetItemList(InternalItemsQuery filter)
|
||||
{
|
||||
return GetItemListAsync(filter, CancellationToken.None)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<BaseItemDto>> GetItemListAsync(InternalItemsQuery filter, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(filter);
|
||||
PrepareFilterQuery(filter);
|
||||
|
||||
using var context = _dbProvider.CreateDbContext();
|
||||
await using var context = _dbProvider.CreateDbContext();
|
||||
IQueryable<BaseItemEntity> dbQuery = PrepareItemQuery(context, filter);
|
||||
|
||||
dbQuery = TranslateQuery(dbQuery, context, filter);
|
||||
@@ -303,14 +340,21 @@ public sealed class BaseItemRepository
|
||||
var hasRandomSort = filter.OrderBy.Any(e => e.OrderBy == ItemSortBy.Random);
|
||||
if (hasRandomSort)
|
||||
{
|
||||
var orderedIds = dbQuery.Select(e => e.Id).ToList();
|
||||
var orderedIds = await dbQuery
|
||||
.Select(e => e.Id)
|
||||
.ToListAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (orderedIds.Count == 0)
|
||||
{
|
||||
return Array.Empty<BaseItemDto>();
|
||||
}
|
||||
|
||||
var itemsById = ApplyNavigations(context.BaseItems.Where(e => orderedIds.Contains(e.Id)), filter)
|
||||
.AsEnumerable()
|
||||
var items = await ApplyNavigations(context.BaseItems.Where(e => orderedIds.Contains(e.Id)), filter)
|
||||
.ToListAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var itemsById = items
|
||||
.Select(w => DeserializeBaseItem(w, filter.SkipDeserialization))
|
||||
.Where(dto => dto is not null)
|
||||
.ToDictionary(i => i!.Id);
|
||||
@@ -320,7 +364,15 @@ public sealed class BaseItemRepository
|
||||
|
||||
dbQuery = ApplyNavigations(dbQuery, filter);
|
||||
|
||||
return dbQuery.AsEnumerable().Where(e => e is not null).Select(w => DeserializeBaseItem(w, filter.SkipDeserialization)).Where(dto => dto is not null).ToArray()!;
|
||||
var results = await dbQuery
|
||||
.ToListAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
return results
|
||||
.Where(e => e is not null)
|
||||
.Select(w => DeserializeBaseItem(w, filter.SkipDeserialization))
|
||||
.Where(dto => dto is not null)
|
||||
.ToArray()!;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -496,30 +548,47 @@ public sealed class BaseItemRepository
|
||||
/// <inheritdoc/>
|
||||
public int GetCount(InternalItemsQuery filter)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(filter);
|
||||
// Hack for right now since we currently don't support filtering out these duplicates within a query
|
||||
PrepareFilterQuery(filter);
|
||||
|
||||
using var context = _dbProvider.CreateDbContext();
|
||||
var dbQuery = TranslateQuery(context.BaseItems.AsNoTracking(), context, filter);
|
||||
|
||||
return dbQuery.Count();
|
||||
return GetCountAsync(filter, CancellationToken.None)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ItemCounts GetItemCounts(InternalItemsQuery filter)
|
||||
/// <inheritdoc/>
|
||||
public async Task<int> GetCountAsync(InternalItemsQuery filter, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(filter);
|
||||
// Hack for right now since we currently don't support filtering out these duplicates within a query
|
||||
PrepareFilterQuery(filter);
|
||||
|
||||
using var context = _dbProvider.CreateDbContext();
|
||||
await using var context = _dbProvider.CreateDbContext();
|
||||
var dbQuery = TranslateQuery(context.BaseItems.AsNoTracking(), context, filter);
|
||||
|
||||
var counts = dbQuery
|
||||
return await dbQuery.CountAsync(cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public ItemCounts GetItemCounts(InternalItemsQuery filter)
|
||||
{
|
||||
return GetItemCountsAsync(filter, CancellationToken.None)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ItemCounts> GetItemCountsAsync(InternalItemsQuery filter, CancellationToken cancellationToken = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(filter);
|
||||
// Hack for right now since we currently don't support filtering out these duplicates within a query
|
||||
PrepareFilterQuery(filter);
|
||||
|
||||
await using var context = _dbProvider.CreateDbContext();
|
||||
var dbQuery = TranslateQuery(context.BaseItems.AsNoTracking(), context, filter);
|
||||
|
||||
var counts = await dbQuery
|
||||
.GroupBy(x => x.Type)
|
||||
.Select(x => new { x.Key, Count = x.Count() })
|
||||
.ToArray();
|
||||
.ToArrayAsync(cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var lookup = _itemTypeLookup.BaseItemKindNames;
|
||||
var result = new ItemCounts();
|
||||
|
||||
Reference in New Issue
Block a user