Skip to content

Commit

Permalink
Add DbDataSource implementation (#553)
Browse files Browse the repository at this point in the history
  • Loading branch information
smbecker authored Dec 10, 2024
1 parent bd01ae3 commit 9a60f48
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 0 deletions.
18 changes: 18 additions & 0 deletions ClickHouse.Client.Tests/ADO/DataSourceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#if NET7_0_OR_GREATER
using ClickHouse.Client.ADO;
using NUnit.Framework;

namespace ClickHouse.Client.Tests.ADO;

public class DataSourceTests
{
[Test]
public void CanCreateConnection()
{
var connectionString = new ClickHouseConnection("Host=localhost").ConnectionString;
using var dataSource = new ClickHouseDataSource(connectionString);
using var connection = dataSource.CreateConnection();
Assert.AreEqual(connection.ConnectionString, connectionString);
}
}
#endif
130 changes: 130 additions & 0 deletions ClickHouse.Client/ADO/ClickHouseDataSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#if NET7_0_OR_GREATER
using System;
using System.Data.Common;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

namespace ClickHouse.Client.ADO;

public sealed class ClickHouseDataSource : DbDataSource, IClickHouseDataSource
{
private readonly IHttpClientFactory httpClientFactory;
private readonly string httpClientName;
private readonly HttpClient httpClient;
private readonly bool disposeHttpClient;

/// <summary>
/// Initializes a new instance of the <see cref="ClickHouseDataSource"/> class using provided HttpClient.
/// Note that HttpClient must have AutomaticDecompression enabled if compression is not disabled in connection string
/// </summary>
/// <param name="connectionString">Connection string</param>
/// <param name="httpClient">instance of HttpClient</param>
/// <param name="disposeHttpClient">dispose of the passed-in instance of HttpClient</param>
public ClickHouseDataSource(string connectionString, HttpClient httpClient = null, bool disposeHttpClient = true)
{
ConnectionString = connectionString;
this.httpClient = httpClient;
this.disposeHttpClient = disposeHttpClient;
}

/// <summary>
/// Initializes a new instance of the <see cref="ClickHouseDataSource"/> class using an HttpClient generated by the provided <paramref name="httpClientFactory"/>.
/// </summary>
/// <param name="connectionString">The ClickHouse connection string.</param>
/// <param name="httpClientFactory">The factory to be used for creating the clients.</param>
/// <param name="httpClientName">
/// The name of the HTTP client you want to be created using the provided factory.
/// If left empty, the default client will be created.
/// </param>
/// <remarks>
/// <list type="bullet">
/// <item>
/// If compression is not disabled in the <paramref name="connectionString"/>, the <paramref name="httpClientFactory"/>
/// must be configured to enable <see cref="HttpClientHandler.AutomaticDecompression"/> for its generated clients.
/// <example>
/// For example, you can do this while registering the HTTP client:
/// <code>
/// services.AddHttpClient("ClickHouseClient").ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler
/// {
/// AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
/// });
/// </code>
/// </example>
/// </item>
/// <item>
/// The <paramref name="httpClientFactory"/> must set the timeout for its clients if needed.
/// <example>
/// For example, you can do this while registering the HTTP client:
/// <code>
/// services.AddHttpClient("ClickHouseClient", c => c.Timeout = TimeSpan.FromMinutes(5));
/// </code>
/// </example>
/// </item>
/// </list>
/// </remarks>
public ClickHouseDataSource(string connectionString, IHttpClientFactory httpClientFactory, string httpClientName = "")
{
ArgumentNullException.ThrowIfNull(httpClientFactory);
ArgumentNullException.ThrowIfNull(httpClientName);
ConnectionString = connectionString;
this.httpClientFactory = httpClientFactory;
this.httpClientName = httpClientName;
}

public override string ConnectionString
{
get;
}

public ILogger Logger
{
get;
set;
}

protected override void Dispose(bool disposing)
{
base.Dispose(disposing);

if (disposing && disposeHttpClient)
{
httpClient?.Dispose();
}
}

protected override DbConnection CreateDbConnection()
{
var cn = httpClientFactory != null
? new ClickHouseConnection(ConnectionString, httpClientFactory, httpClientName)
: new ClickHouseConnection(ConnectionString, httpClient);
if (cn.Logger == null && Logger != null)
{
cn.Logger = Logger;
}

return cn;
}

public new ClickHouseConnection CreateConnection() => (ClickHouseConnection)CreateDbConnection();

IClickHouseConnection IClickHouseDataSource.CreateConnection() => CreateConnection();

public new ClickHouseConnection OpenConnection() => (ClickHouseConnection)OpenDbConnection();

IClickHouseConnection IClickHouseDataSource.OpenConnection() => OpenConnection();

public new async Task<ClickHouseConnection> OpenConnectionAsync(CancellationToken cancellationToken)
{
var cn = await OpenDbConnectionAsync(cancellationToken).ConfigureAwait(false);
return (ClickHouseConnection)cn;
}

async Task<IClickHouseConnection> IClickHouseDataSource.OpenConnectionAsync(CancellationToken cancellationToken)
{
var cn = await OpenDbConnectionAsync(cancellationToken).ConfigureAwait(false);
return (IClickHouseConnection)cn;
}
}
#endif
19 changes: 19 additions & 0 deletions ClickHouse.Client/IClickHouseDataSource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#if NET7_0_OR_GREATER
using System.Threading;
using System.Threading.Tasks;

namespace ClickHouse.Client;

public interface IClickHouseDataSource
{
string ConnectionString {
get;
}

IClickHouseConnection CreateConnection();

IClickHouseConnection OpenConnection();

Task<IClickHouseConnection> OpenConnectionAsync(CancellationToken cancellationToken = default);
}
#endif

0 comments on commit 9a60f48

Please sign in to comment.