Class CharacterPartyService
Service for managing character party membership in the database. Provides async operations for CRUD operations on character party data. Implements execution strategies for automatic retry on transient database failures. Returns DatabaseResult for consistent, safe error handling.
public sealed class CharacterPartyService : BaseService<CharacterPartyEntity>, ICharacterPartyService, ICountByKeyAction<long>, IDeleteByKeyVersionedAction<long>, IFetchByKeyAction<long, CharacterPartyData?>, IFetchManyByKeyAction<long, CharacterPartyData>
- Inheritance
-
CharacterPartyService
- Implements
- Inherited Members
Remarks
This service manages character party memberships including:
- Party membership save/update with atomic UPSERT operations
- Rank updates
- Party membership deletion
- Party membership and member retrieval
Database operations are executed via the BaseService execution wrappers for:
- Automatic transient failure retry
- Centralized exception handling and mapping
- Consistent DatabaseResult pattern
When a write requires multiple database statements, it should be wrapped in ExecuteTransactionAsync(Func<NpgsqlDbContext, Task>, bool, string?, CancellationToken). Single-statement SQL operations (including CTE-based UPSERT/DELETE/UPDATE) are executed atomically without requiring an explicit transaction wrapper.
Constructors
CharacterPartyService(INpgsqlDbContextFactory)
Initializes a new instance of the CharacterPartyService class.
public CharacterPartyService(INpgsqlDbContextFactory dbContextFactory)
Parameters
dbContextFactoryINpgsqlDbContextFactoryFactory for creating database contexts.
Exceptions
- ArgumentNullException
Thrown when dbContextFactory is null.
Methods
CountAsync(long, CancellationToken)
Counts items for the given key.
public Task<DatabaseResult<int>> CountAsync(long partyId, CancellationToken cancellationToken = default)
Parameters
partyIdlongcancellationTokenCancellationTokenToken to cancel the operation.
Returns
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 an entity for the given key.
public Task<DatabaseResult<CharacterPartyData?>> FetchAsync(long characterId, CancellationToken cancellationToken = default)
Parameters
characterIdlongcancellationTokenCancellationTokenToken to cancel the operation.
Returns
FetchManyAsync(long, CancellationToken)
Fetches many items for the given key.
public Task<DatabaseResult<IReadOnlyList<CharacterPartyData>>> FetchManyAsync(long partyId, CancellationToken cancellationToken = default)
Parameters
partyIdlongcancellationTokenCancellationTokenToken to cancel the operation.
Returns
FetchOnlineMemberIdsAsync(long, CancellationToken)
Returns the party's members who currently hold a live session.
public Task<DatabaseResult<IReadOnlyList<long>>> FetchOnlineMemberIdsAsync(long partyId, CancellationToken cancellationToken = default)
Parameters
partyIdlongThe party to inspect.
cancellationTokenCancellationTokenToken to cancel the operation.
Returns
- Task<DatabaseResult<IReadOnlyList<long>>>
The character IDs of members with a live session; empty when none are online.
Remarks
Exists so party leadership can be repaired convergently rather than only on the events that break it. A scene server knows which characters IT hosts and nothing about the rest of the shard, so an absent leader — one who disconnected, or crashed, or whose server died — is invisible to every server that could do something about it. Without this the party is stuck: it HAS a leader, so nothing that merely counts leaders sees a problem, and that leader is not there to invite, kick, promote, or close the instance the party is holding open.
"Online" is the same definition the account session checks use, and it is the strict one on purpose. A lapsed lease does not count, which is what lets a party recover from a scene server dying rather than waiting for it to come back. A character running out a combat-logout timer does not count either: its session is still claimed so its body stays authoritative, but the player is gone, and leadership must follow the player.
PersistAsync(CharacterPartyData, int, CancellationToken)
Persists the provided party membership data, enforcing capacity limits.
public Task<DatabaseResult> PersistAsync(CharacterPartyData partyData, int maxCapacity, CancellationToken cancellationToken = default)
Parameters
partyDataCharacterPartyDataThe party membership data to persist.
maxCapacityintThe maximum number of members allowed in the party.
cancellationTokenCancellationTokenToken to cancel the operation.
Returns
- Task<DatabaseResult>
A DatabaseResult indicating success or failure.
UpdateRankAsync(long, long, byte, long, CancellationToken)
Updates a character's party rank if incomingVersion is newer.
public Task<DatabaseResult> UpdateRankAsync(long characterId, long partyId, byte rank, long incomingVersion, CancellationToken cancellationToken = default)
Parameters
characterIdlongThe character ID.
partyIdlongThe party ID.
rankbyteThe new rank.
incomingVersionlongThe authoritative, monotonic version for this update operation.
cancellationTokenCancellationTokenToken to cancel the operation.
Returns
- Task<DatabaseResult>
A DatabaseResult indicating success or failure.