Class CharacterMailService
Service for managing character mail in the database. Provides async operations for CRUD operations on character mail data. Implements execution strategies for automatic retry on transient database failures. Returns DatabaseResult for consistent, safe error handling.
public sealed class CharacterMailService : BaseService<CharacterMailEntity>, ICharacterMailService, ICountByKeyAction<long>, IDeleteByKeyVersionedAction<long>, IFetchCollectionByKeyAction<long, CharacterMailData>
- Inheritance
-
CharacterMailService
- Implements
- Inherited Members
Remarks
This service manages character mail including:
- Sending new mail with atomic INSERT operations
- Mail deletion (individual and bulk by character)
- Mail retrieval and count queries
All exceptions are classified by BaseService and mapped to DatabaseResult error codes
(e.g., UNIQUE_VIOLATION, FOREIGN_KEY_VIOLATION, STALE_STATE, DATABASE_ERROR). Transient failures are retried automatically.
Methods return DatabaseResult to provide structured error handling
without throwing exceptions to calling code.
Constructors
CharacterMailService(INpgsqlDbContextFactory)
Initializes a new instance of the CharacterMailService class.
public CharacterMailService(INpgsqlDbContextFactory dbContextFactory)
Parameters
dbContextFactoryINpgsqlDbContextFactoryFactory for creating database contexts.
Exceptions
- ArgumentNullException
Thrown when dbContextFactory is null.
Methods
ClaimAttachmentAsync(long, long, long, CancellationToken)
Takes the attachment off one mail, returning what was on it.
public Task<DatabaseResult<CharacterMailAttachmentData?>> ClaimAttachmentAsync(long mailId, long characterId, long incomingVersion, CancellationToken cancellationToken = default)
Parameters
mailIdlongThe mail to claim from.
characterIdlongThe owning character ID, for authorization.
incomingVersionlongThe authoritative, monotonic version for this write.
cancellationTokenCancellationTokenToken to cancel the operation.
Returns
- Task<DatabaseResult<CharacterMailAttachmentData?>>
The attachment as it stood before the clear, or no data when the mail had nothing attached, does not belong to this character, or has already been claimed.
Remarks
Read and clear are one statement. The attachment columns are zeroed and their
previous values returned by the same UPDATE, whose WHERE additionally
requires that something is still attached. Two claims racing on one mail therefore
serialise on the row lock and only the first affects a row — the second matches nothing
and returns no data. Fetching the mail and then clearing it would leave a window in
which both reads see the same item and both callers grant it.
The caller grants what comes back. That ordering means a crash between the clear and the grant loses the attachment rather than duplicating it, which is the correct direction for an authoritative server — the same reasoning the merchant and corpse loot paths use.
CountAsync(long, CancellationToken)
Counts items for the given key.
public Task<DatabaseResult<int>> CountAsync(long characterId, CancellationToken cancellationToken = default)
Parameters
characterIdlongcancellationTokenCancellationTokenToken to cancel the operation.
Returns
DeleteAsync(long, long, long, CancellationToken)
Deletes a specific mail message by its ID if incomingVersion is newer.
public Task<DatabaseResult> DeleteAsync(long mailId, long characterId, long incomingVersion, CancellationToken cancellationToken = default)
Parameters
mailIdlongThe mail ID to delete.
characterIdlongThe owning character ID (for authorization).
incomingVersionlongThe authoritative, monotonic version for this delete operation.
cancellationTokenCancellationTokenToken to cancel the operation.
Returns
- Task<DatabaseResult>
A DatabaseResult indicating success or failure.
DeleteAsync(long, long, CancellationToken)
Deletes the entity identified by the given key if incomingVersion is newer.
public Task<DatabaseResult> DeleteAsync(long characterId, long incomingVersion, CancellationToken cancellationToken = default)
Parameters
characterIdlongincomingVersionlongThe authoritative, monotonic version for this delete operation.
cancellationTokenCancellationTokenToken to cancel the operation.
Returns
FetchAsync(long, CancellationToken)
Fetches a collection of items for the given key.
public Task<DatabaseResult<IReadOnlyList<CharacterMailData>>> FetchAsync(long characterId, CancellationToken cancellationToken = default)
Parameters
characterIdlongcancellationTokenCancellationTokenToken to cancel the operation.
Returns
SendAsync(long, string, long, string, string, int, int, uint, int, long, CancellationToken)
Sends a new mail message from one character to another.
public Task<DatabaseResult> SendAsync(long senderCharacterId, string senderName, long recipientCharacterId, string subject, string body, int itemAttachmentTemplateID, int itemAttachmentSeed, uint itemAttachmentAmount, int currencyAttachment, long incomingVersion, CancellationToken cancellationToken = default)
Parameters
senderCharacterIdlongThe sending character ID.
senderNamestringThe display name of the sender.
recipientCharacterIdlongThe recipient character ID.
subjectstringThe mail subject.
bodystringThe mail body text.
itemAttachmentTemplateIDintThe attached item template ID (0 for none).
itemAttachmentSeedintThe attached item seed (0 for none).
itemAttachmentAmountuintThe attached item amount (0 for none).
currencyAttachmentintincomingVersionlongThe authoritative, monotonic version for this persist operation.
cancellationTokenCancellationTokenToken to cancel the operation.
Returns
- Task<DatabaseResult>
A DatabaseResult indicating success or failure.