Skip to main content
O cliente C# oficial para se conectar ao ClickHouse. O código-fonte do cliente está disponível no repositório do GitHub. Desenvolvido originalmente por Oleg V. Kozlyuk. A biblioteca fornece duas APIs principais:
  • ClickHouseClient (recomendado): um cliente de alto nível, thread-safe, projetado para uso como singleton. Fornece uma API assíncrona simples para consultas e inserções em massa. Ideal para a maioria das aplicações.
  • ADO.NET (ClickHouseDataSource, ClickHouseConnection, ClickHouseCommand): abstrações padrão de banco de dados do .NET. Necessário para integração com ORM (Dapper, Linq2db) e quando você precisa de compatibilidade com ADO.NET. ClickHouseBulkCopy é uma classe auxiliar para inserir dados com eficiência usando uma conexão ADO.NET. ClickHouseBulkCopy foi descontinuado e será removido em um lançamento futuro; use ClickHouseClient.InsertBinaryAsync no lugar.
Ambas as APIs compartilham o mesmo pool de conexões HTTP subjacente e podem ser usadas juntas na mesma aplicação.

Guia de migração

  1. Atualize o arquivo .csproj com o novo nome do pacote ClickHouse.Driver e a versão mais recente no NuGet.
  2. Atualize todas as referências a ClickHouse.Client para ClickHouse.Driver no seu código.

Versões compatíveis do .NET

ClickHouse.Driver oferece suporte às seguintes versões do .NET:
  • .NET 6.0
  • .NET 8.0
  • .NET 9.0
  • .NET 10.0

Versões compatíveis do ClickHouse

O cliente oferece suporte oficial aos 3 lançamentos mais recentes, além dos 2 lançamentos LTS mais recentes.

Instalação

Instale o pacote via NuGet:
Ou use o Gerenciador de Pacotes NuGet:

Início rápido

Configuração

Há duas formas de configurar sua conexão com o ClickHouse:
  • String de conexão: pares de chave/valor separados por ponto e vírgula que especificam o host, as credenciais de autenticação e outras opções de conexão.
  • Objeto ClickHouseClientSettings: um objeto de configuração fortemente tipado que pode ser carregado de arquivos de configuração ou definido no código.
Abaixo está a lista completa de todas as configurações, seus valores padrão e seus efeitos.

Configurações de conexão

Formato e serialização de dados

Gerenciamento de sessão

O sinalizador UseSession habilita a persistência da sessão do servidor, permitindo usar instruções SET e tabelas temporárias. As sessões serão redefinidas após 60 segundos de inatividade (timeout padrão). A duração da sessão pode ser estendida definindo configurações de sessão por meio de instruções do ClickHouse ou da configuração do servidor.A classe ClickHouseConnection normalmente permite operação paralela (várias threads podem executar consultas concorrentemente). No entanto, habilitar o sinalizador UseSession limitará isso a uma consulta ativa por conexão a qualquer momento (esta é uma limitação do servidor).

Segurança

Configuração do cliente HTTP

Logging e depuração

Configurações personalizadas e roles

Ao usar uma string de conexão para definir configurações personalizadas, use o prefixo set_, por exemplo, “set_max_threads=4”. Ao usar um objeto ClickHouseClientSettings, não use o prefixo set_.Para ver a lista completa de configurações disponíveis, consulte aqui.

Exemplos de string de conexão

Conexão básica

Com configurações personalizadas do ClickHouse


QueryOptions

QueryOptions permite substituir configurações do cliente individualmente para cada consulta. Todas as propriedades são opcionais e só substituem os padrões do cliente quando especificadas. Exemplo:

InsertOptions

InsertOptions estende QueryOptions com configurações específicas para operações de inserção em massa via InsertBinaryAsync. Todas as propriedades de QueryOptions também estão disponíveis em InsertOptions. Exemplo:

Ignorando a consulta de sondagem do esquema

Por padrão, InsertBinaryAsync envia uma consulta SELECT ... WHERE 1=0 antes de cada inserção para identificar os tipos das colunas. Em cenários de alta taxa de transferência, você pode eliminar essa sobrecarga de duas formas: Opção 1: Informe explicitamente os tipos das colunas Quando você conhece o esquema da tabela em tempo de compilação, passe-o diretamente por meio de ColumnTypes. Nenhuma consulta de esquema é enviada:
Opção 2: Armazene o esquema em cache Ao inserir repetidamente na mesma tabela, defina UseSchemaCache = true para consultar o esquema uma única vez e reutilizá-lo nas inserções subsequentes na mesma instância do ClickHouseClient:
  • ColumnTypes tem prioridade sobre UseSchemaCache. Se ambos estiverem definidos, os tipos explícitos serão usados.
  • O cache de esquema não detecta alterações feitas com ALTER TABLE. Se você modificar o esquema da tabela, crie um novo ClickHouseClient ou evite usar UseSchemaCache para essa tabela.
  • O cache tem escopo na instância de ClickHouseClient e é indexado por (banco de dados, tabela). Diferentes subconjuntos de colunas da mesma tabela compartilham um único esquema em cache.

ClickHouseClient

ClickHouseClient é a API recomendada para interagir com o ClickHouse. Ela é thread-safe, foi projetada para uso como singleton e gerencia internamente um pool de conexões HTTP.

Criando um cliente

Crie um ClickHouseClient com uma string de conexão ou um objeto ClickHouseClientSettings. Consulte a seção Configuração para conhecer as opções disponíveis. Os detalhes do seu serviço do ClickHouse Cloud estão disponíveis no console do ClickHouse Cloud. Selecione um serviço e clique em Connect: Escolha C#. Os detalhes da conexão são exibidos abaixo. Se você estiver usando ClickHouse autogerenciado, os detalhes da conexão serão definidos pelo administrador do ClickHouse. Usando uma string de conexão:
Ou use ClickHouseClientSettings:
Para cenários com injeção de dependência, use IHttpClientFactory:
ClickHouseClient foi projetado para ter longa vida útil e ser compartilhado em toda a aplicação. Crie-o uma única vez (normalmente como um singleton) e reutilize-o em todas as operações do banco de dados. O cliente gerencia internamente o pool de conexões HTTP.

Executando consultas

Use ExecuteNonQueryAsync para instruções que não retornam resultados:
Use ExecuteScalarAsync para obter um único valor:

Inserção de dados

Inserções parametrizadas

Insira dados por meio de consultas parametrizadas com ExecuteNonQueryAsync. Os tipos dos parâmetros devem ser especificados no SQL usando a sintaxe {name:Type}:

Inserção em massa

Use InsertBinaryAsync para inserir grandes volumes de linhas com eficiência. Ele transmite os dados usando o formato binário nativo de linhas do ClickHouse, oferece suporte ao envio paralelo de lotes e evita erros de “URL muito longa” que podem ocorrer com consultas parametrizadas.
Para grandes volumes de dados, configure o envio em lotes e o paralelismo com InsertOptions:
  • O cliente obtém automaticamente a estrutura da tabela por meio de SELECT * FROM <table> WHERE 1=0 antes da inserção. Os valores fornecidos devem corresponder aos tipos das colunas de destino. Para ignorar essa consulta, use InsertOptions.ColumnTypes ou InsertOptions.UseSchemaCache.
  • Quando MaxDegreeOfParallelism > 1, os lotes são enviados em paralelo. As sessões não são compatíveis com inserção em paralelo; desative as sessões ou defina MaxDegreeOfParallelism = 1.
  • Use RowBinaryFormat.RowBinaryWithDefaults em InsertOptions.Format se quiser que o servidor aplique valores DEFAULT às colunas não fornecidas.

Inserções com POCO

Em vez de construir arrays object[], você pode inserir diretamente objetos POCO com tipagem forte. Registre o tipo uma vez e, em seguida, passe IEnumerable<T>:
Por padrão, todas as propriedades públicas legíveis são mapeadas para colunas com base em uma correspondência estrita de nomes que diferencia maiúsculas de minúsculas. Você pode personalizar esse mapeamento com atributos:
Quando todas as propriedades mapeadas especificam um Type explícito, a consulta de sondagem do esquema é ignorada por completo. Quando apenas algumas propriedades têm tipos explícitos, o driver recorre à consulta de sondagem do esquema para o conjunto completo de colunas. InsertBinaryAsync<T> oferece suporte às mesmas InsertOptions (batching, paralelismo, cache de esquema) que a sobrecarga object[].
Diferentemente da sobrecarga object[], InsertBinaryAsync<T> não aceita uma lista explícita de colunas. As colunas são determinadas pelas propriedades mapeadas do tipo registrado. Para controlar quais colunas são inseridas, use [ClickHouseNotMapped] para excluir propriedades ou [ClickHouseColumn(Name = "...")] para renomeá-las.Se ColumnTypes estiver definido em InsertOptions, eles substituirão os atributos do POCO.

Evolução do esquema

Os inserts de POCO funcionam perfeitamente quando colunas são adicionadas à tabela de destino depois que o tipo é registrado. Como o driver insere apenas as colunas mapeadas pelo POCO, quaisquer novas colunas com DEFAULT (ou outras expressões padrão) são preenchidas automaticamente pelo servidor. Não é necessário alterar o código nem fazer um novo registro.

Lendo dados

Use ExecuteReaderAsync para executar consultas SELECT. O ClickHouseDataReader retornado fornece acesso tipado às colunas do resultado por meio de métodos como GetInt64(), GetString() e GetFieldValue<T>(). Chame Read() para avançar para a próxima linha. Ele retorna false quando não há mais linhas. Acesse as colunas pelo índice (baseado em 0) ou pelo nome da coluna.

Leitura com POCO

Em vez de ler colunas por índice ou nome, você pode direcionar os resultados da consulta diretamente para suas próprias classes. Registre o tipo uma vez no cliente e, em seguida, use QueryAsync<T>:
Registro
RegisterPocoType<T>() configura os mapeamentos de inserção e de leitura e valida ambos de antemão. RegisterBinaryInsertType<T>() permanece inalterado e continua sendo exclusivo para inserção por compatibilidade com versões anteriores. Um tipo registrado deve ter:
  • Um construtor público sem parâmetros.
  • Pelo menos uma propriedade pública com um setter público que não seja init. Propriedades required são compatíveis.
Correspondência de colunas
A correspondência de colunas diferencia maiúsculas de minúsculas. Colunas de resultado ausentes mantêm as propriedades com seu valor padrão; colunas de resultado adicionais são ignoradas. Não há conversões automáticas, e uma incompatibilidade de tipo lança InvalidOperationException.
Materializando uma única linha
Ao iterar manualmente sobre um reader, use ClickHouseDataReader.MapTo<T>() para materializar a linha atual em um POCO registrado sem avançar o reader:
Diagnósticos de registro
Quando uma LoggerFactory está configurada, RegisterPocoType<T>() e RegisterBinaryInsertType<T>() geram um log no nível Debug (categoria ClickHouse.Driver.Client) informando quais propriedades foram mapeadas para quais colunas e quais foram ignoradas, bem como o motivo. Consulte Logging e diagnósticos.

Parâmetros SQL

No ClickHouse, o formato padrão para parâmetros em consultas SQL é {parameter_name:DataType}. Exemplos:
Os parâmetros SQL de ‘bind’ são passados como parâmetros de consulta do URI HTTP, portanto o uso excessivo deles pode resultar em uma exceção de “URL too long”. Use InsertBinaryAsync para inserção de dados em massa e evitar essa limitação.

Parâmetros do tipo Identifier

O tipo de parâmetro Identifier permite vincular com segurança o nome de um banco de dados, tabela ou coluna, em vez de um literal de string entre aspas. Use-o com a sintaxe {name:Identifier} em SQL ou definindo ClickHouseDbParameter.ClickHouseType = "Identifier":
O valor é enviado literalmente, e o servidor o substitui como um identificador SQL não entre aspas, aplicando seu próprio uso de backticks e escape. Identificadores que contêm caracteres especiais (inclusive backticks) podem fazer o percurso de ida e volta com segurança.

ID da consulta

Cada consulta recebe um query_id único, que pode ser usado para obter dados da tabela system.query_log ou cancelar consultas de longa execução. Você pode especificar um ID de consulta personalizado por meio de QueryOptions:
Se você estiver especificando um QueryId personalizado, garanta que ele seja único em cada chamada. Um GUID aleatório é uma boa opção.

Mapeamento personalizado de tipos de parâmetro

