Table of Contents

Class ServiceCollectionExtensions

Namespace
Savvyio.Extensions.DependencyInjection.Dapper
Assembly
Savvyio.Extensions.DependencyInjection.Dapper.dll

Extension methods for the IServiceCollection interface.

public static class ServiceCollectionExtensions
Inheritance
ServiceCollectionExtensions

Examples

Wiring Dapper persistence into Savvy I/O requires two sequential registrations: AddDapperDataSource to register the connection factory and IDapperDataSource, and AddDapperDataStore to bind each data store implementation to its service interface. The connection factory lambda must return an IDbConnection instance; the data source must be registered before the data stores. The example builds a provider with both registrations and confirms the data store resolves to the expected concrete type.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Savvyio;
using Savvyio.Data;
using Savvyio.Extensions.Dapper;
using Savvyio.Extensions.DependencyInjection;
using Savvyio.Extensions.DependencyInjection.Dapper;

namespace ExampleApp;

public sealed class DapperRegistrationExample
{
    public ServiceProvider BuildProvider()
    {
        var services = new ServiceCollection();
        services.AddMarshaller<FakeMarshaller>(p => new FakeMarshaller());
        services.AddDapperDataSource(options => options.ConnectionFactory = () => new FakeDbConnection());
        services.AddDapperDataStore<OrderProjectionStore, OrderProjection>();

        return services.BuildServiceProvider();
    }

    public bool HasExpectedRegistration(ServiceProvider provider)
    {
        var source = provider.GetRequiredService<IDapperDataSource>();
        var store = provider.GetRequiredService<IPersistentDataStore<OrderProjection, DapperQueryOptions>>();

        return source is DapperDataSource && store is OrderProjectionStore;
    }
}

public sealed class OrderProjection
{
    public long Id { get; set; }

    public string Status { get; set; } = string.Empty;
}

public sealed class OrderProjectionStore : DapperDataStore<OrderProjection, DapperQueryOptions>
{
    public OrderProjectionStore(IDapperDataSource source) : base(source)
    {
    }

    public override Task CreateAsync(OrderProjection dto, Action<Cuemon.Threading.AsyncOptions>? setup = null)
    {
        return Task.CompletedTask;
    }

    public override Task UpdateAsync(OrderProjection dto, Action<Cuemon.Threading.AsyncOptions>? setup = null)
    {
        return Task.CompletedTask;
    }

    public override Task<OrderProjection> GetByIdAsync(object id, Action<Cuemon.Threading.AsyncOptions>? setup = null)
    {
        return Task.FromResult(new OrderProjection { Id = Convert.ToInt64(id), Status = "Pending" });
    }

    public override Task<IEnumerable<OrderProjection>> FindAllAsync(Action<DapperQueryOptions>? setup = null)
    {
        return Task.FromResult<IEnumerable<OrderProjection>>(Array.Empty<OrderProjection>());
    }

    public override Task DeleteAsync(OrderProjection dto, Action<Cuemon.Threading.AsyncOptions>? setup = null)
    {
        return Task.CompletedTask;
    }
}

public sealed class FakeDbConnection : IDbConnection
{
    private ConnectionState _state;

    public string ConnectionString { get; set; } = "Data Source=fake;";

    public int ConnectionTimeout => 30;

    public string Database => "Fake";

    public ConnectionState State => _state;

    public IDbTransaction BeginTransaction()
    {
        return new FakeDbTransaction(this, IsolationLevel.ReadCommitted);
    }

    public IDbTransaction BeginTransaction(IsolationLevel il)
    {
        return new FakeDbTransaction(this, il);
    }

    public void ChangeDatabase(string databaseName)
    {
    }

    public void Close()
    {
        _state = ConnectionState.Closed;
    }

    public IDbCommand CreateCommand()
    {
        return new FakeDbCommand(this);
    }

    public void Open()
    {
        _state = ConnectionState.Open;
    }

    public void Dispose()
    {
        Close();
    }
}

public sealed class FakeDbTransaction : IDbTransaction
{
    public FakeDbTransaction(IDbConnection connection, IsolationLevel isolationLevel)
    {
        Connection = connection;
        IsolationLevel = isolationLevel;
    }

    public IDbConnection Connection { get; }

    public IsolationLevel IsolationLevel { get; }

    public void Commit()
    {
    }

    public void Rollback()
    {
    }

    public void Dispose()
    {
    }
}

public sealed class FakeDbCommand : IDbCommand
{
    public FakeDbCommand(IDbConnection connection)
    {
        Connection = connection;
        Parameters = new FakeParameterCollection();
    }

    public string CommandText { get; set; } = string.Empty;

    public int CommandTimeout { get; set; }

    public CommandType CommandType { get; set; }

    public IDbConnection Connection { get; set; }

