Fix concurrent

This commit is contained in:
2025-08-14 18:31:44 +07:00
parent 0a2b7aa335
commit cfb04e9dc9
4 changed files with 71 additions and 74 deletions

View File

@@ -1,4 +1,3 @@
using System.Data;
using Managing.Application.Abstractions.Repositories; using Managing.Application.Abstractions.Repositories;
using Managing.Application.Abstractions.Services; using Managing.Application.Abstractions.Services;
using Managing.Domain.Accounts; using Managing.Domain.Accounts;
@@ -17,27 +16,7 @@ public class PostgreSqlAccountRepository : IAccountRepository
_cacheService = cacheService; _cacheService = cacheService;
} }
/// <summary>
/// Ensures the database connection is open before executing queries
/// </summary>
private async Task EnsureConnectionOpenAsync()
{
if (_context.Database.GetDbConnection().State != ConnectionState.Open)
{
await _context.Database.OpenConnectionAsync();
}
}
/// <summary>
/// Safely closes the database connection if it was opened by us
/// </summary>
private async Task SafeCloseConnectionAsync()
{
if (_context.Database.GetDbConnection().State == ConnectionState.Open)
{
await _context.Database.CloseConnectionAsync();
}
}
public void DeleteAccountByName(string name) public void DeleteAccountByName(string name)
{ {
@@ -55,7 +34,7 @@ public class PostgreSqlAccountRepository : IAccountRepository
{ {
try try
{ {
await EnsureConnectionOpenAsync(); await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context);
var accountEntity = await _context.Accounts var accountEntity = await _context.Accounts
.AsNoTracking() .AsNoTracking()
@@ -65,11 +44,10 @@ public class PostgreSqlAccountRepository : IAccountRepository
return PostgreSqlMappers.Map(accountEntity); return PostgreSqlMappers.Map(accountEntity);
} }
catch (Exception) finally
{ {
// If there's an error, try to reset the connection // Always ensure the connection is closed after the operation
await SafeCloseConnectionAsync(); await PostgreSqlConnectionHelper.SafeCloseConnectionAsync(_context);
throw;
} }
} }
@@ -108,7 +86,7 @@ public class PostgreSqlAccountRepository : IAccountRepository
{ {
try try
{ {
await EnsureConnectionOpenAsync(); await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context);
// Use proper async operations with AsNoTracking for optimal performance // Use proper async operations with AsNoTracking for optimal performance
var accountEntities = await _context.Accounts var accountEntities = await _context.Accounts
@@ -119,11 +97,10 @@ public class PostgreSqlAccountRepository : IAccountRepository
return PostgreSqlMappers.Map(accountEntities); return PostgreSqlMappers.Map(accountEntities);
} }
catch (Exception) finally
{ {
// If there's an error, try to reset the connection // Always ensure the connection is closed after the operation
await SafeCloseConnectionAsync(); await PostgreSqlConnectionHelper.SafeCloseConnectionAsync(_context);
throw;
} }
} }

View File