Ao usar parâmetros no estilo @ (por exemplo, WHERE id = @id), o driver infere automaticamente o tipo do ClickHouse com base no tipo de valor do .NET. Por exemplo, int é mapeado para Int32.
Comportamento de parâmetros DateTime inferidosPara parâmetros no estilo @ sem hint {name:Type} no SQL e sem ClickHouseType definido, valores que representam um instante são inferidos como DateTime('UTC') em vez de um DateTime simples. DateTime com Kind igual a Utc ou Local, e todos os valores DateTimeOffset, são enviados como DateTime('UTC'), preservando o instante em qualquer fuso horário do servidor.Hints explícitos ({name:DateTime}) têm precedência sobre a inferência e são a forma recomendada de criar consultas.
Para substituir esses padrões, defina ParameterTypeResolver em ClickHouseClientSettings. Isso é útil quando você quer que todos os parâmetros DateTime usem DateTime64(3) para precisão de milissegundos ou que todos os decimais usem uma escala específica, sem precisar definir ClickHouseType em cada parâmetro individualmente. Usando DictionaryParameterTypeResolver para mapeamentos simples de tipo:
IParameterTypeResolver personalizado para cenários avançados: Para resolução com base no valor ou no nome, implemente diretamente a interface IParameterTypeResolver. Retorne null para usar a inferência padrão:
Você também pode definir um resolver para uma única consulta por meio de QueryOptions.ParameterTypeResolver. Quando definido, ele tem precedência sobre o resolver no nível do cliente. Precedência da resolução de tipos: O resolver é uma etapa em uma cadeia de precedência. Da maior para a menor prioridade:
  1. ClickHouseType explícito definido no parâmetro
  2. Type hint de SQL da sintaxe {name:Type} na consulta
  3. IParameterTypeResolver (de QueryOptions.ParameterTypeResolver, com fallback para ClickHouseClientSettings.ParameterTypeResolver)
  4. Inferência de tipo integrada (TypeConverter.ToClickHouseType)
O resolver também funciona com o caminho do ADO.NET ClickHouseConnection — as configurações são herdadas pelas conexões criadas a partir do cliente.

Formatação personalizada de valores de parâmetros

IParameterFormatter é um hook que define como os valores dos parâmetros são serializados. Use-o quando a formatação padrão (por exemplo, precisão de DateTime, convenção decimal, escaping de strings, representação de números) não corresponder ao que seu esquema ou suas ferramentas downstream esperam. Defina ParameterFormatter em ClickHouseClientSettings para instalar um formatador para todas as consultas parametrizadas. O formatador recebe o valor, o nome do tipo ClickHouse resolvido e o nome do parâmetro, e retorna a representação em string que é enviada ao servidor. Retorne null para deixar o processamento seguir para o formatador padrão. Usando DictionaryParameterFormatter para formatação simples por tipo CLR:
IParameterFormatter personalizado para casos avançados:
Você também pode definir um formatador por consulta via QueryOptions.ParameterFormatter. Quando definido, ele tem precedência sobre o formatador em nível de cliente. Valores compostos: O formatador é executado tanto para parâmetros de collection de nível superior quanto para cada elemento dentro de valores compostos (Array, Tuple, Map, Nullable, LowCardinality, Variant). Por exemplo, um mapeamento typeof(int) formata individualmente cada elemento Int32 de um Array(Int32). Uso de aspas simples em contextos compostos: Para types do ClickHouse semelhantes a string (String, FixedString, Enum8, Enum16, IPv4, IPv6, UUID) embutidos em um literal composto, o driver envolve a saída do formatador em aspas simples, mas não escapa seu conteúdo. Se a string retornada contiver uma aspa simples ou barra invertida sem escape, o literal composto ficará malformado e o servidor rejeitará a consulta. Parâmetros de string de nível superior (não embutidos em um composto) são usados literalmente, sem aspas, portanto não é necessário escaping nesse caso. Precedência do formatador:
  1. IParameterFormatter (de QueryOptions.ParameterFormatter, com fallback para ClickHouseClientSettings.ParameterFormatter). Se ele retornar um valor não nulo, esse valor será usado.
  2. Formatação interna específica de cada tipo em HttpParameterFormatter.
O formatador não é consultado para valores null ou DBNull; eles são sempre serializados como a sentinela nula do ClickHouse (\N).

Conversão personalizada de valores lidos

IReadValueConverter permite transformar os valores retornados pelo leitor de dados após a desserialização, sem alterar o tipo CLR deles. Usos típicos: definir DateTime.Kind = Utc em uma coluna DateTime sem timezone, aparar ou normalizar strings, ou fazer o pós-processamento de uma coluna JSON antes que ela chegue ao código da aplicação. Defina ReadValueConverter em ClickHouseClientSettings para instalar um conversor para todas as leituras. O conversor é invocado uma vez por coluna por linha, tanto no caminho com boxing (GetValue) quanto no genérico (GetFieldValue<T>). Quando nenhum conversor é definido, a sobrecarga é zero — o leitor retorna os valores diretamente. Usando DictionaryReadValueConverter para conversão simples por tipo CLR:
Valores cujo tipo CLR em runtime não está registrado com For<T> passam inalterados. O despacho é feito pelo tipo CLR exato, portanto registre o tipo real produzido pelo leitor (por exemplo, For<JsonObject> para uma coluna JSON em JsonReadMode.Binary). IReadValueConverter personalizado para cenários avançados: Se você precisar despachar com base na string de tipo do lado do ClickHouse (por exemplo, para distinguir DateTime de DateTime('UTC') — ambos aparecem como o mesmo tipo CLR), implemente IReadValueConverter diretamente:
O conversor deve preservar o tipo CLR de runtime; os metadados da coluna (GetFieldType, GetSchemaTable) não passam por ele e devem permanecer consistentes com o valor retornado. Você também pode definir um conversor por consulta via QueryOptions.ReadValueConverter; quando definido, ele tem precedência sobre o conversor no nível do cliente. Limite do despacho: O conversor é invocado uma vez por coluna com o valor completo da célula desserializada; ele não processa recursivamente contêineres compostos. Para uma coluna Array(Int32), o valor passado é um int[]; para Tuple(Int32, String), é um ITuple. O conversor funciona com o caminho ClickHouseConnection do ADO.NET — as configurações são herdadas pelas conexões criadas a partir do cliente.

