Each example is a self-contained, runnable demonstration of one topic. dotnet run in this directory
runs them all against a live server, and CI does the same on every pull request that touches
examples/ or the driver, so an example that throws fails the build.
Examples are grouped by transport: Http/ uses ClickHouseClient / ClickHouseConnection over
HTTP, Tcp/ uses ClickHouseTcpClient over the native protocol. See Tcp/README.md for what is
specific to the native client.
cd examples
dotnet run # Run all examples
dotnet run -- --http # Run one transport's examples (or --tcp)
dotnet run -- --list # List class names, which --filter matches
dotnet run -- --filter basicusage # Run the examples whose class name matchesExampleConfig takes the server from environment variables, with localhost defaults. The "How to
run" section of README.md lists them. HTTP examples use port 8123, TCP examples port 9000.
Five steps. Skip any one of them and the example does not run.
-
Put the file in the transport and category it belongs to:
Http/<Category>/<Category>_0NN_<Topic>.csorTcp/<Category>/Tcp_0NN_<Topic>.cs. Take the next free number in that category. -
Declare
namespace ClickHouse.Driver.Examples;— not a namespace derived from the folder.ExampleRunnermatches that namespace exactly and will not find a class in any other one. -
Make it a
public static classwith apublic static Task Run(). Discovery requires both. The class name is what--listprints and what--filtermatches, so name it after the topic (BasicUsage), not after the file (Core_001_BasicUsage).A native-protocol example's class name must start with
Tcp(TcpBasicUsage). Every example shares one namespace, so it could not reuse an HTTP example's name anyway, and that prefix is howExampleInfo.Transportknows which endpoint to check before running it — and what--httpand--tcpselect on. -
Add it to
RunAllExamplesinProgram.cs, under its category banner, in file-number order. That list is hand-maintained so the run order and the banners stay meaningful. An example missing from it still compiles, still appears in--list, and still runs under--filter— it just never runs in CI, so nothing tells you when it breaks. -
Add it to the index in
README.md, in the matching section, with a one-line description.
Then run it (dotnet run -- --filter <topic>) and read the output. An example whose output does not
teach the topic is not finished.
Take the server from ExampleConfig, which resolves environment variables over localhost defaults:
ExampleConfig.CreateHttpClient()/CreateHttpConnection()for the common case.ExampleConfig.HttpConnectionStringwhere a constructor takes the string itself.$"{ExampleConfig.HttpConnectionString};SomeKey=value"to add a key the assembled string does not already set. It setsHost,Port,Username,PasswordandDatabase, so appending any of those would produce a duplicate.ExampleConfig.HttpBuilder()to change one of those five. It returns a fresh builder each call.
Seven examples are exempt, because configuration is their subject, they use a separate endpoint, or
they start their own server:
Core_002_ConnectionStringConfiguration, Core_003_DependencyInjection,
Auth_001_JwtAuthentication, Tables_003_CreateTableCloud, Testing_001_Testcontainers,
Tcp_003_Tls, Tcp_005_Testcontainers. A literal connection string inside a comment,
shown to teach the reader what one looks like, is also fine.
Ask ExampleConfig for the endpoint with HttpEndpoint or TcpEndpoint when an example has to name
or dial it itself. There are no Host/Port properties: a whole-string override would not reach
them, so an example reading one could print an endpoint it did not connect to.
Three need infrastructure the CI server does not have, so they are registered nowhere and run only by explicit filter:
Tables_002_CreateTableCluster— needs a ClickHouse cluster.Tables_003_CreateTableCloud— needs ClickHouse Cloud credentials.Auth_001_JwtAuthentication— needs a JWT.
Their class names are also in ExampleRunner._optIn, which is what keeps --http and --tcp from
running them: those select by reflection, so leaving an example out of RunAllExamples does not on
its own keep a transport run from picking it up.
If you add an example of that kind, leave it out of RunAllExamples, add its class name to
_optIn, and list it here. Otherwise the omission is indistinguishable from having forgotten step 4.
- Console output is the teaching surface. Print what you did and what came back, not just "OK".
- Show one thing well rather than covering an API exhaustively. A reader who wants the full surface reads the docs.
- Drop any table you create, in a
finally, andDROP TABLE IF EXISTSit before theCREATEas well. The second one is what lets a run survive an earlier run that was interrupted before itsfinally. - Name a table
example_<topic>, fixed, not unique per run. This project is not the test suites: it assumes one suite run at a time against a server, so it does not needCreateTableName. Two things do need a per-runGuid: a name the example needs the server not to have (a deliberately missing table), and a marker an example counts insystem.processesorsystem.query_log. A second run would otherwise make the first one's measurement or expected failure wrong. - Comments explain why the server or the driver behaves as it does, not what the line does.