Building a typed DbConnection factory
// <summary>
/// Represents a type used to create instances of <see cref="DbConnection"/> based on the given name.
/// </summary>
public interface IDbConnectionFactory
{
/// <summary>
/// Creates a new <typeparamref name="TConnection"/> instance using the given <paramref name="name"/>.
/// </summary>
/// <param name="name">The name of the connection.</param>
/// <returns>The <typeparamref name="TConnection"/> that was created.</returns>
DbConnection Create(string name);
}
/// <summary>
/// A generic interface for creating <see cref="DbConnection"/> where the category name is derived
/// from the specified <typeparamref name="TCategoryName"/> type name.
/// </summary>
/// <typeparam name="TCategoryName">The type who's name is used for the factory category name.</typeparam>
public interface IDbConnectionFactory<out TCategoryName>
{
/// <summary>
/// Creates a <see cref="DbConnection"/>.
/// </summary>
/// <returns>The newly created <see cref="DbConnection"/>.</returns>
DbConnection Create();
}
/// <summary>
/// Delegates the create of the <see cref="DbConnection"/> to the provided <see cref="IDbConnectionFactory"/>.
/// </summary>
/// <typeparam name="T">The type.</typeparam>
public class DbConnectionFactory<T> : IDbConnectionFactory<T>
{
private readonly IDbConnectionFactory _factory;
/// <summary>
/// Creates a new instance.
/// </summary>
/// <param name="factory">The factory.</param>
public DbConnectionFactory(IDbConnectionFactory factory)
{
_factory = factory;
}
/// <inheritdoc/>
public DbConnection Create()
{
return _factory.Create(typeof(T).Name);
}
}
public static DbConnection Open<T>(this IDbConnectionFactory<T> factory)
{
var connection = factory.Create();
connection.Open();
return connection;
}
/// <summary>
/// Opens a database connection for the given factory.
/// </summary>
/// <typeparam name="TConnection">The type of connection to open.</typeparam>
/// <param name="factory">The connection factory.</param>
/// <returns>The connection of the given type, opened.</returns>
public static TConnection Open<TConnection>(this IDbConnectionFactory<IDbRequire<TConnection>> factory)
where TConnection : DbConnection
{
var connection = (TConnection)factory.Create();
connection.Open();
return connection;
}
/// <summary>
/// Marker interface to type the required <see cref="DbConnection"/>.
/// </summary>
/// <typeparam name="TConnection">The required type of connection.</typeparam>
public interface IDbRequire<TConnection>
where TConnection : DbConnection
{
}
public class DbConnectionFactoryTests
{
[Fact]
public void Can_resolve_a_factory_of_DbConnection_of_the_required_type()
{
var services = new ServiceCollection();
services.TryAddSingleton(typeof(DbConnectionFactory<>));
services.AddSingleton<IDbConnectionFactory, StubConnectionFactory>();
var provider = services.BuildServiceProvider();
var factory = provider.GetService<DbConnectionFactory<Trait>>();
Assert.NotNull(factory);
using (var connection = factory.Create())
{
Assert.IsType<SQLiteConnection>(connection);
}
}
#region Test suite helpers
private class Trait : IDbRequire<SQLiteConnection> { }
private class StubConnectionFactory : IDbConnectionFactory
{
public DbConnection Create(string name)
{
return name switch
{
nameof(Trait) => new SQLiteConnection("Filename=:memory:"),
_ => throw new ArgumentOutOfRangeException(nameof(name)),
};
}
}
#endregion
}