Fluxo bruto

Use ExecuteRawResultAsync para transmitir diretamente os resultados da consulta em um formato específico, sem passar pelo leitor de dados. Isso é útil para exportar dados para arquivos ou repassá-los a outros sistemas:
Formatos comuns: JSONEachRow, CSV, TSV, Parquet, Native. Consulte a documentação sobre formatos para ver todas as opções.

Compressão de transporte por consulta

Por padrão, o cliente negocia gzip, deflate quando Compression=true (o padrão da string de conexão), e o cliente HTTP descomprime o fluxo de forma transparente. Para exportações brutas (por exemplo, Parquet, Arrow, Native), talvez você queira negociar um codec diferente (por exemplo, zstd ou lz4) para trocar CPU por largura de banda sem alterar a configuração da conexão como um todo. QueryOptions.AcceptEncoding e ClickHouseCommand.AcceptEncoding definem o cabeçalho HTTP Accept-Encoding para uma única solicitação, substituindo qualquer valor padrão definido anteriormente, e forçam enable_http_compression=1 na URL (o que o ClickHouse exige antes de respeitar Accept-Encoding).

Configuração do HttpClient

O HttpClient padrão que o driver cria tem AutomaticDecompression = GZip | Deflate, o que descomprime esses algoritmos de forma transparente e remove Content-Encoding da resposta. Isso é o ideal para consultas normais, mas não se você quiser processar por conta própria os dados brutos e comprimidos. Nesse caso, passe um HttpClient ou HttpClientFactory para o ClickHouseClient com AutomaticDecompression = DecompressionMethods.None
Se AcceptEncoding solicitar um codec que o HttpClient configurado não consegue descomprimir automaticamente, somente ExecuteRawResultAsync é seguro. Chamar ExecuteReaderAsync, ExecuteScalarAsync ou ExecuteNonQueryAsync nesse mesmo fluxo de consulta fará com que o sistema tente analisar os bytes comprimidos como se fossem o formato normal do resultado, produzindo dados sem sentido.

Corpos de erro

Quando o servidor responde com um 4xx/5xx e enable_http_compression=1 foi definido, ele compacta o corpo do erro com o mesmo codec que usaria em uma resposta bem-sucedida. O driver descompacta esses corpos para os codecs incluídos na BCL (gzip, deflate, br/brotli), para que a mensagem exibida em ClickHouseServerException seja legível. Para codecs que ele não consegue decodificar (zstd, lz4, …), o driver retorna uma mensagem substituta que informa o codec e aponta para system.query_log, onde está o texto original do erro.

Inserção bruta via stream

Use InsertRawStreamAsync para inserir dados diretamente de streams de arquivo ou de memória em formatos como CSV, JSON, Parquet ou qualquer formato compatível do ClickHouse. Inserir de um arquivo CSV:
Consulte a documentação das configurações de formato para ver as opções de controle do comportamento da ingestão de dados.

Mais exemplos

Para mais exemplos práticos de uso, consulte o diretório examples no repositório do GitHub.

ADO.NET

A biblioteca oferece suporte completo ao ADO.NET por meio de ClickHouseConnection, ClickHouseCommand e ClickHouseDataReader. Essa API é necessária para a integração com ORMs (Dapper, Linq2db) e quando você precisa das abstrações padrão de banco de dados do .NET.

Gerenciamento do ciclo de vida com ClickHouseDataSource

Sempre crie conexões a partir de um ClickHouseDataSource para garantir o gerenciamento adequado do ciclo de vida e o uso de pool de conexões. A DataSource gerencia internamente um único ClickHouseClient, e todas as conexões compartilham seu pool de conexões HTTP.
Para injeção de dependências:
Não crie ClickHouseConnection diretamente em código de produção. Cada instanciação direta cria um novo cliente HTTP e um novo pool de conexões, o que pode levar ao esgotamento de sockets sob carga:
Em vez disso, sempre use ClickHouseDataSource ou compartilhe uma única instância de ClickHouseClient.

Usando o ClickHouseCommand

Crie comandos usando uma conexão para executar SQL:
Métodos de comando:
  • ExecuteNonQueryAsync() - Para instruções INSERT, UPDATE, DELETE e DDL
  • ExecuteScalarAsync() - Retorna a primeira coluna da primeira linha
  • ExecuteReaderAsync() - Retorna um ClickHouseDataReader para percorrer os resultados

Usando ClickHouseDataReader

O ClickHouseDataReader fornece acesso tipado aos resultados da consulta:

Boas práticas

Ciclo de vida da conexão e pool de conexões

ClickHouse.Driver usa System.Net.Http.HttpClient internamente. O HttpClient tem um pool de conexões por endpoint. Como consequência:
  • As sessões do banco de dados são multiplexadas por conexões HTTP gerenciadas pelo pool de conexões.
  • As conexões HTTP são recicladas automaticamente pelo pool.
  • As conexões podem permanecer ativas mesmo depois que os objetos ClickHouseClient ou ClickHouseConnection são descartados.
Padrões recomendados:
Ao usar um HttpClient ou HttpClientFactory personalizado, garanta que PooledConnectionIdleTimeout esteja definido com um valor menor que o keep_alive_timeout do servidor, para evitar erros causados por conexões parcialmente fechadas. O keep_alive_timeout padrão para Implantações no Cloud é de 10 segundos.
Evite criar várias instâncias de ClickHouseClient ou de ClickHouseConnection independentes sem um HttpClient compartilhado. Cada instância cria seu próprio pool de conexões.

Tratamento de DateTime

  1. Use UTC sempre que possível. Armazene timestamps como colunas DateTime('UTC') e use DateTimeKind.Utc no seu código. Isso elimina ambiguidades de fuso horário.
  2. Use DateTimeOffset para lidar explicitamente com o fuso horário. Ele sempre representa um instante específico e inclui a informação de offset.
  3. Especifique o fuso horário nas type hints de SQL. Ao usar parâmetros com valores DateTime Unspecified destinados a colunas que não usam UTC, inclua o fuso horário no SQL:

Inserções assíncronas

