diff --git a/Domca.Core/Repositories/IHydrationRepository.cs b/Domca.Core/Repositories/IHydrationRepository.cs new file mode 100644 index 0000000..30b8508 --- /dev/null +++ b/Domca.Core/Repositories/IHydrationRepository.cs @@ -0,0 +1,115 @@ +using Domca.Core.Entities; +using Domca.Core.Entities.IDs; + +namespace Domca.Core.Repositories; + +/// +/// Defines a contract for managing and retrieving hydration records within a repository. +/// +/// This interface provides asynchronous methods for querying hydration record and synchronous methods +/// for adding, updating, and removing hydration records. Implementations are expected to handle data persistence and ensure +/// thread safety where appropriate. Methods that accept a allow callers to cancel +/// ongoing operations, which is useful for long-running queries or when integrating with responsive +/// applications. +public interface IHydrationRepository +{ + // Read Methods + #region Read Methods + + /// + /// Asynchronously retrieves a hydration record by its unique identifier. + /// + /// The unique identifier of the hydration record to retrieve. + /// A cancellation token that can be used to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. The task result contains the hydration record if found; + /// otherwise, null. + Task GetByIdAsync(HydrationRecordId id, CancellationToken cancellationToken = default); + + /// + /// Retrieves the hydration record associated with the specified user, if one exists. + /// + /// The identifier of the user whose hydration record is to be retrieved. Cannot be null. + /// A cancellation token that can be used to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. The task result contains the hydration record for the + /// specified user, or null if no record is found. + Task?> GetByUserAsync(UserId id, CancellationToken cancellationToken = default); + + /// + /// Retrieves all hydration records associated with the specified user. + /// + /// The unique identifier of the user whose hydration records are to be retrieved. + /// A cancellation token that can be used to cancel the operation. + /// A list of hydration records for the specified user. The list is empty if the user has no hydration records. + Task?> GetByUserForTodayAsync(UserId id, CancellationToken cancellationToken = default); + + /// + /// Retrieves all hydration records for the specified user within the week containing the given reference date. + /// + /// The week is defined as starting on Monday and ending before the following Monday, based on + /// the provided reference date. + /// The unique identifier of the user whose hydration records are to be retrieved. + /// A date used to determine the target week. The method returns records from the week that includes this date, + /// starting on Monday. + /// A cancellation token that can be used to cancel the asynchronous operation. + /// A list of hydration records for the specified user that fall within the week containing the reference date. The + /// list is empty if no records are found. + Task?> GetByUserForWeekAsync(UserId id, DateTime referenceDate, CancellationToken cancellationToken = default); + + /// + /// Retrieves all hydration records for the specified user within the given month and year. + /// + /// The unique identifier of the user whose hydration records are to be retrieved. + /// The month for which to retrieve records, specified as an integer from 1 (January) to 12 (December). + /// The year for which to retrieve records. + /// A cancellation token that can be used to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. The task result contains a list of hydration records for the + /// specified user and month, or if no records are found. + Task?> GetByUserForMonthAsync(UserId id, int month, int year, CancellationToken cancellationToken = default); + + /// + /// Retrieves all hydration records for the specified user and year asynchronously. + /// + /// The unique identifier of the user whose hydration records are to be retrieved. + /// The year for which to retrieve hydration records. Must be a four-digit year. + /// A cancellation token that can be used to cancel the asynchronous operation. + /// A list of hydration records for the specified user and year, or if no records are found. + Task?> GetByUserForYearAsync(UserId id, int year, CancellationToken cancellationToken = default); + + #endregion + + // Write Methods + #region Write Methods + + /// + /// Adds a hydration record to the data context for tracking and persistence. + /// + /// The record is not saved to the database until changes are committed. This method does not + /// check for duplicate records. + /// The hydration record to add to the context. Cannot be null. + void Add(HydrationRecord hydrationRecord); + + /// + /// Adds a collection of hydration records to the data context for insertion. + /// + /// This method stages the specified hydration records for addition to the underlying data store. + /// Changes are not persisted until the context is saved. If any record in the collection already exists in the + /// context, it may result in duplicate entries unless handled appropriately. + /// The collection of objects to add. Cannot be null. + void AddRange(List hydrationRecords); + + /// + /// Updates the specified hydration record in the data store. + /// + /// If the specified record does not exist in the data store, no changes will be made. This + /// method does not save changes to the database; call SaveChanges to persist updates. + /// The hydration record to update. Must not be null. + void Update(HydrationRecord hydrationRecord); + + /// + /// Removes the specified hydration record from the data context. + /// + /// The hydration record to remove from the context. Cannot be null. + void Remove(HydrationRecord hydrationRecord); + + #endregion +} diff --git a/Domca.EntityFrameworkCore/Repositories/HydrationRepository.cs b/Domca.EntityFrameworkCore/Repositories/HydrationRepository.cs new file mode 100644 index 0000000..e7b83dc --- /dev/null +++ b/Domca.EntityFrameworkCore/Repositories/HydrationRepository.cs @@ -0,0 +1,134 @@ +using Domca.Core.Entities; +using Domca.Core.Entities.IDs; +using Domca.Core.Repositories; +using Microsoft.EntityFrameworkCore; + +namespace Domca.EntityFrameworkCore.Repositories; + +/// +/// Provides access to hydration-related data operations using the specified data context. +/// +/// The data context used to interact with the underlying hydration data store. Cannot be null. +public class HydrationRepository(DataContext context) : IHydrationRepository +{ + /// + /// Asynchronously retrieves a hydration record by its unique identifier. + /// + /// The unique identifier of the hydration record to retrieve. + /// A cancellation token that can be used to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. The task result contains the hydration record if found; + /// otherwise, null. + public async Task GetByIdAsync(HydrationRecordId id, CancellationToken cancellationToken = default) + => await context.HydrationRecords.FindAsync([id], cancellationToken); + + /// + /// Asynchronously retrieves all hydration records associated with the specified user. + /// + /// The unique identifier of the user whose hydration records are to be retrieved. + /// A cancellation token that can be used to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. The task result contains a list of hydration records for the specified user. The list is empty if the user has no hydration records. + public async Task?> GetByUserAsync(UserId id, CancellationToken cancellationToken = default) + => await context.HydrationRecords + .Where(h => h.UserId == id) + .ToListAsync(cancellationToken); + + /// + /// Asynchronously retrieves all hydration records for the specified user that were created on the current UTC date. + /// + /// The method compares record dates using UTC time. If called near midnight UTC, results may + /// differ from local time expectations. + /// The unique identifier of the user whose hydration records are to be retrieved. + /// A cancellation token that can be used to cancel the asynchronous operation. + /// A list of hydration records for the specified user for today, or if no records are found. + public async Task?> GetByUserForTodayAsync(UserId id, CancellationToken cancellationToken = default) + => await context.HydrationRecords + .Where(h => h.UserId == id + && h.Date.Date == DateTime.UtcNow.Date) + .ToListAsync(cancellationToken); + + /// + /// Retrieves all hydration records for the specified user within the week containing the given reference date. + /// + /// The week is defined as starting on Monday and ending before the following Monday, based on + /// the provided reference date. + /// The unique identifier of the user whose hydration records are to be retrieved. + /// A date used to determine the target week. The method returns records from the week that includes this date, + /// starting on Monday. + /// A cancellation token that can be used to cancel the asynchronous operation. + /// A list of hydration records for the specified user that fall within the week containing the reference date. The + /// list is empty if no records are found. + public async Task?> GetByUserForWeekAsync(UserId id, DateTime referenceDate, CancellationToken cancellationToken = default) + { + int diff = (7 + (referenceDate.DayOfWeek - DayOfWeek.Monday)) % 7; + + var startOfWeek = referenceDate.Date.AddDays(-1 * diff); + + var endOfWeek = startOfWeek.AddDays(7); + + return await context.HydrationRecords + .Where(h => h.UserId == id + && h.Date >= startOfWeek + && h.Date < endOfWeek) + .ToListAsync(cancellationToken); + } + + /// + /// Retrieves all hydration records for the specified user within the given month and year. + /// + /// The unique identifier of the user whose hydration records are to be retrieved. + /// The month for which to retrieve records, specified as an integer from 1 (January) to 12 (December). + /// The year for which to retrieve records. + /// A cancellation token that can be used to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. The task result contains a list of hydration records for the + /// specified user and month, or if no records are found. + public async Task?> GetByUserForMonthAsync(UserId id, int month, int year, CancellationToken cancellationToken = default) + => await context.HydrationRecords + .Where(h => h.UserId == id + && h.Date.Month == month + && h.Date.Year == year) + .ToListAsync(cancellationToken); + + /// + /// Retrieves all hydration records for the specified user and year. + /// + /// The unique identifier of the user whose hydration records are to be retrieved. + /// The year for which to retrieve hydration records. Must be a four-digit year. + /// A cancellation token that can be used to cancel the asynchronous operation. + /// A list of hydration records for the specified user and year, or if no records are found. + public async Task?> GetByUserForYearAsync(UserId id, int year, CancellationToken cancellationToken = default) + => await context.HydrationRecords + .Where(h => h.UserId == id + && h.Date.Year == year) + .ToListAsync(cancellationToken); + + /// + /// Adds a hydration record to the data context for tracking and persistence. + /// + /// The record is not saved to the database until changes are committed. This method does not + /// check for duplicate records. + /// The hydration record to add to the context. Cannot be null. + public void Add(HydrationRecord hydrationRecord) => context.HydrationRecords.Add(hydrationRecord); + + /// + /// Adds a collection of hydration records to the data context for insertion. + /// + /// This method stages the specified hydration records for addition to the underlying data store. + /// Changes are not persisted until the context is saved. If any record in the collection already exists in the + /// context, it may result in duplicate entries unless handled appropriately. + /// The collection of objects to add. Cannot be null. + public void AddRange(List hydrationRecords) => context.HydrationRecords.AddRange(hydrationRecords); + + /// + /// Updates the specified hydration record in the data store. + /// + /// If the specified record does not exist in the data store, no changes will be made. This + /// method does not save changes to the database; call SaveChanges to persist updates. + /// The hydration record to update. Must not be null. + public void Update(HydrationRecord hydrationRecord) => context.HydrationRecords.Update(hydrationRecord); + + /// + /// Removes the specified hydration record from the data context. + /// + /// The hydration record to remove from the context. Cannot be null. + public void Remove(HydrationRecord hydrationRecord) => context.HydrationRecords.Remove(hydrationRecord); +} \ No newline at end of file diff --git a/Domca.EntityFrameworkCore/Repositories/UserRepository.cs b/Domca.EntityFrameworkCore/Repositories/UserRepository.cs index b94054f..226c55d 100644 --- a/Domca.EntityFrameworkCore/Repositories/UserRepository.cs +++ b/Domca.EntityFrameworkCore/Repositories/UserRepository.cs @@ -24,9 +24,7 @@ public sealed class UserRepository(DataContext context) : IUserRepository /// A task that represents the asynchronous operation. The task result contains the user entity if found; otherwise, /// . public async Task GetByIdAsync(UserId id, CancellationToken cancellationToken = default) - { - return await context.Users.FindAsync([id], cancellationToken); - } + => await context.Users.FindAsync([id], cancellationToken); /// /// Asynchronously retrieves a user whose normalized email address matches the specified value. @@ -37,10 +35,8 @@ public sealed class UserRepository(DataContext context) : IUserRepository /// A task that represents the asynchronous operation. The task result contains the user whose normalized email /// address matches the specified value, or if no such user is found. public async Task GetByEmailAsync(string email, CancellationToken cancellationToken = default) - { - return await context.Users - .FirstOrDefaultAsync(u => u.EmailAddressNormalized == email.ToUpperInvariant(), cancellationToken); - } + => await context.Users + .FirstOrDefaultAsync(u => u.EmailAddressNormalized == email.ToUpperInvariant(), cancellationToken); /// /// Asynchronously determines whether the specified email address is not already associated with an existing user. @@ -52,10 +48,8 @@ public sealed class UserRepository(DataContext context) : IUserRepository /// A task that represents the asynchronous operation. The task result is if the email /// address is unique; otherwise, . public async Task IsEmailUniqueAsync(string email, CancellationToken cancellationToken = default) - { - return !await context.Users - .AnyAsync(u => u.EmailAddressNormalized == email.ToUpperInvariant(), cancellationToken); - } + => !await context.Users + .AnyAsync(u => u.EmailAddressNormalized == email.ToUpperInvariant(), cancellationToken); #endregion