@@ -137,14 +137,24 @@ public class PostgreSqlBotRepository : IBotRepository
public async Task<IEnumerable<Bot>> GetBotsByIdsAsync(IEnumerable<Guid> identifiers) public async Task<IEnumerable<Bot>> GetBotsByIdsAsync(IEnumerable<Guid> identifiers)
{ {
var entities = await _context.Bots try
.AsNoTracking() {
.Include(m => m.User) await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context);
.Where(b => identifiers.Contains(b.Identifier))
.ToListAsync()
.ConfigureAwait(false);
return PostgreSqlMappers.Map(entities); var entities = await _context.Bots
.AsNoTracking()
.Include(m => m.User)
.Where(b => identifiers.Contains(b.Identifier))
.ToListAsync()
.ConfigureAwait(false);
return PostgreSqlMappers.Map(entities);
}
finally
{
// Always ensure the connection is closed after the operation
await PostgreSqlConnectionHelper.SafeCloseConnectionAsync(_context);
}
} }
public async Task<(IEnumerable<Bot> Bots, int TotalCount)> GetBotsPaginatedAsync( public async Task<(IEnumerable<Bot> Bots, int TotalCount)> GetBotsPaginatedAsync(

View File

@@ -0,0 +1,34 @@
using System.Data;
using Microsoft.EntityFrameworkCore;
namespace Managing.Infrastructure.Databases.PostgreSql;
/// <summary>
/// Helper class for managing PostgreSQL database connections in Entity Framework repositories
/// </summary>
public static class PostgreSqlConnectionHelper
{
/// <summary>
/// Ensures the database connection is open before executing queries
/// </summary>
/// <param name="context">The DbContext to manage the connection for</param>
public static async Task EnsureConnectionOpenAsync(DbContext context)
{
if (context.Database.GetDbConnection().State != ConnectionState.Open)
{
await context.Database.OpenConnectionAsync();
}
}
/// <summary>
/// Safely closes the database connection if it was opened by us
/// </summary>
/// <param name="context">The DbContext to manage the connection for</param>
public static async Task SafeCloseConnectionAsync(DbContext context)
{
if (context.Database.GetDbConnection().State == ConnectionState.Open)
{
await context.Database.CloseConnectionAsync();
}
}
}

View File

@@ -1,4 +1,3 @@
using System.Data;
using Managing.Application.Abstractions.Repositories; using Managing.Application.Abstractions.Repositories;
using Managing.Domain.Users; using Managing.Domain.Users;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@@ -14,33 +13,13 @@ public class PostgreSqlUserRepository : IUserRepository
_context = context; _context = context;
} }
/// <summary>
/// Ensures the database connection is open before executing queries
/// </summary>
private async Task EnsureConnectionOpenAsync()
{
if (_context.Database.GetDbConnection().State != ConnectionState.Open)
{
await _context.Database.OpenConnectionAsync();
}
}
/// <summary>
/// Safely closes the database connection if it was opened by us
/// </summary>
private async Task SafeCloseConnectionAsync()
{
if (_context.Database.GetDbConnection().State == ConnectionState.Open)
{
await _context.Database.CloseConnectionAsync();
}
}
public async Task<User> GetUserByAgentNameAsync(string agentName) public async Task<User> GetUserByAgentNameAsync(string agentName)
{ {
try try
{ {
await EnsureConnectionOpenAsync(); await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context);
var userEntity = await _context.Users var userEntity = await _context.Users
.AsNoTracking() .AsNoTracking()
@@ -49,11 +28,10 @@ public class PostgreSqlUserRepository : IUserRepository
return PostgreSqlMappers.Map(userEntity); return PostgreSqlMappers.Map(userEntity);
} }
catch (Exception) finally
{ {
// If there's an error, try to reset the connection // Always ensure the connection is closed after the operation
await SafeCloseConnectionAsync(); await PostgreSqlConnectionHelper.SafeCloseConnectionAsync(_context);
throw;
} }
} }
@@ -61,7 +39,7 @@ public class PostgreSqlUserRepository : IUserRepository
{ {
try try
{ {
await EnsureConnectionOpenAsync(); await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context);
var userEntity = await _context.Users var userEntity = await _context.Users
.AsNoTracking() .AsNoTracking()
@@ -70,11 +48,10 @@ public class PostgreSqlUserRepository : IUserRepository
return PostgreSqlMappers.Map(userEntity); return PostgreSqlMappers.Map(userEntity);
} }
catch (Exception) finally
{ {
// If there's an error, try to reset the connection // Always ensure the connection is closed after the operation
await SafeCloseConnectionAsync(); await PostgreSqlConnectionHelper.SafeCloseConnectionAsync(_context);
throw;
} }
} }
@@ -82,7 +59,7 @@ public class PostgreSqlUserRepository : IUserRepository
{ {
try try
{ {
await EnsureConnectionOpenAsync(); await PostgreSqlConnectionHelper.EnsureConnectionOpenAsync(_context);
var userEntities = await _context.Users var userEntities = await _context.Users
.AsNoTracking() .AsNoTracking()
@@ -91,11 +68,10 @@ public class PostgreSqlUserRepository : IUserRepository
return userEntities.Select(PostgreSqlMappers.Map); return userEntities.Select(PostgreSqlMappers.Map);
} }
catch (Exception) finally
{ {
// If there's an error, try to reset the connection // Always ensure the connection is closed after the operation
await SafeCloseConnectionAsync(); await PostgreSqlConnectionHelper.SafeCloseConnectionAsync(_context);
throw;
} }
} }