Inserções assíncronas transferem do cliente para o servidor a responsabilidade pelo agrupamento em lotes. Em vez de exigir esse agrupamento no lado do cliente, o servidor armazena em buffer os dados recebidos e os grava no armazenamento com base em limites configuráveis. Isso é útil em cenários de alta concorrência, como workloads de observabilidade, em que muitos agentes enviam payloads pequenos. Habilite inserções assíncronas via CustomSettings ou pela connection string:
Dois modos (controlados por wait_for_async_insert):
Com wait_for_async_insert=0, os erros só aparecem durante o flush e não podem ser rastreados até a inserção original. O cliente também não fornece backpressure, o que pode sobrecarregar o servidor.
Configurações principais:

Sessões

Ative sessões apenas quando precisar de recursos com estado no servidor, por exemplo:
  • Tabelas temporárias (CREATE TEMPORARY TABLE)
  • Manter o contexto da consulta em várias instruções
  • Configurações no nível da sessão (SET max_threads = 4)
Quando as sessões estão ativadas, as solicitações são serializadas para evitar o uso simultâneo da mesma sessão. Isso adiciona sobrecarga a cargas de trabalho que não exigem estado de sessão.
Usando ADO.NET (para compatibilidade com ORMs):

Tipos de dados compatíveis

ClickHouse.Driver é compatível com todos os tipos de dados do ClickHouse. As tabelas abaixo mostram o mapeamento entre os tipos do ClickHouse e os tipos nativos do .NET na leitura de dados do banco de dados.

Mapeamento de tipos: leitura do ClickHouse

Tipos inteiros


Tipos de ponto flutuante


Tipos decimais

A conversão de tipos decimais é controlada pela configuração UseCustomDecimals.

Tipo booleano


Tipos String

Por padrão, as colunas String e FixedString(N) são retornadas como string. Defina ReadStringsAsByteArrays=true na string de conexão para lê-las como byte[]. Isso é útil ao armazenar dados binários que podem não estar em UTF-8 válido.

Tipos de data e hora

O ClickHouse armazena internamente os valores DateTime e DateTime64 como timestamps Unix (segundos ou frações de segundo desde a epoch). Embora o armazenamento seja sempre em UTC, as colunas podem ter um fuso horário associado, o que afeta como os valores são exibidos e interpretados. Ao ler valores DateTime, a propriedade DateTime.Kind é definida com base no fuso horário da coluna: Para colunas que não estão em UTC, o DateTime retornado representa a hora local nesse fuso horário. Use ClickHouseDataReader.GetDateTimeOffset() para obter um DateTimeOffset com o deslocamento correto para esse fuso horário:
Para colunas sem um fuso horário explícito (ou seja, DateTime em vez de DateTime('Europe/Amsterdam')), o driver retorna um DateTime com Kind=Unspecified. Isso preserva exatamente a hora local como foi armazenada, sem fazer suposições sobre o fuso horário. Se você precisar de um comportamento sensível a fuso horário para colunas sem fusos horários explícitos, faça uma destas opções:
  1. Use fusos horários explícitos nas definições das colunas: DateTime('UTC') ou DateTime('Europe/Amsterdam')
  2. Aplique o fuso horário manualmente após a leitura.

Tipo JSON

O tipo de retorno das colunas JSON é controlado pela configuração JsonReadMode:
  • Binary (padrão): Retorna System.Text.Json.Nodes.JsonObject. Fornece acesso estruturado aos dados JSON, mas tipos especializados do ClickHouse (como endereços IP, UUIDs e valores decimais grandes) são convertidos para suas representações em string dentro da estrutura JSON.
  • String: Retorna o JSON bruto como string. Preserva a representação exata do JSON no ClickHouse, o que é útil quando você precisa repassar o JSON sem fazer o parsing ou quando deseja cuidar da desserialização por conta própria.

Outros tipos

Os tipos Dynamic e Variant serão convertidos para o tipo correspondente ao tipo subjacente real de cada linha.

Tipos de geometria

O tipo Geometry é um Variant que pode conter qualquer um dos tipos de geometria. Ele será convertido para o tipo correspondente.

Mapeamento de tipos: escrita no ClickHouse

Ao inserir dados, o driver converte tipos .NET nos tipos correspondentes do ClickHouse. As tabelas abaixo mostram quais tipos .NET são aceitos para cada tipo de coluna do ClickHouse.

Tipos inteiros


Tipos de ponto flutuante


Tipo booleano


Tipos String


Tipos de data e hora

Valores fora do intervaloNo caminho de gravação binária, valores de Date, Date32, DateTime e DateTime32 fora do intervalo suportado lançam ArgumentOutOfRangeException no momento de Write, indicando o tipo da coluna e o intervalo suportado. Anteriormente, valores fora do intervalo podiam ser truncados silenciosamente por meio de um inteiro de 32 bits e reinterpretados pelo servidor, produzindo timestamps reais, mas incorretos.
O driver respeita DateTime.Kind ao gravar valores: Os valores de DateTimeOffset sempre preservam o instante exato. Exemplo: DateTime UTC (instante preservado)
Exemplo: DateTime não especificado (hora local)
Recomendação: para obter o comportamento mais simples e previsível, use DateTimeKind.Utc ou DateTimeOffset em todas as operações com DateTime. Isso garante que seu código funcione de forma consistente, independentemente do fuso horário do servidor, do cliente ou da coluna.

Parâmetros HTTP vs bulk copy

Há uma diferença importante entre a vinculação de parâmetros HTTP e o bulk copy ao gravar valores Unspecified de DateTime: Bulk Copy conhece o fuso horário da coluna de destino e interpreta corretamente os valores Unspecified nesse fuso. Parâmetros HTTP não conhecem automaticamente o fuso horário da coluna. Você deve especificá-lo na dica de tipo SQL:

Tipos Decimal


Tipo JSON

O comportamento ao escrever JSON é controlado pela configuração JsonWriteMode:
  • String (padrão): Aceita string, JsonObject, JsonNode ou qualquer objeto. Todas as entradas são serializadas com System.Text.Json.JsonSerializer e enviadas como strings JSON para processamento no servidor. Este é o modo mais flexível e funciona sem registro de tipo.
  • Binary: Aceita apenas tipos POCO registrados. Os dados são convertidos no cliente para o formato JSON binário do ClickHouse, com suporte completo a dicas de tipo. Requer chamar connection.RegisterJsonSerializationType<T>() antes do uso. Escrever valores string ou JsonNode nesse modo lança ArgumentException.
Colunas JSON tipadas
Quando uma coluna JSON tem dicas de tipo (por exemplo, JSON(id UInt64, price Decimal128(2))), o driver usa essas dicas para serializar valores com total fidelidade aos tipos. Isso preserva a precisão de tipos como UInt64, Decimal, UUID e DateTime64, que, de outra forma, perderiam precisão ao serem serializados como JSON genérico.
Serialização de POCO
POCOs podem ser gravados em colunas JSON de duas formas, dependendo do JsonWriteMode: Modo String (padrão): os POCOs são serializados por meio de System.Text.Json.JsonSerializer. Não é necessário registrar tipos. Esta é a abordagem mais simples e funciona com objetos anônimos. Modo binário: os POCOs são serializados usando o formato JSON binário do driver, com suporte completo a type hints. Os tipos devem ser registrados com connection.RegisterJsonSerializationType<T>() antes do uso. Esse modo oferece suporte a mapeamentos de path personalizados por meio de atributos:
  • [ClickHouseJsonPath("path")]: Mapeia uma propriedade para um path JSON personalizado. Útil para estruturas aninhadas ou quando o nome da propriedade difere da chave JSON desejada. Funciona apenas no modo binário.
  • [ClickHouseJsonIgnore]: Exclui uma propriedade da serialização. Funciona apenas no modo binário.
A correspondência entre o nome da propriedade e as dicas de tipo da coluna diferencia maiúsculas de minúsculas. Uma propriedade UserId só corresponderá a uma dica definida como UserId, não como userid. Isso está de acordo com o comportamento do ClickHouse, que permite que caminhos como userName e UserName coexistam como campos separados. Limitações (apenas no modo Binary):
  • Os tipos POCO precisam ser registrados na conexão com connection.RegisterJsonSerializationType<T>() antes da serialização. Tentar serializar um tipo não registrado lança ClickHouseJsonSerializationException.
  • Propriedades de Dicionário e array/lista exigem dicas de tipo na definição da coluna para serem serializadas corretamente. Sem essas dicas, use o modo String.
  • Valores nulos em propriedades POCO só são gravados quando o caminho tem uma dica de tipo Nullable(T) na definição da coluna. O ClickHouse não permite tipos Nullable em caminhos JSON dinâmicos, portanto propriedades nulas sem dica são ignoradas.
  • Os atributos ClickHouseJsonPath e ClickHouseJsonIgnore são ignorados no modo String (eles só funcionam no modo Binary).

Outros tipos


Tipos de geometria


Não suportado para escrita


Tratamento do tipo Nested

Os tipos aninhados do ClickHouse (Nested(...)) podem ser lidos e gravados usando semântica de arrays.

Logging e diagnósticos

O cliente .NET do ClickHouse se integra às abstrações Microsoft.Extensions.Logging para oferecer logging leve e opcional. Quando habilitado, o driver emite mensagens estruturadas para eventos do ciclo de vida da conexão, execução de comandos, operações de transporte e operações de inserção em massa. O logging é totalmente opcional — aplicações que não configuram um logger continuam em execução sem sobrecarga adicional.

Início rápido

Usando appsettings.json

Você pode configurar os níveis de log usando a configuração padrão do .NET:

Usando configuração em memória

Você também pode configurar o nível de verbosidade do logging por categoria no código:

Categorias e emissores

O driver usa categorias específicas para que você possa ajustar com precisão os níveis de log por componente:

Exemplo: Diagnóstico de problemas de conexão

Isso registrará:
  • Seleção da fábrica do cliente HTTP (pool padrão vs. conexão única)
  • Configuração do handler HTTP (SocketsHttpHandler ou HttpClientHandler)
  • Configurações do pool de conexões (MaxConnectionsPerServer, PooledConnectionLifetime etc.)
  • Configurações de timeout (ConnectTimeout, Expect100ContinueTimeout etc.)
  • Configuração de SSL/TLS
  • Eventos de abertura/fechamento de conexões
  • Rastreamento do ID da sessão

Modo de depuração: rastreamento de rede e diagnósticos

Para ajudar a diagnosticar problemas de rede, a biblioteca do driver inclui um auxiliar que habilita o rastreamento de baixo nível dos componentes internos de rede do .NET. Para habilitá-lo, você deve passar uma LoggerFactory com o nível definido como Trace e definir EnableDebugMode como true (ou habilitá-lo manualmente pela classe ClickHouse.Driver.Diagnostic.TraceHelper). Os eventos serão registrados na categoria ClickHouse.Driver.NetTrace. Aviso: isso gerará logs extremamente detalhados e afetará o desempenho. Não é recomendável habilitar o modo de depuração em production.

OpenTelemetry

O driver oferece suporte nativo ao rastreamento distribuído com OpenTelemetry por meio da API .NET System.Diagnostics.Activity. Quando habilitado, o driver emite spans para operações de banco de dados que podem ser exportados para backends de observabilidade, como Jaeger ou o próprio ClickHouse (por meio do OpenTelemetry Collector).

Habilitando o rastreamento

Em aplicações ASP.NET Core, adicione o ActivitySource do driver do ClickHouse à configuração do OpenTelemetry:
Para aplicativos de console, testes ou configuração manual:

Atributos de span

Cada span inclui atributos de banco de dados padrão do OpenTelemetry, além de estatísticas de consulta específicas do ClickHouse que podem ser usadas para depuração.

Opções de configuração

Controle o comportamento do rastreamento por meio de ClickHouseDiagnosticsOptions:
Ativar IncludeSqlInActivityTags pode expor dados sensíveis nos seus traces. Use com cautela em ambientes de produção.

Configuração de TLS

Ao se conectar ao ClickHouse via HTTPS, você pode configurar o comportamento do TLS/SSL de várias formas.

Validação personalizada de certificados

Para ambientes de produção que exigem uma lógica personalizada de validação de certificados, forneça seu próprio HttpClient com um handler ServerCertificateCustomValidationCallback configurado:
Considerações importantes ao fornecer um HttpClient personalizado
  • Descompressão automática: Você deve habilitar AutomaticDecompression se a compressão não estiver desabilitada (a compressão é habilitada por padrão).
  • Tempo limite de inatividade: Defina PooledConnectionIdleTimeout com um valor menor que o keep_alive_timeout do servidor (10 segundos para ClickHouse Cloud) para evitar erros de conexão causados por conexões semiabertas.

Suporte a ORMs

ORMs exigem a API ADO.NET (ClickHouseConnection). Para gerenciar corretamente o ciclo de vida da conexão, crie as conexões a partir de um ClickHouseDataSource:

Dapper

ClickHouse.Driver funciona com Dapper. O driver converte automaticamente a sintaxe @parameter do Dapper para a sintaxe nativa {parameter:Type} do ClickHouse, com os tipos inferidos a partir dos valores do .NET. Use ClickHouseDataSource para gerenciar corretamente o ciclo de vida da conexão:

Estilos de passagem de parâmetros

Todos os estilos padrão de passagem de parâmetros do Dapper são compatíveis: Objetos anônimos:
Classes do tipo POCO:
Dicionário:
DynamicParameters (a partir de um dicionário ou de um objeto anônimo):

Consultas com POCOs

O Dapper mapeia colunas para propriedades pelo nome (sem diferenciar maiúsculas de minúsculas):

Sintaxe nativa de parâmetros do ClickHouse

Quando precisar de controle explícito sobre o tipo, use diretamente no SQL a sintaxe {param:Type} do ClickHouse com um Dictionary<string, object> para os valores dos parâmetros. Não combine a sintaxe @param com a sintaxe {param:Type} para o mesmo parâmetro.

WHERE IN

A expansão nativa do IN no Dapper funciona:
O Dapper reescreve isso como WHERE id IN (@Ids1, @Ids2, @Ids3), e o driver converte cada parâmetro expandido. O has() do ClickHouse com parâmetro Array também funciona:

Manipuladores de tipo personalizados

Alguns tipos do ClickHouse, como ITuple, BigInteger e ClickHouseDecimal, precisam ter manipuladores registrados na inicialização:
Consulte o exemplo do Dapper para ver uma implementação de exemplo de um manipulador de tipos.

Dapper.Contrib

GetAll<T>() e Get<T>(id) funcionam. Insert<T>() não — ele gera sintaxe do SQL Server (SCOPE_IDENTITY, []). Recomenda-se usar, em vez disso, o método nativo InsertBinaryAsync do ClickHouseClient.
Os nomes das propriedades devem corresponder exatamente aos nomes de coluna do ClickHouse (diferenciam maiúsculas de minúsculas).

Limitações

Linq2db

Este driver é compatível com o linq2db, um ORM leve e provedor LINQ para .NET. Consulte o site do projeto para obter a documentação detalhada. Exemplo de uso: Crie uma DataConnection usando o provedor do ClickHouse:
Os mapeamentos de tabelas podem ser definidos usando atributos ou a API fluente. Se os nomes da sua classe e propriedade corresponderem exatamente aos nomes da tabela e da coluna, nenhuma configuração será necessária:
Consultando:
Cópia em lote: Use BulkCopyAsync para inserções em lote eficientes.

Entity Framework Core

O provedor oficial do Entity Framework Core para ClickHouse. Mapeie classes C# para tabelas do ClickHouse, faça consultas com LINQ e insira dados via SaveChanges — tudo usando os padrões familiares do EF Core.
Este provedor está em desenvolvimento ativo. O lançamento atual oferece suporte a consultas LINQ (incluindo junções, subconsultas e operações de conjunto), INSERT via SaveChanges / BulkInsertAsync, migrations com DDL completo (CREATE / ALTER / DROP) e configuração específica do motor de tabela do ClickHouse. UPDATE / DELETE não são suportados.

Instalação

Requer o .NET 10.0 e o EF Core 10.

Início rápido

Defina sua entidade e o DbContext e, em seguida, consulte com LINQ:

Tipos com suporte

Use ClickHouseDecimal (de ClickHouse.Driver.Numerics) em vez de decimal quando precisar da precisão total de colunas Decimal128/Decimal256 — o decimal do .NET é limitado a 28–29 dígitos significativos.

Operações LINQ compatíveis

Consultas: Where, OrderBy, Take, Skip, Select, First, Single, Any, All, Count, Distinct, AsNoTracking GROUP BY e agregações: GroupBy com Count, LongCount, Sum, Average, Min, Max — incluindo HAVING (.Where() após .GroupBy()), várias agregações em uma única projeção e OrderBy com base nos resultados agregados. JOINs: Join (INNER), padrões GroupJoin/SelectMany (LEFT e CROSS). LEFT JOIN retorna null de fato para linhas sem correspondência (veja semântica de null em LEFT JOIN abaixo). Subconsultas: Contains / IN correlacionados, Any / EXISTS, All e subconsultas escalares em projeções. Operações de conjunto: Concat (→ UNION ALL), Union (→ UNION DISTINCT), Intersect, Except. Coleções locais inline: junções e Contains em coleções em memória (int[], List<T>, etc.) são convertidos em uma série de UNIONs. Métodos de string: Contains, StartsWith, EndsWith, IndexOf, Replace, Substring, Trim/TrimStart/TrimEnd, ToLower, ToUpper, Length, IsNullOrEmpty, Concat (e o operador +). Funções matemáticas: métodos padrão de Math e MathF traduzidos para seus equivalentes no ClickHouse — funções aritméticas, logarítmicas, trigonométricas e utilitárias.
Semântica de NULL em LEFT JOIN
O provider injeta set_join_use_nulls=1 automaticamente em cada conexão para atender às expectativas do Entity Framework em relação ao comportamento de JOIN. Se o seu servidor ClickHouse ou profile impedir a alteração dessa configuração (por exemplo, um profile readonly=1), desative isso com:
Com o opt-out ativado, o LEFT JOIN retorna os valores padrão das colunas do ClickHouse, e a detecção de navegação baseada em nulos do EF não funciona mais como esperado. Use comparações explícitas com 0 / "" em vez de == null.

Inserção de dados

SaveChanges usa a API nativa InsertBinaryAsync do driver — codificação RowBinary com compressão GZip, muito mais eficiente do que SQL parametrizado:
As entidades passam de Added para Unchanged após salvar, assim como em qualquer outro provedor do EF Core. O tamanho do lote é configurável (padrão: 1000):

Inserção em massa

