-
ClickHouseClient(recomendado): un cliente de alto nivel, seguro para subprocesos, diseñado para usarse como singleton. Ofrece una API asíncrona sencilla para consultas e inserciones masivas. Es la mejor opción para la mayoría de las aplicaciones. -
ADO.NET (
ClickHouseDataSource,ClickHouseConnection,ClickHouseCommand): abstracciones estándar de base de datos de .NET. Son necesarias para la integración con ORM (Dapper, Linq2db) y cuando necesita compatibilidad con ADO.NET.ClickHouseBulkCopyes una clase auxiliar para insertar datos de forma eficiente mediante una conexión ADO.NET.ClickHouseBulkCopyestá obsoleto y se eliminará en una versión futura; en su lugar, useClickHouseClient.InsertBinaryAsync.
Guía de migración
- Actualiza tu archivo
.csprojcon el nuevo nombre del paqueteClickHouse.Drivery la versión más reciente en NuGet. - Actualiza en tu código todas las referencias de
ClickHouse.ClientaClickHouse.Driver.
Versiones de .NET compatibles
ClickHouse.Driver es compatible con las siguientes versiones de .NET:
- .NET 6.0
- .NET 8.0
- .NET 9.0
- .NET 10.0
Instalación
Inicio rápido
Configuración
- Cadena de conexión: pares clave/valor separados por punto y coma que especifican el host, las credenciales de autenticación y otras opciones de conexión.
- Objeto
ClickHouseClientSettings: objeto de configuración fuertemente tipado que puede cargarse desde archivos de configuración o establecerse en el código.
Configuración de la conexión
Formato de datos y serialización
Gestión de sesiones
El indicador
UseSession habilita la persistencia de la sesión del servidor, lo que permite usar sentencias SET y tablas temporales. Las sesiones se reinician tras 60 segundos de inactividad (timeout predeterminado). La duración de la sesión puede ampliarse configurando ajustes de sesión mediante sentencias de ClickHouse o la configuración del servidor.La clase ClickHouseConnection normalmente permite operaciones en paralelo (varios hilos pueden ejecutar consultas de forma concurrente). Sin embargo, habilitar el indicador UseSession lo limita a una sola consulta activa por conexión en un momento dado (esta es una limitación del lado del servidor).Seguridad
Configuración del cliente HTTP
Registro y depuración
Ajustes personalizados y roles
Al usar una cadena de conexión para establecer ajustes personalizados, usa el prefijo
set_, p. ej., “set_max_threads=4”. Al usar un objeto ClickHouseClientSettings, no uses el prefijo set_.Para ver la lista completa de ajustes disponibles, consulta aquí.Ejemplos de cadenas de conexión
Conexión básica
Con ajustes personalizados de ClickHouse
QueryOptions
QueryOptions permite anular la configuración del cliente para una consulta concreta. Todas las propiedades son opcionales y solo anulan los valores predeterminados del cliente cuando se especifican.
Ejemplo:
InsertOptions
InsertOptions amplía QueryOptions con opciones específicas para operaciones de inserción masiva mediante InsertBinaryAsync.
Todas las propiedades de
QueryOptions también están disponibles en InsertOptions.
Ejemplo:
Omitir la consulta de sondeo del esquema
InsertBinaryAsync envía una consulta SELECT ... WHERE 1=0 antes de cada inserción para detectar los tipos de columna. Para escenarios de alto rendimiento, puedes eliminar esta sobrecarga con dos opciones:
Opción 1: Proporcionar los tipos de columna explícitamente
Cuando conoces el esquema de la tabla en tiempo de compilación, pásalo directamente mediante ColumnTypes. No se envía ninguna consulta de esquema en absoluto:
UseSchemaCache = true para consultar el esquema una sola vez y reutilizarlo en las inserciones posteriores de la misma instancia de ClickHouseClient:
ColumnTypestiene prioridad sobreUseSchemaCache. Si se configuran ambos, se usan los tipos explícitos.- La caché de esquema no detecta cambios realizados con
ALTER TABLE. Si modifica el esquema de la tabla, cree un nuevoClickHouseCliento eviteUseSchemaCachepara esa tabla. - La caché se limita a la instancia de
ClickHouseClienty se indexa por (database, table). Los distintos subconjuntos de columnas de una misma tabla comparten un único esquema en caché.
ClickHouseClient
ClickHouseClient es la API recomendada para interactuar con ClickHouse. Es seguro para subprocesos, está diseñado para usarse como singleton y gestiona internamente el pool de conexiones HTTP.
Crear un cliente
ClickHouseClient con una cadena de conexión o un objeto ClickHouseClientSettings. Consulte la sección Configuración para ver las opciones disponibles.
Los detalles de su servicio de ClickHouse Cloud están disponibles en la consola de ClickHouse Cloud.
Seleccione un servicio y haga clic en Connect:
Elija C#. Los detalles de la conexión se muestran a continuación.
Si utiliza ClickHouse autogestionado, los detalles de la conexión los establece el administrador de ClickHouse.
Usar una cadena de conexión:
ClickHouseClientSettings:
IHttpClientFactory:
ClickHouseClient está diseñado para ser de larga duración y para compartirse en toda la aplicación. Créelo una sola vez (normalmente como un singleton) y reutilícelo para todas las operaciones de base de datos. El cliente administra internamente el pool de conexiones HTTP.Ejecutar consultas
ExecuteNonQueryAsync para las sentencias que no devuelven resultados:
ExecuteScalarAsync para obtener un único valor:
Insertar datos
Inserciones parametrizadas
ExecuteNonQueryAsync. Los tipos de los parámetros deben especificarse en el SQL usando la sintaxis {name:Type}:
Inserciones masivas
InsertBinaryAsync para insertar un gran número de filas de forma eficiente. Transmite los datos mediante el formato binario nativo de filas de ClickHouse, admite envíos por lotes en paralelo y evita los errores de “URL too long” que pueden producirse con consultas parametrizadas.
InsertOptions:
- El cliente obtiene automáticamente la estructura de la tabla mediante
SELECT * FROM <table> WHERE 1=0antes de insertar. Los valores proporcionados deben coincidir con los tipos de las columnas de destino. Para omitir esta consulta, useInsertOptions.ColumnTypesoInsertOptions.UseSchemaCache. - Cuando
MaxDegreeOfParallelism > 1, los batches se cargan en paralelo. Las sesiones no son compatibles con la inserción en paralelo; desactive las sesiones o establezcaMaxDegreeOfParallelism = 1. - Use
RowBinaryFormat.RowBinaryWithDefaultsenInsertOptions.Formatsi desea que el servidor aplique valores DEFAULT a las columnas no proporcionadas.
Inserciones con POCO
object[], puede insertar directamente objetos POCO fuertemente tipados. Registre el tipo una sola vez y luego pase IEnumerable<T>:
Cuando todas las propiedades mapeadas especifican un
Type explícito, la consulta de sondeo del esquema se omite por completo. Cuando solo algunas propiedades tienen tipos explícitos, el driver recurre a la consulta de sondeo del esquema para el conjunto completo de columnas.
InsertBinaryAsync<T> admite las mismas InsertOptions (agrupación en lotes, paralelismo, almacenamiento en caché del esquema) que la sobrecarga object[].
A diferencia de la sobrecarga
object[], InsertBinaryAsync<T> no acepta una lista explícita de columnas. Las columnas se determinan a partir de las propiedades mapeadas del tipo registrado. Para controlar qué columnas se insertan, use [ClickHouseNotMapped] para excluir propiedades o [ClickHouseColumn(Name = "...")] para cambiarles el nombre.Si se establece ColumnTypes en InsertOptions, sobrescribirá los atributos del POCO.Evolución del esquema
DEFAULT (u otras expresiones predeterminadas) la rellena automáticamente el servidor. No se requieren cambios en el código ni volver a registrar nada.
Lectura de datos
ExecuteReaderAsync para ejecutar consultas SELECT. El ClickHouseDataReader devuelto proporciona acceso tipado a las columnas del resultado mediante métodos como GetInt64(), GetString() y GetFieldValue<T>().
Llame a Read() para avanzar a la fila siguiente. Devuelve false cuando ya no quedan más filas. Acceda a las columnas por índice (empezando en 0) o por nombre de columna.
Parámetros SQL
{parameter_name:DataType}.
Ejemplos:
Los parámetros SQL ‘bind’ se pasan como parámetros de consulta en la URI HTTP, por lo que usar demasiados puede provocar una excepción de “URL demasiado larga”. Use
InsertBinaryAsync para la inserción masiva de datos y así evitar esta limitación.ID de consulta
query_id único que puede usarse para obtener datos de la tabla system.query_log o cancelar consultas de larga duración. Puedes especificar un ID de consulta personalizado mediante QueryOptions:
Correspondencia personalizada de tipos de parámetros
@ (por ejemplo, WHERE id = @id), el driver infiere automáticamente el tipo de ClickHouse a partir del tipo de valor de .NET. Por ejemplo, int se corresponde con Int32, y DateTime con DateTime.
Para anular estos valores predeterminados, configure ParameterTypeResolver en ClickHouseClientSettings. Esto resulta útil cuando desea que todos los parámetros DateTime usen DateTime64(3) con precisión de milisegundos, o que todos los decimales usen una escala específica, sin tener que establecer ClickHouseType en cada parámetro individual.
Uso de DictionaryParameterTypeResolver para correspondencias de tipos simples:
IParameterTypeResolver personalizado para casos avanzados:
Para una resolución basada en el valor o en el nombre, implemente directamente la interfaz IParameterTypeResolver. Devuelva null para que se aplique la inferencia predeterminada:
QueryOptions.ParameterTypeResolver. Cuando se establece, tiene prioridad sobre el resolver a nivel de cliente.
Precedencia de la resolución de tipos:
El resolver es un paso dentro de una cadena de precedencia. De mayor a menor prioridad:
ClickHouseTypeexplícito establecido en el parámetro- Indicación de tipo SQL de la sintaxis
{name:Type}en la consulta IParameterTypeResolver(deQueryOptions.ParameterTypeResolver, con fallback aClickHouseClientSettings.ParameterTypeResolver)- Inferencia de tipos integrada (
TypeConverter.ToClickHouseType)
ClickHouseConnection de ADO.NET: las conexiones creadas desde el cliente heredan la configuración.
Transmisión sin procesar
ExecuteRawResultAsync para transmitir directamente los resultados de una consulta en un formato específico, omitiendo el lector de datos. Esto resulta útil para exportar datos a archivos o enviarlos a otros sistemas:
JSONEachRow, CSV, TSV, Parquet, Native. Consulta la documentación sobre formatos para conocer todas las opciones.
Inserción desde flujo sin procesar
InsertRawStreamAsync para insertar datos directamente desde flujos de archivo o de memoria en formatos como CSV, JSON, Parquet o cualquier formato compatible con ClickHouse.
Insertar desde un archivo CSV:
Consulte la documentación sobre la configuración de formatos para conocer las opciones que permiten controlar el comportamiento de la ingestión de datos.
Más ejemplos
ADO.NET
ClickHouseConnection, ClickHouseCommand y ClickHouseDataReader. Esta API es necesaria para la integración con ORM (Dapper, Linq2db) y cuando necesita las abstracciones estándar de bases de datos de .NET.
Gestión del ciclo de vida con ClickHouseDataSource
ClickHouseDataSource para garantizar una gestión correcta del ciclo de vida y del pool de conexiones. El DataSource administra internamente un único ClickHouseClient, y todas las conexiones comparten su pool de conexiones HTTP.
Uso de ClickHouseCommand
ExecuteNonQueryAsync()- Para INSERT, UPDATE, DELETE y sentencias DDLExecuteScalarAsync()- Devuelve la primera columna de la primera filaExecuteReaderAsync()- Devuelve unClickHouseDataReaderpara recorrer los resultados
Uso de ClickHouseDataReader
ClickHouseDataReader proporciona acceso tipado a los resultados de la consulta:
Buenas prácticas
Tiempo de vida de las conexiones y pool de conexiones
ClickHouse.Driver usa System.Net.Http.HttpClient internamente. HttpClient tiene un pool de conexiones por endpoint. Como consecuencia:
- Las sesiones de la base de datos se multiplexan a través de conexiones HTTP administradas por el pool de conexiones.
- El pool recicla automáticamente las conexiones HTTP.
- Las conexiones pueden permanecer activas después de desechar los objetos
ClickHouseClientoClickHouseConnection.
Gestión de DateTime
-
Usa UTC siempre que sea posible. Almacena las marcas de tiempo como columnas
DateTime('UTC')y usaDateTimeKind.Utcen tu código. Esto elimina la ambigüedad de la zona horaria. -
Usa
DateTimeOffsetpara gestionar explícitamente la zona horaria. Siempre representa un instante específico e incluye la información de desplazamiento. -
Especifica la zona horaria en las indicaciones de tipo de SQL. Al usar parámetros con valores
DateTimeUnspecifieddestinados a columnas que no son UTC, incluye la zona horaria en el SQL:
Inserciones asíncronas
CustomSettings o la cadena de conexión:
wait_for_async_insert):
Configuraciones clave:
Sesiones
- Tablas temporales (
CREATE TEMPORARY TABLE) - Mantener el contexto de la consulta entre varias sentencias
- Ajustes a nivel de sesión (
SET max_threads = 4)
Tipos de datos compatibles
ClickHouse.Driver admite todos los tipos de datos de ClickHouse. Las tablas siguientes muestran la correspondencia entre los tipos de ClickHouse y los tipos nativos de .NET al leer datos de la base de datos.
Correspondencia de tipos: lectura desde ClickHouse
Tipos enteros
Tipos de coma flotante
Tipos decimales
La conversión de tipos decimales se controla con la configuración UseCustomDecimals.
Tipo booleano
Tipos String
De forma predeterminada, las columnas
String y FixedString(N) se devuelven como string. Establezca ReadStringsAsByteArrays=true en la cadena de conexión para leerlas como byte[] en su lugar. Esto es útil cuando se almacenan datos binarios que podrían no ser UTF-8 válidos.Tipos de fecha y hora
ClickHouse almacena internamente los valores
DateTime y DateTime64 como marcas de tiempo Unix (segundos o fracciones de segundo desde la época Unix). Aunque el almacenamiento siempre está en UTC, las columnas pueden tener una zona horaria asociada que afecta a cómo se muestran e interpretan los valores.
Al leer valores DateTime, la propiedad DateTime.Kind se establece en función de la zona horaria de la columna:
Para las columnas que no son UTC, el
DateTime devuelto representa la hora local en esa zona horaria. Usa ClickHouseDataReader.GetDateTimeOffset() para obtener un DateTimeOffset con el desplazamiento correcto para esa zona horaria:
DateTime en lugar de DateTime('Europe/Amsterdam')), el driver devuelve un DateTime con Kind=Unspecified. Esto conserva la hora local exactamente tal como está almacenada, sin hacer suposiciones sobre la zona horaria.
Si necesita un comportamiento con reconocimiento de zona horaria para columnas sin una zona horaria explícita, haga una de estas dos cosas:
- Use zonas horarias explícitas en las definiciones de sus columnas:
DateTime('UTC')oDateTime('Europe/Amsterdam') - Aplique usted mismo la zona horaria después de leer el valor.
Tipo JSON
El tipo de retorno de las columnas JSON está determinado por la configuración
JsonReadMode:
-
Binary(predeterminado): DevuelveSystem.Text.Json.Nodes.JsonObject. Proporciona acceso estructurado a los datos JSON, pero los tipos especializados de ClickHouse (como direcciones IP, UUIDs y decimales grandes) se convierten a su representación en cadena dentro de la estructura JSON. -
String: Devuelve el JSON sin procesar comostring. Conserva la representación exacta del JSON de ClickHouse, lo que resulta útil cuando necesitas pasar el JSON sin analizarlo o cuando quieres encargarte tú mismo de la deserialización.
Otros tipos
Los tipos Dynamic y Variant se convertirán al tipo correspondiente según el tipo subyacente real de cada fila.
Tipos de geometría
El tipo Geometry es un tipo Variant que puede contener cualquiera de los tipos de geometría. Se convertirá al tipo correspondiente.
Correspondencia de tipos: escritura en ClickHouse
Tipos enteros
Tipos de coma flotante
Tipo booleano
Tipos de cadena
Tipos de fecha y hora
El driver respeta
DateTime.Kind al escribir valores:
Los valores
DateTimeOffset siempre conservan el instante exacto.
Ejemplo: DateTime UTC (se conserva el instante)
DateTimeKind.Utc o DateTimeOffset para todas las operaciones con DateTime. Esto garantiza que su código funcione de forma coherente independientemente de la zona horaria del servidor, la zona horaria del cliente o la zona horaria de la columna.
Parámetros HTTP vs Bulk Copy
Unspecified:
Bulk Copy conoce la zona horaria de la columna de destino e interpreta correctamente los valores Unspecified en esa zona horaria.
HTTP Parameters no conocen automáticamente la zona horaria de la columna. Debe especificarla en la indicación de tipo de SQL:
Tipos Decimal
Tipo JSON
El comportamiento al escribir JSON está controlado por el ajuste
JsonWriteMode:
-
String(predeterminado): Aceptastring,JsonObject,JsonNodeo cualquier objeto. Todas las entradas se serializan medianteSystem.Text.Json.JsonSerializery se envían como cadenas JSON para que el servidor las procese. Este es el modo más flexible y funciona sin registrar tipos. -
Binary: Solo acepta tipos POCO registrados. Los datos se convierten en el cliente al formato JSON binario de ClickHouse, con compatibilidad completa con indicaciones de tipo. Requiere llamar aconnection.RegisterJsonSerializationType<T>()antes de usarlo. Escribir valoresstringoJsonNodeen este modo lanzaArgumentException.
Columnas JSON tipadas
JSON(id UInt64, price Decimal128(2))), el driver usa estas indicaciones para serializar los valores respetando plenamente sus tipos. Esto preserva la precisión de tipos como UInt64, Decimal, UUID y DateTime64, que de otro modo la perderían al serializarse como JSON genérico.
Serialización de POCO
JsonWriteMode:
Modo String (predeterminado): los POCO se serializan mediante System.Text.Json.JsonSerializer. No es necesario registrar tipos. Es el enfoque más sencillo y funciona con objetos anónimos.
Modo binario: los POCO se serializan usando el formato JSON binario del driver, con compatibilidad completa con indicaciones de tipo. Los tipos deben registrarse con connection.RegisterJsonSerializationType<T>() antes de usarlos. Este modo admite asignaciones de rutas personalizadas mediante atributos:
-
[ClickHouseJsonPath("path")]: Asigna una propiedad a una ruta JSON personalizada. Es útil para estructuras anidadas o cuando el nombre de la propiedad difiere de la clave JSON deseada. Solo funciona en modo binario. -
[ClickHouseJsonIgnore]: Excluye una propiedad de la serialización. Solo funciona en modo binario.
UserId solo coincidirá con una indicación definida como UserId, no como userid. Esto sigue el comportamiento de ClickHouse, que permite que rutas como userName y UserName coexistan como campos independientes.
Limitaciones (solo en modo Binary):
- Los tipos POCO deben registrarse en la conexión con
connection.RegisterJsonSerializationType<T>()antes de serializarse. Si se intenta serializar un tipo no registrado, se lanzaClickHouseJsonSerializationException. - Las propiedades de diccionario y array/lista requieren indicaciones de tipo en la definición de la columna para serializarse correctamente. Sin esas indicaciones, use el modo String.
- Los valores NULL en las propiedades POCO solo se escriben cuando la ruta tiene una indicación de tipo
Nullable(T)en la definición de la columna. ClickHouse no permite tiposNullabledentro de rutas JSON dinámicas, por lo que las propiedades con valor NULL sin indicación se omiten. - Los atributos
ClickHouseJsonPathyClickHouseJsonIgnorese ignoran en modo String (solo funcionan en modo Binary).
Otros tipos
Tipos de geometría
No admitido para escritura
Manejo de tipos anidados
Nested(...)) se pueden leer y escribir usando la semántica de arrays.
Registro y diagnósticos
Microsoft.Extensions.Logging para ofrecer un registro ligero y opcional. Cuando está habilitado, el driver emite mensajes estructurados sobre eventos del ciclo de vida de la conexión, la ejecución de comandos, las operaciones de transporte y las operaciones de inserción masiva. El registro es totalmente opcional: las aplicaciones que no configuran un logger siguen ejecutándose sin sobrecarga adicional.
Primeros pasos
Uso de appsettings.json
Uso de la configuración en memoria
Categorías y emisores
Ejemplo: Cómo diagnosticar problemas de conexión
- Selección de la fábrica de clientes HTTP (pool predeterminado frente a conexión única)
- Configuración del controlador HTTP (
SocketsHttpHandleroHttpClientHandler) - Configuración del pool de conexiones (
MaxConnectionsPerServer,PooledConnectionLifetime, etc.) - Configuración de timeout (
ConnectTimeout,Expect100ContinueTimeout, etc.) - Configuración de SSL/TLS
- Eventos de apertura/cierre de conexiones
- Seguimiento del ID de sesión
Modo de depuración: tracing de red y diagnóstico
ClickHouse.Driver.Diagnostic.TraceHelper). Los eventos se registrarán en la categoría ClickHouse.Driver.NetTrace. Advertencia: esto generará logs extremadamente verbosos y afectará al rendimiento. No se recomienda habilitar el modo de depuración en producción.
OpenTelemetry
System.Diagnostics.Activity. Cuando está habilitado, el driver emite spans para las operaciones de base de datos que pueden exportarse a backends de observabilidad como Jaeger o al propio ClickHouse (mediante el OpenTelemetry Collector).
Habilitar el tracing
ActivitySource del driver de ClickHouse a su configuración de OpenTelemetry:
Atributos del span
Opciones de configuración
ClickHouseDiagnosticsOptions:
Configuración de TLS
Validación personalizada de certificados
HttpClient con un controlador ServerCertificateCustomValidationCallback configurado:
Consideraciones importantes al proporcionar un
HttpClient personalizado- Descompresión automática: Debes habilitar
AutomaticDecompressionsi la compresión no está desactivada (la compresión está activada de forma predeterminada). - Tiempo de espera de inactividad: Configura
PooledConnectionIdleTimeoutcon un valor inferior alkeep_alive_timeoutdel servidor (10 segundos en ClickHouse Cloud) para evitar errores de conexión causados por conexiones semiabiertas.
Compatibilidad con los ORM
ClickHouseConnection). Para gestionar correctamente el ciclo de vida de la conexión, cree las conexiones desde un ClickHouseDataSource:
Dapper
ClickHouse.Driver funciona con Dapper. El driver convierte automáticamente la sintaxis @parameter de Dapper a la sintaxis nativa {parameter:Type} de ClickHouse, e infiere los tipos a partir de los valores de .NET.
Usa ClickHouseDataSource para gestionar correctamente el ciclo de vida de la conexión:
Estilos para pasar parámetros
DynamicParameters (de un diccionario o de un objeto anónimo):
Consultas con POCOs
Sintaxis de parámetros nativa de ClickHouse
{param:Type} de ClickHouse con un Dictionary<string, object> para los valores de los parámetros. No combines la sintaxis @param con la sintaxis {param:Type} para el mismo parámetro.
WHERE IN
WHERE id IN (@Ids1, @Ids2, @Ids3), y el driver convierte cada parámetro expandido.
La función has() de ClickHouse con un parámetro Array también funciona:
Manejadores de tipos personalizados
ITuple, BigInteger y ClickHouseDecimal, requieren registrar manejadores al inicio:
Dapper.Contrib
GetAll<T>() y Get<T>(id) funcionan. Insert<T>() no: genera sintaxis de SQL Server (SCOPE_IDENTITY, []). En su lugar, se recomienda usar el método nativo InsertBinaryAsync de ClickHouseClient.
Limitaciones
Linq2db
DataConnection con el proveedor de ClickHouse:
BulkCopyAsync para realizar inserciones masivas de forma eficiente.
Entity Framework Core
SaveChanges, todo ello con los patrones habituales de EF Core.
- NuGet:
ClickHouse.EntityFrameworkCore - Código fuente: GitHub
Este proveedor está en desarrollo activo. La versión actual admite consultas LINQ (incluidos JOIN, subconsultas y operaciones de conjuntos),
INSERT mediante SaveChanges / BulkInsertAsync, migraciones con DDL completo (CREATE / ALTER / DROP) y la configuración del motor de tabla específica de ClickHouse. UPDATE / DELETE no son compatibles.Instalación
Inicio rápido
DbContext, y luego haz consultas con LINQ:
Tipos compatibles
Usa
ClickHouseDecimal (de ClickHouse.Driver.Numerics) en lugar de decimal cuando necesites toda la precisión de las columnas Decimal128/Decimal256: decimal de .NET está limitado a 28–29 dígitos significativos.
Operaciones LINQ compatibles
Where, OrderBy, Take, Skip, Select, First, Single, Any, All, Count, Distinct, AsNoTracking
GROUP BY y agregaciones: GroupBy con Count, LongCount, Sum, Average, Min, Max — incluido HAVING (.Where() después de .GroupBy()), varias agregaciones en una sola proyección y OrderBy sobre resultados agregados.
JOINs: Join (INNER) y patrones GroupJoin/SelectMany (LEFT y CROSS). LEFT JOIN devuelve null real para las filas sin coincidencia (consulta la semántica de null de LEFT JOIN más abajo).
Subconsultas: Contains / IN correlacionados, Any / EXISTS, All y subconsultas escalares en proyecciones.
Operaciones de conjuntos: Concat (→ UNION ALL), Union (→ UNION DISTINCT), Intersect, Except.
Colecciones locales insertadas en línea: los joins y Contains con colecciones en memoria (int[], List<T>, etc.) se traducen en una serie de UNION.
Métodos de cadena: Contains, StartsWith, EndsWith, IndexOf, Replace, Substring, Trim/TrimStart/TrimEnd, ToLower, ToUpper, Length, IsNullOrEmpty, Concat (y el operador +).
Funciones matemáticas: los métodos estándar de Math y MathF se traducen a sus equivalentes en ClickHouse — funciones aritméticas, logarítmicas, trigonométricas y auxiliares.
Semántica de NULL en LEFT JOIN
set_join_use_nulls=1 en cada connection path para ajustarse a las expectativas de Entity Framework sobre el comportamiento de JOIN.
Si su servidor ClickHouse o profile impide cambiar esta configuración (por ejemplo, un profile readonly=1), desactívelo con:
0 / "" en lugar de == null.
Inserción de datos
SaveChanges usa la API nativa InsertBinaryAsync del driver: codificación RowBinary con compresión GZip, mucho más eficiente que SQL con parámetros:
Added a Unchanged tras guardar, igual que con cualquier otro proveedor de EF Core.
El tamaño del lote es configurable (valor predeterminado: 1000):
Inserción masiva
BulkInsertAsync en lugar de SaveChanges. Es un método de extensión de DbContext que omite por completo el seguimiento de cambios, la resolución de identidad y la administración del estado de EF Core; llama directamente al método InsertBinaryAsync del driver con codificación RowBinary y compresión GZip.
Esto lo hace adecuado para cargar grandes volúmenes de datos cuando no necesita el seguimiento de entidades después de la inserción:
IEnumerable<T> — recorre las entidades en streaming sin cargarlas todas en memoria. El valor devuelto es el número de filas insertadas. Las entidades no se adjuntan al DbContext después de la inserción, por lo que no hay transición de estado Added → Unchanged.
Enumeraciones
Enum8/Enum16 de ClickHouse se pueden asignar a propiedades string o a tipos enum de C#. Al usar enumeraciones de C#, el proveedor convierte automáticamente entre la enumeración y su representación textual:
Conversiones de tipos personalizadas
ValueConverter de EF Core te permite mapear tipos personalizados a tipos que el proveedor ya admite. El proveedor nunca ve tu tipo personalizado: EF Core realiza la conversión en ese punto.
Conversión por propiedad:
Anotaciones de tipo de columna
string, int, DateTime, etc., el proveedor infiere automáticamente el tipo de ClickHouse. Para los tipos parametrizados y los envoltorios, debe especificar explícitamente el tipo de ClickHouse.
Uso de anotaciones de datos (atributos):
OnModelCreating:
Array(Nullable(Int32)) y LowCardinality(Nullable(String)) — el proveedor elimina automáticamente Nullable y LowCardinality en cada nivel de anidamiento.
Columnas Variant y Dynamic
Variant(T1, T2, ...) y Dynamic de ClickHouse se corresponden con object en .NET. Como object es demasiado genérico para la inferencia automática de tipos, debe declarar explícitamente el tipo de almacenamiento mediante .HasColumnType():
string, ulong, ulong[]).
Columnas JSON
Json de ClickHouse, que se corresponde con System.Text.Json.Nodes.JsonNode (principal) o string (mediante ValueConverter automático):
SaveChanges como con BulkInsertAsync:
string con un tipo de columna Json; el proveedor aplica automáticamente un ValueConverter:
- Sin traducción de rutas JSON —
entity.Data["name"]en LINQ no se corresponde con la sintaxis SQLdata.namede ClickHouse. Filtre por columnas no JSON e inspeccione el JSON en memoria. - Semántica de NULL — El tipo JSON de ClickHouse devuelve
{}(objeto vacío) para los valores NULL en lugar de SQL NULL. - Precisión de enteros — El JSON de ClickHouse almacena todos los enteros como
Int64. Al leerlo medianteJsonNode, useGetValue<long>()en lugar deGetValue<int>().
Motores de tablas
ToTable(name, t => ...). Si no se configura ningún motor, el proveedor usa MergeTree de forma predeterminada, con ORDER BY derivado de la clave primaria de la entidad.
Cláusulas del motor:
WithOrderBy, WithPartitionBy, WithPrimaryKey, WithSampleBy, WithTtl, WithSettings. Todas se aplican al generador de motores devuelto por HasXxxEngine().
Características a nivel de columna: HasCodec, HasTtl, HasComment, HasDefault — todas forman parte de las migraciones.
Índices de omisión de datos — mediante HasIndex(...).HasSkippingIndexType(...):
Migraciones
Limitaciones de las migraciones
Además de las migraciones, el proveedor tampoco admite aún:
UPDATE/DELETE- Transacciones:
BeginTransactiones una operación sin efecto. ClickHouse no admite transacciones ACID. - Traducción de consultas con rutas JSON:
entity.Data["key"]en LINQ no se traduce a la sintaxis SQLdata.keyde ClickHouse. Filtre por columnas que no sean JSON e inspeccione el JSON en memoria.
Limitaciones
Columnas de AggregateFunction
AggregateFunction(...) no se pueden consultar ni insertar directamente.
Para insertar: