Table of Contents

Class CharacterFriendService

Namespace
FishMMO.Database.Npgsql.Services
Assembly
FishMMO-DB.dll

Service for managing character friend relationships in the database. Provides async operations for CRUD operations on character friend data. Implements execution strategies for automatic retry on transient database failures. Returns DatabaseResult for consistent, safe error handling.

public sealed class CharacterFriendService : BaseService<CharacterFriendEntity>, ICharacterFriendService, ICountByKeyAction<long>, IDeleteByKeyVersionedAction<long>, IFetchCollectionByKeyAction<long, CharacterFriendData>
Inheritance
CharacterFriendService
Implements
Inherited Members

Remarks

This service manages character friend relationships including:

  • Friend relationship creation with atomic INSERT operations
  • Friend deletion (individual and bulk)
  • Friend 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

CharacterFriendService(INpgsqlDbContextFactory)

Initializes a new instance of the CharacterFriendService class.

public CharacterFriendService(INpgsqlDbContextFactory dbContextFactory)

Parameters

dbContextFactory INpgsqlDbContextFactory

Factory 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 characterId, CancellationToken cancellationToken = default)

Parameters

characterId long
cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult<int>>

DeleteAsync(long, long, long, CancellationToken)

Deletes a friend relationship for the specified character if incomingVersion is newer.

public Task<DatabaseResult> DeleteAsync(long characterId, long friendCharacterId, long incomingVersion, CancellationToken cancellationToken = default)

Parameters

characterId long

The owning character ID.

friendCharacterId long

The friend character ID.

incomingVersion long

The authoritative, monotonic version for this delete operation.

cancellationToken CancellationToken

Token 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

characterId long
incomingVersion long

The authoritative, monotonic version for this delete operation.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

FetchAsync(long, CancellationToken)

Fetches a collection of items for the given key.

public Task<DatabaseResult<IReadOnlyList<CharacterFriendData>>> FetchAsync(long characterId, CancellationToken cancellationToken = default)

Parameters

characterId long
cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult<IReadOnlyList<CharacterFriendData>>>

IsBlockedAsync(long, long, CancellationToken)

Determines whether characterId has blocked otherCharacterId.

public Task<DatabaseResult<bool>> IsBlockedAsync(long characterId, long otherCharacterId, CancellationToken cancellationToken = default)

Parameters

characterId long

The character who may own a block entry.

otherCharacterId long

The character who may be blocked.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult<bool>>

A DatabaseResult<T> that is true when an active row exists marking otherCharacterId as blocked by characterId.

Remarks

The is_blocked column has existed since the friend table was introduced and nothing has ever read it — every block a player recorded was written and then ignored, so blocking someone did not stop them inviting or whispering. This is the read side. The check is deliberately one-directional: it answers "has A blocked B", so the caller must ask it about the TARGET of an unwanted action, not about the initiator.

PersistAsync(long, long, long, bool, CancellationToken)

Persists a friend or block relationship for the specified character.

public Task<DatabaseResult> PersistAsync(long characterId, long friendCharacterId, long incomingVersion, bool isBlocked, CancellationToken cancellationToken = default)

Parameters

characterId long

The owning character ID.

friendCharacterId long

The friend character ID.

incomingVersion long

The authoritative, monotonic version for this persist operation.

isBlocked bool

When true the relationship is a block; when false it is a friend.

cancellationToken CancellationToken

Token to cancel the operation.

Returns

Task<DatabaseResult>

A DatabaseResult indicating success or failure.