Para cargas de alta taxa de transferência, use BulkInsertAsync em vez de SaveChanges. Esse é um método de extensão no DbContext que ignora completamente o rastreador de alterações, a resolução de identidade e o gerenciamento de estado do EF Core — ele chama diretamente o InsertBinaryAsync do driver com codificação RowBinary e compressão GZip. Isso o torna ideal para carregar grandes conjuntos de dados quando você não precisa rastrear entidades após a inserção:
A entrada pode ser qualquer IEnumerable<T> — ela processa as entidades em fluxo, sem carregá-las todas na memória. O valor retornado é o número de linhas inseridas. As entidades não ficam vinculadas ao DbContext após a inserção, portanto não há transição de estado de AddedUnchanged.

Enums

As colunas Enum8/Enum16 do ClickHouse podem ser mapeadas como propriedades string ou como tipos enum em C#. Ao usar enums em C#, o provedor converte automaticamente entre o enum e sua representação textual:

Conversões de tipos personalizadas

O sistema ValueConverter do EF Core permite mapear tipos personalizados para tipos que o provedor já suporta. O provedor nunca vê seu tipo personalizado — o EF Core faz a conversão na interface entre os dois. Conversão por propriedade:
Classe de conversor reutilizável:

Anotações de tipo de coluna

Para tipos escalares como string, int, DateTime etc., o provedor deduz automaticamente o tipo do ClickHouse. Para tipos parametrizados e wrappers, é necessário especificar explicitamente o tipo do ClickHouse. Usando anotações de dados (atributos):
Usando a API fluente no OnModelCreating:
Wrappers aninhados como Array(Nullable(Int32)) e LowCardinality(Nullable(String)) são suportados — o provedor desempacota Nullable e LowCardinality automaticamente em todos os níveis de aninhamento.

Colunas Variant e Dynamic

As colunas Variant(T1, T2, ...) e Dynamic do ClickHouse são mapeadas para object no .NET. Como object é genérico demais para a inferência automática de tipos, você deve declarar explicitamente o tipo de armazenamento por meio de .HasColumnType():
Ao ler, o valor é desserializado automaticamente para o tipo .NET correspondente ao discriminador armazenado (por exemplo, string, ulong, ulong[]).

Colunas JSON

O provedor dá suporte ao tipo de coluna Json do ClickHouse, com mapeamento para System.Text.Json.Nodes.JsonNode (principal) ou string (via ValueConverter automático):
A leitura e a escrita de JSON funcionam tanto com SaveChanges quanto com BulkInsertAsync:
Se você preferir strings JSON brutas, mapeie a propriedade como string, com o tipo de coluna Json — o provedor aplica um ValueConverter automaticamente:
  • Sem tradução de caminhos JSONentity.Data["name"] no LINQ não é convertido para a sintaxe SQL data.name do ClickHouse. Filtre colunas não JSON e inspecione o JSON na memória.
  • Semântica de NULL — o tipo JSON do ClickHouse retorna {} (objeto vazio) para valores NULL, em vez de SQL NULL.
  • Precisão de inteiros — o JSON do ClickHouse armazena todos os inteiros como Int64. Ao ler com JsonNode, use GetValue<long>() em vez de GetValue<int>().

Motores de tabela

Configure os motores de tabela do ClickHouse e as cláusulas específicas de cada motor por meio da API fluente ToTable(name, t => ...). Quando nenhum motor é configurado, o provedor usa MergeTree, com ORDER BY derivado da chave primária da entidade.
Famílias de motores compatíveis: Cláusulas do motor: WithOrderBy, WithPartitionBy, WithPrimaryKey, WithSampleBy, WithTtl, WithSettings. Todas são anexadas ao construtor de motor retornado por HasXxxEngine(). Recursos em nível de coluna: HasCodec, HasTtl, HasComment, HasDefault — todos participam das migrações. Índices de data skipping — via HasIndex(...).HasSkippingIndexType(...):
Índices padrão (sem skipping) são ignorados silenciosamente, já que não têm equivalente no ClickHouse. Índices únicos geram exceção, pois o ClickHouse não impõe unicidade.

Migrações

Fluxo de trabalho padrão das migrações do EF Core:
Operações suportadas:

Limitações de migração

Além das migrações, o provedor também ainda não oferece suporte a:
  • UPDATE / DELETE
  • Transações: BeginTransaction é um no-op. Não há suporte a transações ACID no ClickHouse.
  • Tradução de consultas com caminho JSON: entity.Data["key"] em LINQ não é traduzido para a sintaxe SQL data.key do ClickHouse. Aplique filtros em colunas não JSON e inspecione o JSON na memória.

Limitações

Tuple com 8+ elementos e uma tupla aninhada na última posição

Tipos ValueTuple de C# com mais de 7 elementos usam um esquema de aninhamento gerado pelo compilador: o 8º argumento genérico (TRest) é, ele próprio, um ValueTuple que contém os elementos restantes. Por exemplo, (int, int, int, int, int, int, int, string, string) é compilado como ValueTuple<int, int, int, int, int, int, int, ValueTuple<string, string>>. Isso cria uma ambiguidade quando a coluna do ClickHouse é uma tupla de 8 elementos em que o último elemento também é uma tupla — por exemplo, Tuple(Int32, Int32, Int32, Int32, Int32, Int32, Int32, Tuple(String, String)). O driver não consegue distinguir entre:
  • Uma tupla plana de 9 elementos (aninhamento TRest gerado pelo compilador)
  • Uma tupla de 8 elementos em que o último elemento é um Tuple(String, String) aninhado
Ambos produzem o mesmo tipo .NET: ValueTuple<int, int, int, int, int, int, int, ValueTuple<string, string>>. O driver trata o 8º argumento como TRest (ou seja, o expande), o que significa que o caso de 8 elementos com tupla aninhada será serializado incorretamente. Isso afeta tanto System.Tuple quanto ValueTuple, já que ambos usam aninhamento TRest para >7 elementos. Tuple com 7 ou menos elementos, ou Tuple em que o último elemento não é uma tupla, não são afetadas. Solução alternativa: Envolva a tupla interna em uma camada extra para que o driver consiga distingui-la do aninhamento TRest:

Colunas do tipo AggregateFunction

Colunas do tipo AggregateFunction(...) não podem ser consultadas nem inseridas diretamente. Para inserir:
Para selecionar:

Última modificação em 23 de julho de 2026