Custom Database Provider

Altruist ships with PostgreSQL support. If you need a different database (MySQL, MongoDB, SQLite, etc.), implement IGeneralDatabaseProvider.

Implement the Provider

[Service(typeof(IGeneralDatabaseProvider))]
public class MyDbProvider : IGeneralDatabaseProvider
{
    public string ServiceName => "MyDB";
    public bool IsConnected { get; private set; }

    public event Action? OnConnected;
    public event Action<Exception>? OnFailed;
    public event Action<Exception>? OnRetryExhausted;

    public async Task ConnectAsync(int maxRetries = 30, int delayMilliseconds = 2000)
    {
        // Your connection logic — Altruist calls this at startup
        // Retry loop, then call RaiseConnectedEvent() on success
        IsConnected = true;
        RaiseConnectedEvent();
    }

    public async Task CreateTableAsync<TVaultModel>(IKeyspace? keyspace = null)
        where TVaultModel : class, IVaultModel
    {
        // Create table for the vault model
    }

    public async Task CreateTableAsync(Type entityType, IKeyspace? keyspace = null)
    {
        // Create table by type
    }

    public async Task CreateKeySpaceAsync(string keyspace, ReplicationOptions? options = null)
    {
        // Create schema/keyspace
    }

    public async Task ChangeKeyspaceAsync(string keyspace)
    {
        // Switch active schema
    }

    public void RaiseConnectedEvent() => OnConnected?.Invoke();
    public void RaiseFailedEvent(Exception ex) => OnFailed?.Invoke(ex);
    public void RaiseOnRetryExhaustedEvent(Exception ex) => OnRetryExhausted?.Invoke(ex);
}

Because IGeneralDatabaseProvider extends IConnectable, Altruist automatically monitors your provider's health during startup.

Create a Keyspace

public class MyKeyspace : IKeyspace
{
    public string Name { get; set; } = "myapp";
}

Custom Vault (Optional)

If your database needs custom query behavior, implement IVault<T>:

public class MyVault<T> : IVault<T> where T : class, IVaultModel
{
    // Implement Where, OrderBy, Save, SaveBatchAsync, Delete, etc.
}

And register a custom service factory so IVault<T> resolves to your implementation:

[ConditionalOnConfig("altruist:persistence:database:provider", havingValue: "mydb")]
public class MyDbServiceFactory : IServiceFactory
{
    public bool CanCreate(Type serviceType)
    {
        if (!serviceType.IsGenericType) return false;
        return serviceType.GetGenericTypeDefinition() == typeof(IVault<>);
    }

    public object Create(IServiceProvider sp, Type serviceType)
    {
        var modelType = serviceType.GetGenericArguments()[0];
        var vaultType = typeof(MyVault<>).MakeGenericType(modelType);
        return ActivatorUtilities.CreateInstance(sp, vaultType);
    }
}

Using Your Provider

Once registered, IVault<T> injection works automatically:

[Service]
public class MyService
{
    private readonly IVault<PlayerVault> _players;

    public MyService(IVault<PlayerVault> players)
    {
        _players = players;
    }
}

Note:

The framework resolves which provider to use based on your config.yml. Set provider: mydb and your factory handles the rest.