    public IDataParameterCollection Parameters { get; }

    public IDbTransaction? Transaction { get; set; }

    public UpdateRowSource UpdatedRowSource { get; set; }

    public void Cancel()
    {
    }

    public IDbDataParameter CreateParameter()
    {
        throw new NotSupportedException();
    }

    public void Dispose()
    {
    }

    public int ExecuteNonQuery()
    {
        throw new NotSupportedException();
    }

    public IDataReader ExecuteReader()
    {
        throw new NotSupportedException();
    }

    public IDataReader ExecuteReader(CommandBehavior behavior)
    {
        throw new NotSupportedException();
    }

    public object ExecuteScalar()
    {
        throw new NotSupportedException();
    }

    public void Prepare()
    {
    }
}

public sealed class FakeParameterCollection : List<object>, IDataParameterCollection
{
    public object this[string parameterName]
    {
        get => throw new NotSupportedException();
        set => throw new NotSupportedException();
    }

    public bool Contains(string parameterName)
    {
        return false;
    }

    public int IndexOf(string parameterName)
    {
        return -1;
    }

    public void RemoveAt(string parameterName)
    {
    }
}

public sealed class FakeMarshaller : IMarshaller
{
    public Stream Serialize<TValue>(TValue value) => Stream.Null;
    public Stream Serialize(object value, Type inputType) => Stream.Null;
    public TValue Deserialize<TValue>(Stream data) => default!;
    public object Deserialize(Stream data, Type returnType) => null!;
}

Methods

AddDapperDataSource(IServiceCollection, Action<DapperDataSourceOptions>, Action<ServiceOptions>)

Adds an DapperDataSource implementation to the specified IServiceCollection.

public static IServiceCollection AddDapperDataSource(this IServiceCollection services, Action<DapperDataSourceOptions> dataSourceSetup, Action<ServiceOptions> serviceSetup = null)

Parameters

services IServiceCollection

The IServiceCollection to extend.

dataSourceSetup Action<DapperDataSourceOptions>

The DapperDataSourceOptions that needs to be configured.

serviceSetup Action<ServiceOptions>

The ServiceOptions which may be configured.

Returns

IServiceCollection

A reference to services so that additional calls can be chained.

Remarks

The implementation will be type forwarded accordingly.

Exceptions

ArgumentNullException

services cannot be null.

AddDapperDataSource<TService>(IServiceCollection, Action<ServiceOptions>)

Adds an implementation of IDapperDataSource to the specified IServiceCollection.

public static IServiceCollection AddDapperDataSource<TService>(this IServiceCollection services, Action<ServiceOptions> setup = null) where TService : class, IDapperDataSource

Parameters

services IServiceCollection

The IServiceCollection to add the service to.

setup Action<ServiceOptions>

The ServiceOptions which may be configured.

Returns

IServiceCollection

A reference to services so that additional configuration calls can be chained.

Type Parameters

TService

The type of the IDapperDataSource interface to add.

Remarks

If the underlying type of TService implements IDependencyInjectionMarker<TMarker> interface then this is automatically handled. Also, the implementation will be type forwarded accordingly.

Exceptions

ArgumentNullException

services cannot be null.

See Also

AddDapperDataSource<TMarker>(IServiceCollection, Action<DapperDataSourceOptions<TMarker>>, Action<ServiceOptions>)

Adds an DapperDataSource<TMarker> implementation to the specified IServiceCollection.

public static IServiceCollection AddDapperDataSource<TMarker>(this IServiceCollection services, Action<DapperDataSourceOptions<TMarker>> dataSourceSetup, Action<ServiceOptions> serviceSetup = null)

Parameters

services IServiceCollection

The IServiceCollection to extend.

dataSourceSetup Action<DapperDataSourceOptions<TMarker>>

The DapperDataSourceOptions<TMarker> that needs to be configured.

serviceSetup Action<ServiceOptions>

The ServiceOptions which may be configured.

Returns

IServiceCollection

A reference to services so that additional calls can be chained.

Type Parameters

TMarker

Remarks

The implementation will be type forwarded accordingly.

Exceptions

ArgumentNullException

services cannot be null.

AddDapperDataStore<TService, T>(IServiceCollection)

Adds an implementation of DapperDataStore<T, TOptions> to the specified IServiceCollection.

public static IServiceCollection AddDapperDataStore<TService, T>(this IServiceCollection services) where TService : DapperDataStore<T, DapperQueryOptions> where T : class

Parameters

services IServiceCollection

The IServiceCollection to add the service to.

Returns

IServiceCollection

A reference to services so that additional calls can be chained.

Type Parameters

TService

The type of the DapperDataStore<T, TOptions> abstraction to add.

T

The type of the DTO to use.

Exceptions

ArgumentNullException

services cannot be null.