CoreDatabase 2025.7.24
dotnet add package CoreDatabase --version 2025.7.24
NuGet\Install-Package CoreDatabase -Version 2025.7.24
<PackageReference Include="CoreDatabase" Version="2025.7.24" />
<PackageVersion Include="CoreDatabase" Version="2025.7.24" />
<PackageReference Include="CoreDatabase" />
paket add CoreDatabase --version 2025.7.24
#r "nuget: CoreDatabase, 2025.7.24"
#:package CoreDatabase@2025.7.24
#addin nuget:?package=CoreDatabase&version=2025.7.24
#tool nuget:?package=CoreDatabase&version=2025.7.24
CoreDatabase
Biblioteca de acceso a datos para SQL Server que proporciona una capa de abstracción robusta, segura y fácil de usar sobre ADO.NET, con soporte completo para operaciones síncronas y asíncronas.
Características
- Conexión segura a SQL Server con soporte para autenticación integrada y SQL
- Manejo de transacciones (commit/rollback)
- Ejecución de consultas SQL y procedimientos almacenados
- Soporte completo para parámetros SQL con tipado fuerte
- Manejo de errores robusto con mensajes descriptivos
- Operaciones asíncronas para mejor rendimiento
- Manejo automático de conexiones (abre/cierra según sea necesario)
- Integración con inyección de dependencias
- Binding automático de objetos a parámetros SQL
- Operaciones bulk para inserción masiva de datos
- Soporte para múltiples formatos de retorno (DataTable, List<T>, Diccionarios, objetos fuertemente tipados)
- Creación y eliminación dinámica de tablas
- Soporte para múltiples conjuntos de resultados
Instalación
El componente se puede instalar como un paquete NuGet:
dotnet add package DeveloperKit.CoreDatabase
Requisitos
- SQL Server 2012 o superior
- .NET 6.0 o superior (compatible con .NET Standard 2.1+)
- .NET Framework 4.8 (soporte heredado)
- Visual Studio 2022 o superior (recomendado)
- SQL Server Native Client o Microsoft.Data.SqlClient
Configuración
Configuración Inicial
// Configuración básica
services.AddScoped<IMSSqlRepository, MSSqlRepository>(provider =>
{
var connectionString = Configuration.GetConnectionString("SqlServerConnection");
return new MSSqlRepository(connectionString);
});
// Configuración con nombre de aplicación personalizado
services.AddScoped<IMSSqlRepository>(provider =>
{
var connectionString = Configuration.GetConnectionString("SqlServerConnection");
return new MSSqlRepository(connectionString, () => "MiAplicacion");
});
// Configuración con credenciales explícitas
services.AddScoped<IMSSqlRepository>(provider =>
{
var config = provider.GetRequiredService<IConfiguration>();
return new MSSqlRepository(
serverName: config["Database:Server"],
dataBaseName: config["Database:Name"],
userName: config["Database:User"],
password: config["Database:Password"]
);
});
Uso
Ejemplos de Uso
Consultas Básicas
// Inyección del repositorio
private readonly IMSSqlRepository _sqlRepository;
public CustomerService(IMSSqlRepository sqlRepository)
{
_sqlRepository = sqlRepository;
}
// Ejecutar consulta y obtener DataTable
public async Task<DataTable> GetActiveCustomersAsync()
{
var query = "SELECT * FROM CUSTOMERS WHERE ACTIVE = @active";
return await _sqlRepository.ExecuteQueryAsTableAsync(query, parameters =>
{
parameters.Add("active", SqlDbType.Bit, true);
});
}
// Ejecutar consulta y mapear a objetos fuertemente tipados
public async Task<List<Customer>> GetCustomersAsync()
{
var query = "SELECT CustomerId, Name, Email, CreatedDate FROM CUSTOMERS";
return await _sqlRepository.ExecuteQueryAsListAsync(query, reader => new Customer
{
CustomerId = reader.GetInt32(0),
Name = reader.GetString(1),
Email = reader.IsDBNull(2) ? null : reader.GetString(2),
CreatedDate = reader.GetDateTime(3)
});
}
Procedimientos Almacenados
// Ejecutar procedimiento almacenado con parámetros
public async Task<Customer> GetCustomerByIdAsync(int customerId)
{
var spName = "usp_GetCustomerById";
return await _sqlRepository.ExecuteProcedureAsSingleAsync(spName, reader => new Customer
{
CustomerId = reader.GetInt32("CustomerId"),
Name = reader.GetString("Name"),
Email = reader.IsDBNull("Email") ? null : reader.GetString("Email"),
CreatedDate = reader.GetDateTime("CreatedDate")
}, parameters =>
{
parameters.Add("@CustomerId", SqlDbType.Int, customerId);
});
}
// Procedimiento con múltiples conjuntos de resultados
public async Task<(List<Customer> Customers, List<Order> Orders)> GetCustomerWithOrdersAsync(int customerId)
{
var spName = "usp_GetCustomerWithOrders";
var result = await _sqlRepository.ExecuteQueryAsListDictionaryAsync(
"EXEC " + spName + " @CustomerId",
parameters =>
{
parameters.Add("@CustomerId", SqlDbType.Int, customerId);
});
var customers = result[0].Select(dict => new Customer
{
CustomerId = (int)dict["CustomerId"],
Name = (string)dict["Name"]
}).ToList();
var orders = result[1].Select(dict => new Order
{
OrderId = (int)dict["OrderId"],
OrderDate = (DateTime)dict["OrderDate"],
TotalAmount = (decimal)dict["TotalAmount"]
}).ToList();
return (customers, orders);
}
Operaciones CRUD
// Insertar un solo registro
public async Task<int> InsertCustomerAsync(Customer customer)
{
var query = @"
INSERT INTO Customers (Name, Email, CreatedDate)
OUTPUT INSERTED.CustomerId
VALUES (@Name, @Email, @CreatedDate)";
return await _sqlRepository.ExecuteQueryAsSingleAsync<int>(query, reader => reader.GetInt32(0),
parameters =>
{
parameters.Add("@Name", SqlDbType.NVarChar, 100, customer.Name);
parameters.Add("@Email", SqlDbType.NVarChar, 100, (object)customer.Email ?? DBNull.Value);
parameters.Add("@CreatedDate", SqlDbType.DateTime2, DateTime.UtcNow);
});
}
// Insertar múltiples registros (bulk insert)
public async Task BulkInsertCustomersAsync(IEnumerable<Customer> customers)
{
await _sqlRepository.ExecuteBulkCopyAsync("Customers", customers, mapping =>
{
mapping.Column("Name", c => c.Name);
mapping.Column("Email", c => c.Email);
mapping.Column("CreatedDate", c => c.CreatedDate);
});
}
// Actualizar un registro
public async Task<bool> UpdateCustomerAsync(Customer customer)
{
var query = @"
UPDATE Customers
SET Name = @Name,
Email = @Email,
ModifiedDate = @ModifiedDate
WHERE CustomerId = @CustomerId";
int rowsAffected = await _sqlRepository.ExecuteNonQueryAsync(query, parameters =>
{
parameters.Add("@CustomerId", SqlDbType.Int, customer.CustomerId);
parameters.Add("@Name", SqlDbType.NVarChar, 100, customer.Name);
parameters.Add("@Email", SqlDbType.NVarChar, 100, (object)customer.Email ?? DBNull.Value);
parameters.Add("@ModifiedDate", SqlDbType.DateTime2, DateTime.UtcNow);
});
return rowsAffected > 0;
}
Operaciones con Transacciones
public async Task<bool> ProcessOrderAsync(Order order, List<OrderItem> items)
{
try
{
_sqlRepository.BeginTransaction();
// Insertar orden
var orderId = await _sqlRepository.ExecuteQueryAsSingleAsync<int>(
"INSERT INTO Orders (...) OUTPUT INSERTED.OrderId VALUES (...)",
reader => reader.GetInt32(0));
// Insertar ítems
foreach (var item in items)
{
item.OrderId = orderId;
await _sqlRepository.ExecuteInsertAsync("OrderItems", item);
}
// Actualizar inventario
foreach (var item in items)
{
await _sqlRepository.ExecuteNonQueryAsync(
"UPDATE Products SET Stock = Stock - @Quantity WHERE ProductId = @ProductId",
parameters =>
{
parameters.Add("@ProductId", SqlDbType.Int, item.ProductId);
parameters.Add("@Quantity", SqlDbType.Int, item.Quantity);
});
}
_sqlRepository.CommitTransaction();
return true;
}
catch (Exception ex)
{
_sqlRepository.RollbackTransaction();
_logger.LogError(ex, "Error al procesar la orden");
return false;
}
}
Manejo de Transacciones
public async Task ExecuteWithTransaction()
{
try
{
// Iniciar transacción
_sqlRepository.BeginTransaction();
// Ejecutar operaciones
await _sqlRepository.ExecuteQueryAsync("INSERT INTO CUSTOMERS (...) VALUES (...)"));
await _sqlRepository.ExecuteQueryAsync("UPDATE CUSTOMERS SET (...) WHERE (...)"));
// Confirmar transacción
_sqlRepository.CommitTransaction();
}
catch (Exception ex)
{
// Revertir transacción en caso de error
_sqlRepository.RollbackTransaction();
throw;
}
}
Manejo de Parámetros
// Parámetros básicos
parameters.Add("p_input", SqlDbType.Varchar, "valor");
// Parámetros con tamaño
parameters.Add("p_text", SqlDbType.NVarChar, 100, "valor largo");
// Parámetros numéricos
parameters.Add("p_decimal", SqlDbType.Decimal, 18, 2, 123.45);
// Parámetros de salida
parameters.Add("p_output", SqlDbType.Int, ParameterDirection.Output);
// Parámetros de entrada/salida
parameters.Add("p_inout", SqlDbType.Int, 1, ParameterDirection.InputOutput);
Operaciones Avanzadas
Inserción Masiva (Bulk Insert)
// Inserción masiva desde DataTable
public async Task BulkInsertCustomersAsync(DataTable customersData)
{
await _sqlRepository.ExecuteBulkCopyToTableAsync(customersData, "Customers");
}
// Inserción masiva desde colección de objetos
public async Task BulkInsertCustomersAsync(IEnumerable<Customer> customers)
{
await _sqlRepository.ExecuteBulkCopyAsync("Customers", customers, mapping =>
{
mapping.Column("CustomerId", c => c.CustomerId);
mapping.Column("Name", c => c.Name);
mapping.Column("Email", c => c.Email);
mapping.Column("CreatedDate", c => c.CreatedDate);
});
}
Creación Dinámica de Tablas
// Crear tabla desde DataTable
public void CreateTableFromData(DataTable sourceData, string destinationTableName)
{
_sqlRepository.CreateTable(sourceData, destinationTableName);
}
// Crear tabla desde IDataReader
public void CreateTableFromReader(IDataReader reader, string destinationTableName)
{
_sqlRepository.CreateTable(reader, destinationTableName);
}
// Eliminar tabla
public void DropCustomerTable()
{
_sqlRepository.DropTable("Customers");
}
Manejo de Tipos de Datos
La biblioteca soporta todos los tipos de datos de SQL Server, incluyendo tipos especiales como:
- Tipos numéricos: INT, BIGINT, DECIMAL, FLOAT, etc.
- Tipos de texto: VARCHAR, NVARCHAR, CHAR, NCHAR, TEXT, NTEXT
- Tipos de fecha: DATETIME, DATETIME2, DATE, TIME, DATETIMEOFFSET
- Tipos binarios: VARBINARY, IMAGE
- Tipos especiales: UNIQUEIDENTIFIER, XML, JSON, GEOMETRY, GEOGRAPHY
Manejo de Errores
try
{
// Código que interactúa con la base de datos
}
catch (SqlException sqlEx) when (sqlEx.Number == 2627) // Violación de restricción única
{
throw new DuplicateEntryException("Ya existe un registro con la misma clave", sqlEx);
}
catch (SqlException sqlEx) when (sqlEx.Number == 547) // Violación de clave foránea
{
throw new InvalidOperationException("No se puede eliminar el registro porque tiene registros relacionados", sqlEx);
}
catch (SqlException sqlEx) when (sqlEx.Number == 1205) // Deadlock
{
// Reintentar la operación
await Task.Delay(500);
await ExecuteOperationWithRetry();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error al acceder a la base de datos");
throw new DatabaseOperationException("Error al procesar la operación", ex);
}
Mejores Prácticas
Seguridad
- Siempre usar parámetros en las consultas para evitar inyección SQL
- No concatenar valores directamente en las consultas SQL
- Usar los permisos mínimos necesarios para la conexión a la base de datos
- Validar y limpiar todos los datos de entrada
Rendimiento
- Usar operaciones asíncronas para operaciones de E/S
- Implementar paginación para consultas que devuelven grandes conjuntos de datos
- Usar operaciones bulk para inserciones/actualizaciones masivas
- Mantener las transacciones lo más cortas posibles
Mantenibilidad
- Usar procedimientos almacenados para lógica de negocio compleja
- Implementar un patrón de repositorio para centralizar el acceso a datos
- Usar DTOs para transferir datos entre capas
- Documentar consultas complejas
Manejo de Errores
- Implementar un manejo de errores consistente
- Registrar errores con suficiente contexto para diagnóstico
- Proporcionar mensajes de error amigables al usuario final
- Manejar adecuadamente las transacciones fallidas
Patrones de Diseño
- Usar el patrón Unit of Work para operaciones atómicas
- Implementar el patrón Repository para abstraer el acceso a datos
- Usar el patrón Specification para construir consultas complejas
- Aplicar el principio de responsabilidad única
Manejo de Errores
try
{
await _sqlRepository.ExecuteQueryAsync(query);
}
catch (SqlException ex)
{
// Manejo específico de errores de SQL
if (ex.Number == 2627) // Violación de restricción única
{
throw new DuplicateEntryException("Ya existe un registro con esta clave");
}
throw;
}
Optimización de Consultas
- Usar parámetros en lugar de concatenación de strings
- Implementar timeout razonable
- Usar índices apropiados
- Evitar SELECT * cuando no es necesario
- Usar procedimientos almacenados para consultas complejas
- Implementar paginación para listados grandes
- Usar transacciones solo cuando sea necesario
Soporte
Para reportar errores o solicitar características, por favor abre un issue en el repositorio de GitHub.
Licencia
Este proyecto está bajo licencia MIT. Consulta el archivo LICENSE para más detalles.
void CommitTransaction()
Confirma la transacción activa y cierra la conexión.void RollbackTransaction()
Revierte la transacción activa y cierra la conexión.
Consultas y ejecución
DataTable GetTableFromQuery(string query, Action<IDataParameterCollection> parametros = null)
Ejecuta una consulta SQL y devuelve unDataTable
con los resultados.DataTable GetTableFromStoredProcedure(string procedimientoAlmacenado, Action<IDataParameterCollection> parametros = null)
Ejecuta un procedimiento almacenado y devuelve unDataTable
.T GetItemFromQuery<T>(string query, Func<IDataReader, T> expression, Action<IDataParameterCollection> parametros = null)
Ejecuta una consulta y transforma la primera fila a un objeto del tipoT
.T GetItemFromStoredProcedure<T>(string procedimientoAlmacenado, Func<IDataReader, T> expression, Action<IDataParameterCollection> parametros = null)
Igual que el anterior, pero con procedimiento almacenado.ICollection<Dictionary<string, object>> GetDictionaryFromQuery(string query, Action<IDataParameterCollection> parametros = null)
Obtiene resultados de consulta como una colección de diccionarios con nombre/valor.ICollection<Dictionary<string, object>> GetDictionaryFromStoredProcedure(string procedimientoAlmacenado, Action<IDataParameterCollection> parametros = null)
Igual que el anterior, pero para procedimientos almacenados.ICollection<T> GetItemsFromStoredProcedure<T>(string procedimientoAlmacenado, Func<IDataReader, T> expression, Action<IDataParameterCollection> parametros = null)
Obtiene una colección de objetos tipoT
a partir de un procedimiento almacenado.ICollection<T> GetItemsFromQuery<T>(string query, Func<IDataReader, T> expression, Action<IDataParameterCollection> parametros = null)
Obtiene una colección de objetos tipoT
a partir de una consulta SQL.
Ejecución de comandos
void ExecuteNonQuery(string command, Action<IDataParameterCollection> parametros = null)
Ejecuta comandos SQL que no retornan datos (INSERT, UPDATE, DELETE, etc.).void ExecuteInsert<T>(string tableName, T entity) where T : class, new()
Inserta un único objeto en la tabla especificada usando reflexión.void ExecuteInsert<T>(string tableName, ICollection<T> collection) where T : class, new()
Inserta una colección de objetos en la tabla especificada.void ExecuteStoredProcedureCommand(string procedimientoAlmacenado, Action<IDataParameterCollection> parametros = null)
Ejecuta un procedimiento almacenado que no devuelve resultados.
Operaciones Bulk Copy
void ExecuteBulkCopyToTable(DataTable source, string destinationTable)
Borra la tabla destino, la crea según la estructura de la tabla fuente y realiza la inserción masiva.void ExecuteBulkCopy(DataTable source, string destinationTable)
Inserción masiva desde unDataTable
.void ExecuteBulkCopy(IDataReader source, string destinationTable)
Inserción masiva desde unIDataReader
.
Otros métodos útiles
DateTime GetCurrentDateTime()
Obtiene la fecha y hora actual del servidor SQL.Métodos para crear parámetros SQL para consultas y procedimientos almacenados (
AddParameter
).Métodos para configurar la conexión (
SetDatabaseLogon
).
Métodos Asíncronos de IMSSqlRepository
La interfaz también define métodos asíncronos para mejorar el rendimiento y escalabilidad de las operaciones de base de datos.
Métodos asíncronos principales
Task<T> GetItemFromQueryAsync<T>(string query, Func<IDataReader, T> expression, Action<IDataParameterCollection> parametros = null)
Ejecuta una consulta SQL asíncrona y transforma la primera fila en un objetoT
.Task<T> GetItemFromStoredProcedureAsync<T>(string procedimientoAlmacenado) where T : new()
Ejecuta un procedimiento almacenado de forma asíncrona y devuelve un objetoT
por defecto.Task<T> GetItemFromStoredProcedureAsync<T>(string procedimientoAlmacenado, Func<IDataReader, T> expression, Action<IDataParameterCollection> parametros = null)
Ejecuta un procedimiento almacenado de forma asíncrona y mapea la primera fila a un objetoT
.Task<DataTable> GetTableFromQueryAsync(string query, Action<IDataParameterCollection> parametros = null)
Ejecuta una consulta asíncrona y devuelve unDataTable
con los resultados.Task<DataTable> GetTableFromStoredProcedureAsync(string procedimientoAlmacenado, Action<IDataParameterCollection> parametros = null)
Ejecuta un procedimiento almacenado de forma asíncrona y devuelve unDataTable
.Task<ICollection<Dictionary<string, object>>> GetDictionaryFromQueryAsync(string query, Action<IDataParameterCollection> parametros = null)
Ejecuta una consulta asíncrona y devuelve una colección de diccionarios.Task<ICollection<Dictionary<string, object>>> GetDictionaryFromStoredProcedureAsync(string procedimientoAlmacenado, Action<IDataParameterCollection> parametros = null)
Igual que el anterior, pero para procedimientos almacenados.Task<ICollection<T>> GetItemsFromQueryAsync<T>(string query, Func<IDataReader, T> expression, Action<IDataParameterCollection> parametros = null)
Obtiene una colección de objetosT
de una consulta asíncrona.Task<ICollection<T>> GetItemsFromStoredProcedureAsync<T>(string storedProcedure) where T : new()
Obtiene una colección de objetosT
de un procedimiento almacenado asíncrono.Task<ICollection<T>> GetItemsFromStoredProcedureAsync<T>(string storedProcedure, Func<IDataReader, T> expression, Action<IDataParameterCollection> parametros = null)
Igual que el anterior, pero con mapeo personalizado.
Comandos asíncronos
Task<int> ExecuteStoredProcedureCommandAsync(string storedProcedure, Action<IDataParameterCollection> parametros = null)
Ejecuta un procedimiento almacenado asíncrono que devuelve el número de filas afectadas.Task ExecuteNonQueryAsync(string command, Action<IDataParameterCollection> parametros = null)
Ejecuta un comando SQL que no retorna datos de forma asíncrona.Task ExecuteBulkCopyToTableAsync(DataTable sourceTable, string destinationTable)
Realiza una inserción masiva asíncrona creando la tabla destino.Task ExecuteBulkCopyAsync(DataTable source, string destinationTable)
Inserción masiva asíncrona desde unDataTable
.Task ExecuteBulkCopyAsync(IDataReader source, string destinationTable)
Inserción masiva asíncrona desde unIDataReader
.Task ExecuteInsertAsync<T>(string tableName, T entity)
Inserta un objeto de forma asíncrona en la tabla especificada.Task ExecuteInsertAsync<T>(string tableName, ICollection<T> collection)
Inserta una colección de objetos de forma asíncrona.Task<DateTime> GetCurrentDateTimeAsync()
Obtiene la fecha y hora actual del servidor SQL de forma asíncrona.
Métodos para manejo de tablas en IMSSqlRepository
Además de los métodos para manipulación de datos, la interfaz incluye operaciones para gestionar tablas directamente:
void DropTable(string tableName)
Elimina la tabla especificada de la base de datos.void CreateTable(DataTable sourceTable, string destinationTable)
Crea una tabla en la base de datos a partir de la estructura de unDataTable
.void CreateTable(IDataReader reader, string destinationTable)
Crea una tabla en la base de datos basándose en la estructura delIDataReader
.
Interfaz IServiceProviderKeyed
Esta interfaz proporciona un mecanismo para obtener servicios basados en una clave específica:
public interface IServiceProviderKeyed
{
TService GetKeyedService<TService, TKeyed>(TKeyed key);
}
public void ConfigureServices(IServiceCollection services)
{
services.AddMicrosoftSQL(provider =>
{
// Aquí configuras la instancia concreta de IMSSqlRepository
IMSSqlRepository repo = new MSSqlRepository();
repo.SetDatabaseLogon("Server=myServer;Database=myDB;User Id=myUser;Password=myPassword;");
return repo;
}, ServiceLifetime.Scoped);
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
.NET Framework | net48 is compatible. net481 was computed. |
-
.NETFramework 4.8
- CoreUtilerias (>= 2025.7.24)
- Microsoft.Data.SqlClient (>= 6.0.2)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.6)
- System.Threading.Tasks (>= 4.3.0)
-
net8.0
- CoreUtilerias (>= 2025.7.24)
- Microsoft.Data.SqlClient (>= 6.0.2)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.6)
- System.Threading.Tasks (>= 4.3.0)
-
net9.0
- CoreUtilerias (>= 2025.7.24)
- Microsoft.Data.SqlClient (>= 6.0.2)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 9.0.6)
- System.Threading.Tasks (>= 4.3.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated | |
---|---|---|---|
2025.7.24 | 372 | 7/24/2025 | |
2025.7.21 | 422 | 7/22/2025 | |
2025.7.20 | 180 | 7/20/2025 | |
2025.7.19 | 178 | 7/20/2025 | |
2025.7.18 | 26 | 7/18/2025 | |
2025.7.17 | 111 | 7/17/2025 | |
2025.7.14 | 120 | 7/14/2025 | |
2025.7.13 | 118 | 7/14/2025 | |
2025.7.9 | 124 | 7/8/2025 | |
2025.7.8 | 119 | 7/8/2025 | |
2025.7.3 | 335 | 7/4/2025 | |
2025.7.2 | 338 | 7/4/2025 | |
2025.6.11 | 273 | 6/11/2025 | |
2025.5.26 | 90 | 5/24/2025 | |
2025.5.25 | 56 | 5/24/2025 | |
2025.5.24 | 55 | 5/24/2025 | |
2025.5.23 | 58 | 5/23/2025 | |
2025.5.20 | 126 | 5/21/2025 | |
2025.5.15 | 216 | 5/15/2025 | |
2025.5.8 | 148 | 5/8/2025 | |
2025.5.6 | 132 | 5/6/2025 | |
2025.5.4 | 137 | 5/4/2025 | |
2025.5.1 | 136 | 5/1/2025 | |
2025.4.30 | 134 | 4/30/2025 | |
2025.4.29 | 123 | 4/27/2025 | |
2025.4.28 | 82 | 4/26/2025 | |
2025.4.27 | 88 | 4/26/2025 | |
2025.4.26 | 88 | 4/26/2025 | |
2025.4.12 | 119 | 4/12/2025 | |
2025.4.4 | 96 | 4/5/2025 | |
2025.3.24 | 484 | 3/25/2025 | |
2025.3.22 | 153 | 3/22/2025 | |
2025.3.11 | 159 | 3/11/2025 | |
2025.3.9 | 165 | 3/10/2025 | |
2025.3.8 | 130 | 3/9/2025 | |
2025.2.16 | 210 | 2/17/2025 | |
2025.2.3 | 103 | 2/3/2025 | |
2025.2.2 | 113 | 2/2/2025 | |
2025.2.1 | 105 | 2/1/2025 | |
2025.1.31 | 92 | 1/29/2025 | |
2025.1.30 | 95 | 1/28/2025 | |
2025.1.29 | 95 | 1/28/2025 | |
2025.1.28 | 95 | 1/28/2025 | |
2025.1.18 | 98 | 1/15/2025 | |
2025.1.17 | 88 | 1/13/2025 | |
2025.1.16 | 80 | 1/13/2025 | |
2025.1.15 | 89 | 1/13/2025 | |
2025.1.14 | 81 | 1/13/2025 | |
2025.1.13 | 85 | 1/12/2025 | |
2025.1.12 | 83 | 1/12/2025 | |
2025.1.7 | 109 | 1/7/2025 | |
2024.12.29 | 107 | 12/28/2024 | |
2024.12.28 | 104 | 12/28/2024 | |
2024.12.19 | 109 | 12/19/2024 | |
2024.12.18 | 107 | 12/19/2024 | |
2024.12.4 | 121 | 12/4/2024 | |
2024.12.2 | 104 | 12/2/2024 | |
2024.11.30 | 111 | 12/1/2024 | |
2024.11.13 | 115 | 11/13/2024 | |
2024.11.12 | 110 | 11/12/2024 | |
2024.10.10 | 122 | 10/11/2024 | |
2024.10.9 | 109 | 10/9/2024 | |
2024.10.7 | 119 | 10/4/2024 | |
2024.10.6 | 102 | 10/4/2024 | |
2024.10.4 | 111 | 10/3/2024 | |
2024.9.8 | 101 | 9/9/2024 | |
2024.9.5 | 121 | 9/6/2024 | |
2024.8.13 | 136 | 8/14/2024 | |
2024.8.2 | 99 | 8/1/2024 | |
2024.8.1 | 93 | 8/1/2024 | |
2024.7.24 | 119 | 7/24/2024 | |
2024.6.9 | 107 | 6/10/2024 | |
2024.4.22 | 133 | 4/23/2024 | |
2024.4.16 | 123 | 4/15/2024 | |
2024.4.15 | 108 | 4/15/2024 | |
2024.4.14 | 133 | 4/14/2024 | |
2024.4.13 | 112 | 4/13/2024 | |
2024.4.12 | 115 | 4/13/2024 | |
2024.4.7 | 119 | 4/8/2024 | |
2024.2.18 | 128 | 2/24/2024 | |
2024.1.2 | 198 | 1/2/2024 | |
2023.12.26 | 136 | 12/26/2023 | |
2023.11.30 | 151 | 12/11/2023 | |
2023.11.28 | 128 | 11/27/2023 | |
2023.11.15 | 173 | 11/16/2023 | |
2023.11.10 | 155 | 11/10/2023 | |
2023.10.29 | 131 | 10/30/2023 | |
2023.8.17 | 158 | 8/18/2023 | |
2023.8.14 | 139 | 8/14/2023 | |
2023.8.9 | 144 | 8/9/2023 | |
2023.8.1 | 166 | 7/31/2023 | |
2023.7.31 | 162 | 7/31/2023 | |
2023.7.30 | 165 | 7/31/2023 | |
2023.7.26 | 172 | 7/26/2023 | |
2023.7.25 | 170 | 7/25/2023 | |
2023.7.11 | 176 | 7/12/2023 | |
2023.7.6 | 163 | 7/7/2023 | |
2023.6.19 | 171 | 6/20/2023 | |
2023.6.18 | 165 | 6/19/2023 | |
2023.5.20 | 173 | 5/16/2023 | |
2023.5.19 | 171 | 5/13/2023 | |
2023.5.14 | 168 | 5/8/2023 | |
2023.5.10 | 175 | 5/4/2023 | |
2023.5.6 | 191 | 4/29/2023 | |
2023.4.28 | 247 | 4/26/2023 |