adding cache
This commit is contained in:
parent
39faab6483
commit
673cda6408
@ -1,3 +1,4 @@
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using OpenCand.API.Config;
|
||||
using OpenCand.API.Repository;
|
||||
@ -51,15 +52,21 @@ namespace OpenCand.API
|
||||
|
||||
|
||||
app.Run();
|
||||
}
|
||||
|
||||
private static void SetupServices(WebApplicationBuilder builder)
|
||||
} private static void SetupServices(WebApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.Configure<FotosSettings>(builder.Configuration.GetSection("FotosSettings"));
|
||||
builder.Services.AddScoped<OpenCandRepository>();
|
||||
builder.Services.AddScoped<CandidatoRepository>();
|
||||
builder.Services.AddScoped<BemCandidatoRepository>();
|
||||
builder.Services.AddScoped<DespesaReceitaRepository>();
|
||||
builder.Services.AddMemoryCache();
|
||||
|
||||
// Register repositories with IMemoryCache
|
||||
builder.Services.AddScoped<OpenCandRepository>(provider =>
|
||||
new OpenCandRepository(provider.GetRequiredService<IConfiguration>(), provider.GetService<IMemoryCache>()));
|
||||
builder.Services.AddScoped<CandidatoRepository>(provider =>
|
||||
new CandidatoRepository(provider.GetRequiredService<IConfiguration>(), provider.GetService<IMemoryCache>()));
|
||||
builder.Services.AddScoped<BemCandidatoRepository>(provider =>
|
||||
new BemCandidatoRepository(provider.GetRequiredService<IConfiguration>(), provider.GetService<IMemoryCache>()));
|
||||
builder.Services.AddScoped<DespesaReceitaRepository>(provider =>
|
||||
new DespesaReceitaRepository(provider.GetRequiredService<IConfiguration>(), provider.GetService<IMemoryCache>()));
|
||||
|
||||
builder.Services.AddScoped<OpenCandService>();
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Npgsql;
|
||||
|
||||
namespace OpenCand.Repository
|
||||
@ -7,11 +7,131 @@ namespace OpenCand.Repository
|
||||
{
|
||||
protected string ConnectionString { get; private set; }
|
||||
protected NpgsqlConnection? Connection { get; private set; }
|
||||
protected readonly IMemoryCache? _cache;
|
||||
|
||||
public BaseRepository(IConfiguration configuration)
|
||||
// Default cache settings
|
||||
protected static readonly TimeSpan DefaultCacheExpiration = TimeSpan.FromDays(7);
|
||||
protected static readonly CacheItemPriority DefaultCachePriority = CacheItemPriority.Normal;
|
||||
|
||||
public BaseRepository(IConfiguration configuration, IMemoryCache? cache = null)
|
||||
{
|
||||
ConnectionString = configuration["DatabaseSettings:ConnectionString"] ??
|
||||
throw new ArgumentNullException("Connection string not found in configuration");
|
||||
_cache = cache;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic method to get data from cache or execute a factory function if not cached
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of data to cache</typeparam>
|
||||
/// <param name="cacheKey">Unique cache key</param>
|
||||
/// <param name="factory">Function to execute if data is not in cache</param>
|
||||
/// <param name="expiration">Cache expiration time (optional, uses default if not provided)</param>
|
||||
/// <param name="priority">Cache priority (optional, uses default if not provided)</param>
|
||||
/// <returns>Cached or freshly retrieved data</returns>
|
||||
protected async Task<T?> GetOrSetCacheAsync<T>(
|
||||
string cacheKey,
|
||||
Func<Task<T?>> factory,
|
||||
TimeSpan? expiration = null,
|
||||
CacheItemPriority? priority = null) where T : class
|
||||
{
|
||||
// If caching is not available, execute factory directly
|
||||
if (_cache == null)
|
||||
{
|
||||
return await factory();
|
||||
}
|
||||
|
||||
// Try to get cached data first
|
||||
if (_cache.TryGetValue(cacheKey, out T? cachedData) && cachedData != null)
|
||||
{
|
||||
return cachedData;
|
||||
}
|
||||
|
||||
// If not in cache, execute factory function
|
||||
var result = await factory();
|
||||
|
||||
// Only cache non-null results
|
||||
if (result != null)
|
||||
{
|
||||
_cache.Set(cacheKey, result, new MemoryCacheEntryOptions
|
||||
{
|
||||
SlidingExpiration = expiration ?? DefaultCacheExpiration,
|
||||
Priority = priority ?? DefaultCachePriority
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generic method to get data from cache or execute a synchronous factory function if not cached
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of data to cache</typeparam>
|
||||
/// <param name="cacheKey">Unique cache key</param>
|
||||
/// <param name="factory">Function to execute if data is not in cache</param>
|
||||
/// <param name="expiration">Cache expiration time (optional, uses default if not provided)</param>
|
||||
/// <param name="priority">Cache priority (optional, uses default if not provided)</param>
|
||||
/// <returns>Cached or freshly retrieved data</returns>
|
||||
protected T? GetOrSetCache<T>(
|
||||
string cacheKey,
|
||||
Func<T?> factory,
|
||||
TimeSpan? expiration = null,
|
||||
CacheItemPriority? priority = null) where T : class
|
||||
{
|
||||
// If caching is not available, execute factory directly
|
||||
if (_cache == null)
|
||||
{
|
||||
return factory();
|
||||
}
|
||||
|
||||
// Try to get cached data first
|
||||
if (_cache.TryGetValue(cacheKey, out T? cachedData) && cachedData != null)
|
||||
{
|
||||
return cachedData;
|
||||
}
|
||||
|
||||
// If not in cache, execute factory function
|
||||
var result = factory();
|
||||
|
||||
// Only cache non-null results
|
||||
if (result != null)
|
||||
{
|
||||
_cache.Set(cacheKey, result, new MemoryCacheEntryOptions
|
||||
{
|
||||
SlidingExpiration = expiration ?? DefaultCacheExpiration,
|
||||
Priority = priority ?? DefaultCachePriority
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes an item from cache by key
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Cache key to remove</param>
|
||||
protected void ClearCache(string cacheKey)
|
||||
{
|
||||
_cache?.Remove(cacheKey);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if an item exists in cache
|
||||
/// </summary>
|
||||
/// <param name="cacheKey">Cache key to check</param>
|
||||
/// <returns>True if item exists in cache, false otherwise</returns>
|
||||
protected bool IsCached(string cacheKey)
|
||||
{
|
||||
return _cache?.TryGetValue(cacheKey, out _) ?? false;
|
||||
} /// <summary>
|
||||
/// Generates a standardized cache key for entity-based caching
|
||||
/// </summary>
|
||||
/// <param name="entityName">Name of the entity (e.g., "Candidato", "Stats")</param>
|
||||
/// <param name="identifier">Unique identifier for the entity (optional)</param>
|
||||
/// <returns>Formatted cache key</returns>
|
||||
protected static string GenerateCacheKey(string entityName, object? identifier = null)
|
||||
{
|
||||
return identifier != null ? $"{entityName}_{identifier}" : entityName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Npgsql;
|
||||
using OpenCand.Core.Models;
|
||||
|
||||
@ -6,7 +7,7 @@ namespace OpenCand.Repository
|
||||
{
|
||||
public class BemCandidatoRepository : BaseRepository
|
||||
{
|
||||
public BemCandidatoRepository(IConfiguration configuration) : base(configuration)
|
||||
public BemCandidatoRepository(IConfiguration configuration, IMemoryCache? cache = null) : base(configuration, cache)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Npgsql;
|
||||
using OpenCand.Core.Models;
|
||||
|
||||
@ -6,33 +7,44 @@ namespace OpenCand.Repository
|
||||
{
|
||||
public class CandidatoRepository : BaseRepository
|
||||
{
|
||||
public CandidatoRepository(IConfiguration configuration) : base(configuration)
|
||||
public CandidatoRepository(IConfiguration configuration, IMemoryCache? cache = null) : base(configuration, cache)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<List<Candidato>> SearchCandidatosAsync(string query)
|
||||
public async Task<List<Candidato>?> SearchCandidatosAsync(string query)
|
||||
{
|
||||
using var connection = new NpgsqlConnection(ConnectionString);
|
||||
return (await connection.QueryAsync<Candidato>(@"
|
||||
SELECT c.*,
|
||||
GREATEST(similarity(c.apelido, @q), similarity(c.nome, @q)) AS sim
|
||||
FROM candidato c
|
||||
WHERE c.apelido % @q
|
||||
OR c.nome % @q
|
||||
ORDER BY c.popularidade DESC, sim DESC, length(c.nome) ASC
|
||||
LIMIT 10;
|
||||
", new { q = query })).AsList();
|
||||
string cacheKey = GenerateCacheKey("Search", query);
|
||||
|
||||
return await GetOrSetCacheAsync(cacheKey, async () =>
|
||||
{
|
||||
using var connection = new NpgsqlConnection(ConnectionString);
|
||||
return (await connection.QueryAsync<Candidato>(@"
|
||||
SELECT c.*,
|
||||
GREATEST(similarity(c.apelido, @q), similarity(c.nome, @q)) AS sim
|
||||
FROM candidato c
|
||||
WHERE c.apelido % @q
|
||||
OR c.nome % @q
|
||||
ORDER BY c.popularidade DESC, sim DESC, length(c.nome) ASC
|
||||
LIMIT 10;
|
||||
", new { q = query })).AsList();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public async Task<Candidato?> GetCandidatoAsync(Guid idcandidato)
|
||||
{
|
||||
using (var connection = new NpgsqlConnection(ConnectionString))
|
||||
string cacheKey = GenerateCacheKey("Candidato", idcandidato);
|
||||
|
||||
return await GetOrSetCacheAsync(cacheKey, async () =>
|
||||
{
|
||||
return await connection.QueryFirstOrDefaultAsync<Candidato>(@"
|
||||
SELECT * FROM candidato
|
||||
WHERE idcandidato = @idcandidato;",
|
||||
new { idcandidato });
|
||||
}
|
||||
using (var connection = new NpgsqlConnection(ConnectionString))
|
||||
{
|
||||
return await connection.QueryFirstOrDefaultAsync<Candidato>(@"
|
||||
SELECT * FROM candidato
|
||||
WHERE idcandidato = @idcandidato;",
|
||||
new { idcandidato });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<string?> GetCandidatoCpfAsync(Guid idcandidato)
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Npgsql;
|
||||
using OpenCand.Core.Models;
|
||||
@ -7,7 +8,7 @@ namespace OpenCand.Repository
|
||||
{
|
||||
public class DespesaReceitaRepository : BaseRepository
|
||||
{
|
||||
public DespesaReceitaRepository(IConfiguration configuration) : base(configuration)
|
||||
public DespesaReceitaRepository(IConfiguration configuration, IMemoryCache? cache = null) : base(configuration, cache)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1,29 +1,45 @@
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Npgsql;
|
||||
using OpenCand.Core.Models;
|
||||
using OpenCand.Repository;
|
||||
|
||||
namespace OpenCand.API.Repository
|
||||
{
|
||||
public class OpenCandRepository : BaseRepository
|
||||
{ public class OpenCandRepository : BaseRepository
|
||||
{
|
||||
public OpenCandRepository(IConfiguration configuration) : base(configuration)
|
||||
public OpenCandRepository(IConfiguration configuration, IMemoryCache? cache = null) : base(configuration, cache)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<OpenCandStats> GetOpenCandStatsAsync()
|
||||
{
|
||||
using (var connection = new NpgsqlConnection(ConnectionString))
|
||||
string cacheKey = GenerateCacheKey("OpenCandStats");
|
||||
|
||||
var result = await GetOrSetCacheAsync(cacheKey, async () =>
|
||||
{
|
||||
var stats = await connection.QueryFirstOrDefaultAsync<OpenCandStats>(@"
|
||||
SELECT
|
||||
(SELECT COUNT(idcandidato) FROM candidato) AS TotalCandidatos,
|
||||
(SELECT COUNT(*) FROM bem_candidato) AS TotalBemCandidatos,
|
||||
(SELECT SUM(valor) FROM bem_candidato) AS TotalValorBemCandidatos,
|
||||
(SELECT COUNT(*) FROM rede_social) AS TotalRedesSociais,
|
||||
(SELECT COUNT(DISTINCT ano) FROM candidato_mapping) AS TotalEleicoes;");
|
||||
return stats ?? new OpenCandStats();
|
||||
}
|
||||
using (var connection = new NpgsqlConnection(ConnectionString))
|
||||
{
|
||||
var stats = await connection.QueryFirstOrDefaultAsync<OpenCandStats>(@"
|
||||
SELECT
|
||||
(SELECT COUNT(idcandidato) FROM candidato) AS TotalCandidatos,
|
||||
(SELECT COUNT(*) FROM bem_candidato) AS TotalBemCandidatos,
|
||||
(SELECT SUM(valor) FROM bem_candidato) AS TotalValorBemCandidatos,
|
||||
(SELECT COUNT(*) FROM rede_social) AS TotalRedesSociais,
|
||||
(SELECT COUNT(DISTINCT ano) FROM candidato_mapping) AS TotalEleicoes;");
|
||||
|
||||
return stats ?? new OpenCandStats();
|
||||
}
|
||||
});
|
||||
|
||||
return result ?? new OpenCandStats();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears the cached OpenCand statistics, forcing a fresh fetch on the next call
|
||||
/// </summary>
|
||||
public void ClearStatsCache()
|
||||
{
|
||||
ClearCache(GenerateCacheKey("OpenCandStats"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user