diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 6b82f45..e3857a3 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -22,7 +22,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 name: Build outputs: commit_id: ${{ steps.get_commit_id.outputs.commit_id }} @@ -123,7 +123,7 @@ jobs: tar -zcvf server.tar.gz ./release test_build: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: matrix: dotnet: [ '6.0.x', '5.0.x' ] @@ -154,7 +154,7 @@ jobs: ls -al test_unit: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 needs: build strategy: matrix: diff --git a/README-CN.md b/README-CN.md index 79c2427..c95b437 100644 --- a/README-CN.md +++ b/README-CN.md @@ -1,74 +1,56 @@ -# C# 连接器 - -[English edition](./README.md) + +# TDengine C# Connector + + +| Github Action Tests | CodeCov | +|----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| ![actions](https://github.com/taosdata/taos-connector-dotnet/actions/workflows/linux.yml/badge.svg) | [![codecov](https://codecov.io/gh/taosdata/taos-connector-dotnet/graph/badge.svg?token=U30JZYDGMS)](https://codecov.io/gh/taosdata/taos-connector-dotnet) | + +[English](README.md) | 简体中文 + + +## 目录 + + +- [1. 简介](#1-简介) + - [1.1 连接方式](#11-连接方式) + - [1.2 dotnet 版本兼容性](#12-dotnet-版本兼容性) + - [1.3 支持的平台](#13-支持的平台) +- [2. 获取驱动](#2-获取驱动) +- [3. 文档](#3-文档) +- [4. 前置条件](#4-前置条件) +- [5. 构建](#5-构建) +- [6. 测试](#6-测试) + - [6.1 运行测试](#61-运行测试) + - [6.2 添加用例](#62-添加用例) + - [6.3 性能测试](#63-性能测试) +- [7. 提交 Issue](#7-提交-issue) +- [8. 提交 PR](#8-提交-pr) +- [9. 引用](#9-引用) +- [10. 许可证](#10-许可证) + +## 1. 简介 `TDengine.Connector` 是 TDengine 提供的 C# 语言连接器。C# 开发人员可以通过它开发存取 TDengine 集群数据的 C# 应用软件。 -`TDengine.Connector` 连接器支持通过 TDengine 客户端驱动(taosc)建立与 TDengine 运行实例的连接,提供数据写入、查询、数据订阅、schemaless 数据写入、参数绑定接口数据写入等功能。 `TDengine.Connector` 自 v3.0.1 起还支持 WebSocket 连接,提供数据写入、查询、参数绑定接口数据写入等功能。 - -本文介绍如何在 Linux 或 Windows 环境中安装 `TDengine.Connector`,并通过 `TDengine.Connector` 连接 TDengine 集群,进行数据写入、查询等基本操作。 - -**注意**: - -* `TDengine.Connector` 3.x 不兼容 TDengine 2.x,如果在运行 TDengine 2.x 版本的环境下需要使用 C# 连接器请使用 TDengine.Connector 的 1.x 版本 。 -* `TDengine.Connector` 3.1.0 版本进行了完整的重构,不再兼容 3.0.2 及以前版本。3.0.2 文档请参考 [nuget](https://www.nuget.org/packages/TDengine.Connector/3.0.2) - -`TDengine.Connector` 的源码托管在 [GitHub](https://github.com/taosdata/taos-connector-dotnet/tree/3.0)。 - -## 支持的平台 - -支持的平台和 TDengine 客户端驱动支持的平台一致。 - -**注意** TDengine 不再支持 32 位 Windows 平台。 +### 1.1 连接方式 -## 版本支持 +- 原生连接:通过客户端驱动程序 taosc 直接与服务端程序 taosd 建立连接。这种方式需要保证客户端的驱动程序 taosc 和服务端的 taosd 版本保持一致。 +- Websocket 连接: 通过 taosAdapter 组件提供的 WebSocket API 建立与 taosd 的连接,不依赖 TDengine 客户端驱动。 -| **Connector 版本** | **TDengine 版本** | **主要功能** | -|------------------|--------------------------|------------------------------| -| 3.1.5 | 3.3.2.0 及以上/3.1.2.0 及以上 | 修复 websocket sql 包含中文时长度计算错误 | -| 3.1.4 | 3.3.2.0 及以上/3.1.2.0 及以上 | 提升 websocket 查询和写入性能 | -| 3.1.3 | 3.2.1.0 及以上/3.1.1.18 及以上 | 支持 WebSocket 自动重连 | -| 3.1.2 | 3.2.1.0 及以上/3.1.1.18 及以上 | 修复 schemaless 资源释放 | -| 3.1.1 | 3.2.1.0 及以上/3.1.1.18 及以上 | 支持 varbinary 和 geometry 类型 | -| 3.1.0 | 3.2.1.0 及以上/3.1.1.18 及以上 | WebSocket 使用原生实现 | +我们推荐使用 WebSocket 连接方式。 详细说明请参考:[连接方式](https://docs.taosdata.com/develop/connect/#%E8%BF%9E%E6%8E%A5%E6%96%B9%E5%BC%8F) 。 -## 处理异常 +### 1.2 dotnet 版本兼容性 -`TDengine.Connector` 会抛出异常,应用程序需要处理异常。taosc 异常类型 `TDengineError`,包含错误码和错误信息,应用程序可以根据错误码和错误信息进行处理。 +- .NET Framework 4.6 及以上版本。 +- .NET 5.0 及以上版本。 -## TDengine DataType 和 C# DataType +### 1.3 支持的平台 -| TDengine DataType | C# Type | -|-------------------|------------------| -| TIMESTAMP | DateTime | -| TINYINT | sbyte | -| SMALLINT | short | -| INT | int | -| BIGINT | long | -| TINYINT UNSIGNED | byte | -| SMALLINT UNSIGNED | ushort | -| INT UNSIGNED | uint | -| BIGINT UNSIGNED | ulong | -| FLOAT | float | -| DOUBLE | double | -| BOOL | bool | -| BINARY | byte[] | -| NCHAR | string (utf-8编码) | -| JSON | byte[] | -| VARBINARY | byte[] | -| GEOMETRY | byte[] | +- 原生连接支持的平台和 TDengine 客户端驱动支持的平台一致。 +- WebSocket 连接支持所有能运行 .NET 运行时的平台。 -**注意**:JSON 类型仅在 tag 中支持。 - -## 安装步骤 - -### 安装前准备 - -* 安装 [.NET SDK](https://dotnet.microsoft.com/download) -* [Nuget 客户端](https://docs.microsoft.com/en-us/nuget/install-nuget-client-tools) (可选安装) -* 安装 TDengine 客户端驱动,具体步骤请参考[安装客户端驱动](https://docs.taosdata.com/develop/connect/#%E5%AE%89%E8%A3%85%E5%AE%A2%E6%88%B7%E7%AB%AF%E9%A9%B1%E5%8A%A8-taosc) - -### 安装连接器 +## 2. 获取驱动 可以在当前 .NET 项目的路径下,通过 dotnet CLI 添加 Nuget package `TDengine.Connector` 到当前项目。 @@ -84,1056 +66,65 @@ dotnet add package TDengine.Connector ``` -## 建立连接 - -Native 连接 - -``` csharp -var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); -using (var client = DbDriver.Open(builder)) -{ - Console.WriteLine("connected"); -} -``` - -WebSocket 连接 - -```csharp -var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); -using (var client = DbDriver.Open(builder)) -{ - Console.WriteLine("connected"); -} -``` - -ConnectionStringBuilder 支持的参数如下: -* protocol: 连接协议,可选值为 Native 或 WebSocket,默认为 Native -* host: TDengine 或 taosadapter 运行实例的地址 -* port: TDengine 或 taosadapter 运行实例的端口 - * 当 protocol 为 WebSocket 时 useSSL 为 false 时,port 默认为 6041 - * 当 protocol 为 WebSocket 时 useSSL 为 true 时,port 默认为 443 -* useSSL: 是否使用 SSL 连接,仅当 protocol 为 WebSocket 时有效,默认为 false -* token: 连接 TDengine cloud 的 token,仅当 protocol 为 WebSocket 时有效 -* username: 连接 TDengine 的用户名 -* password: 连接 TDengine 的密码 -* db: 连接 TDengine 的数据库 -* timezone: 解析时间结果的时区,默认为 `TimeZoneInfo.Local`,使用 `TimeZoneInfo.FindSystemTimeZoneById` 方法解析字符串为 `TimeZoneInfo` 对象。 -* connTimeout: WebSocket 连接超时时间,仅当 protocol 为 WebSocket 时有效,默认为 1 分钟,使用 `TimeSpan.Parse` 方法解析字符串为 `TimeSpan` 对象。 -* readTimeout: WebSocket 读超时时间,仅当 protocol 为 WebSocket 时有效,默认为 5 分钟,使用 `TimeSpan.Parse` 方法解析字符串为 `TimeSpan` 对象。 -* writeTimeout: WebSocket 写超时时间,仅当 protocol 为 WebSocket 时有效,默认为 10 秒,使用 `TimeSpan.Parse` 方法解析字符串为 `TimeSpan` 对象。 -* enableCompression: 是否启用 WebSocket 压缩(dotnet 版本 6 及以上,连接器版本 3.1.1 及以上生效),默认为 false -* autoReconnect: 是否自动重连(连接器版本 3.1.3 及以上生效),默认为 false -* reconnectRetryCount: 重连次数(连接器版本 3.1.3 及以上生效),默认为 3 -* reconnectIntervalMs: 重连间隔时间(连接器版本 3.1.3 及以上生效),默认为 2000 - -### 指定 URL 和 Properties 获取连接 - -C# 连接器不支持此功能 - -### 配置参数的优先级 - -C# 连接器不支持此功能 - -## 使用示例 - -### 创建数据库和表 - -Native 样例 - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace NativeQuery -{ - internal class Query - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("create database power"); - client.Exec("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -WebSocket 样例 - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace WSQuery -{ - internal class Query - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("create database power"); - client.Exec("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -### 插入数据 - -Native 样例 - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace NativeQuery -{ - internal class Query - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - string insertQuery = - "INSERT INTO " + - "power.d1001 USING power.meters TAGS(2,'California.SanFrancisco') " + - "VALUES " + - "('2023-10-03 14:38:05.000', 10.30000, 219, 0.31000) " + - "('2023-10-03 14:38:15.000', 12.60000, 218, 0.33000) " + - "('2023-10-03 14:38:16.800', 12.30000, 221, 0.31000) " + - "power.d1002 USING power.meters TAGS(3, 'California.SanFrancisco') " + - "VALUES " + - "('2023-10-03 14:38:16.650', 10.30000, 218, 0.25000) " + - "power.d1003 USING power.meters TAGS(2,'California.LosAngeles') " + - "VALUES " + - "('2023-10-03 14:38:05.500', 11.80000, 221, 0.28000) " + - "('2023-10-03 14:38:16.600', 13.40000, 223, 0.29000) " + - "power.d1004 USING power.meters TAGS(3,'California.LosAngeles') " + - "VALUES " + - "('2023-10-03 14:38:05.000', 10.80000, 223, 0.29000) " + - "('2023-10-03 14:38:06.500', 11.50000, 221, 0.35000)"; - client.Exec(insertQuery); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -WebSocket 样例 - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace WSQuery -{ - internal class Query - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - string insertQuery = - "INSERT INTO " + - "power.d1001 USING power.meters TAGS(2,'California.SanFrancisco') " + - "VALUES " + - "('2023-10-03 14:38:05.000', 10.30000, 219, 0.31000) " + - "('2023-10-03 14:38:15.000', 12.60000, 218, 0.33000) " + - "('2023-10-03 14:38:16.800', 12.30000, 221, 0.31000) " + - "power.d1002 USING power.meters TAGS(3, 'California.SanFrancisco') " + - "VALUES " + - "('2023-10-03 14:38:16.650', 10.30000, 218, 0.25000) " + - "power.d1003 USING power.meters TAGS(2,'California.LosAngeles') " + - "VALUES " + - "('2023-10-03 14:38:05.500', 11.80000, 221, 0.28000) " + - "('2023-10-03 14:38:16.600', 13.40000, 223, 0.29000) " + - "power.d1004 USING power.meters TAGS(3,'California.LosAngeles') " + - "VALUES " + - "('2023-10-03 14:38:05.000', 10.80000, 223, 0.29000) " + - "('2023-10-03 14:38:06.500', 11.50000, 221, 0.35000)"; - client.Exec(insertQuery); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -### 查询数据 - -Native 样例 - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace NativeQuery -{ - internal class Query - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("use power"); - string query = "SELECT * FROM meters"; - using (var rows = client.Query(query)) - { - while (rows.Read()) - { - Console.WriteLine($"{((DateTime)rows.GetValue(0)):yyyy-MM-dd HH:mm:ss.fff}, {rows.GetValue(1)}, {rows.GetValue(2)}, {rows.GetValue(3)}, {rows.GetValue(4)}, {Encoding.UTF8.GetString((byte[])rows.GetValue(5))}"); - } - } - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -WebSocket 样例 - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace WSQuery -{ - internal class Query - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("use power"); - string query = "SELECT * FROM meters"; - using (var rows = client.Query(query)) - { - while (rows.Read()) - { - Console.WriteLine($"{((DateTime)rows.GetValue(0)):yyyy-MM-dd HH:mm:ss.fff}, {rows.GetValue(1)}, {rows.GetValue(2)}, {rows.GetValue(3)}, {rows.GetValue(4)}, {Encoding.UTF8.GetString((byte[])rows.GetValue(5))}"); - } - } - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -### 执行带有 reqId 的 SQL - -Native 样例 - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace NativeQueryWithReqID -{ - internal abstract class QueryWithReqID - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec($"create database if not exists test_db",ReqId.GetReqId()); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -WebSocket 样例 - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace WSQueryWithReqID -{ - internal abstract class QueryWithReqID - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec($"create database if not exists test_db",ReqId.GetReqId()); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -### 通过参数绑定写入数据 - -Native 样例 - -```csharp -using System; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace NativeStmt -{ - internal abstract class NativeStmt - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("create database power"); - client.Exec( - "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); - using (var stmt = client.StmtInit()) - { - stmt.Prepare( - "Insert into power.d1001 using power.meters tags(2,'California.SanFrancisco') values(?,?,?,?)"); - var ts = new DateTime(2023, 10, 03, 14, 38, 05, 000); - stmt.BindRow(new object[] { ts, (float)10.30000, (int)219, (float)0.31000 }); - stmt.AddBatch(); - stmt.Exec(); - var affected = stmt.Affected(); - Console.WriteLine($"affected rows: {affected}"); - } - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } - } - } - } -} -``` - -WebSocket 样例 - -```csharp -using System; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace WSStmt -{ - internal abstract class WSStmt - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("create database power"); - client.Exec( - "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); - using (var stmt = client.StmtInit()) - { - stmt.Prepare( - "Insert into power.d1001 using power.meters tags(2,'California.SanFrancisco') values(?,?,?,?)"); - var ts = new DateTime(2023, 10, 03, 14, 38, 05, 000); - stmt.BindRow(new object[] { ts, (float)10.30000, (int)219, (float)0.31000 }); - stmt.AddBatch(); - stmt.Exec(); - var affected = stmt.Affected(); - Console.WriteLine($"affected rows: {affected}"); - } - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } - } - } - } -} -``` - -注意:使用 BindRow 需要注意原始 C# 列类型与 TDengine 列类型的需要一一对应,具体对应关系请参考 [TDengine DataType 和 C# DataType](#tdengine-datatype-和-c-datatype)。 - -### 无模式写入 - -Native 样例 - -```csharp -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace NativeSchemaless -{ - internal class Program - { - public static void Main(string[] args) - { - var builder = - new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - client.Exec("create database sml"); - client.Exec("use sml"); - var influxDBData = - "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000"; - client.SchemalessInsert(new string[] { influxDBData }, - TDengineSchemalessProtocol.TSDB_SML_LINE_PROTOCOL, - TDengineSchemalessPrecision.TSDB_SML_TIMESTAMP_NANO_SECONDS, 0, ReqId.GetReqId()); - var telnetData = "stb0_0 1626006833 4 host=host0 interface=eth0"; - client.SchemalessInsert(new string[] { telnetData }, - TDengineSchemalessProtocol.TSDB_SML_TELNET_PROTOCOL, - TDengineSchemalessPrecision.TSDB_SML_TIMESTAMP_MILLI_SECONDS, 0, ReqId.GetReqId()); - var jsonData = - "{\"metric\": \"meter_current\",\"timestamp\": 1626846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}"; - client.SchemalessInsert(new string[] { jsonData }, TDengineSchemalessProtocol.TSDB_SML_JSON_PROTOCOL, - TDengineSchemalessPrecision.TSDB_SML_TIMESTAMP_MILLI_SECONDS, 0, ReqId.GetReqId()); - } - } - } -} -``` - -WebSocket 样例 - -```csharp -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace WSSchemaless -{ - internal class Program - { - public static void Main(string[] args) - { - var builder = - new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - client.Exec("create database sml"); - client.Exec("use sml"); - var influxDBData = - "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000"; - client.SchemalessInsert(new string[] { influxDBData }, - TDengineSchemalessProtocol.TSDB_SML_LINE_PROTOCOL, - TDengineSchemalessPrecision.TSDB_SML_TIMESTAMP_NANO_SECONDS, 0, ReqId.GetReqId()); - var telnetData = "stb0_0 1626006833 4 host=host0 interface=eth0"; - client.SchemalessInsert(new string[] { telnetData }, - TDengineSchemalessProtocol.TSDB_SML_TELNET_PROTOCOL, - TDengineSchemalessPrecision.TSDB_SML_TIMESTAMP_MILLI_SECONDS, 0, ReqId.GetReqId()); - var jsonData = - "{\"metric\": \"meter_current\",\"timestamp\": 1626846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}"; - client.SchemalessInsert(new string[] { jsonData }, TDengineSchemalessProtocol.TSDB_SML_JSON_PROTOCOL, - TDengineSchemalessPrecision.TSDB_SML_TIMESTAMP_MILLI_SECONDS, 0, ReqId.GetReqId()); - } - } - } -} -``` - -### 执行带有 reqId 的无模式写入 - -```csharp -public void SchemalessInsert(string[] lines, TDengineSchemalessProtocol protocol, - TDengineSchemalessPrecision precision, - int ttl, long reqId) -``` - -### 数据订阅 - -#### 创建 Topic - -Native 样例 - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace NativeSubscription -{ - internal class Program - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("create database power"); - client.Exec("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); - client.Exec("CREATE TOPIC topic_meters as SELECT * from power.meters"); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -WebSocket 样例 - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace WSSubscription -{ - internal class Program - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("create database power"); - client.Exec("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); - client.Exec("CREATE TOPIC topic_meters as SELECT * from power.meters"); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -#### 创建 Consumer - -Native 样例 - -```csharp -var cfg = new Dictionary() -{ - { "group.id", "group1" }, - { "auto.offset.reset", "latest" }, - { "td.connect.ip", "127.0.0.1" }, - { "td.connect.user", "root" }, - { "td.connect.pass", "taosdata" }, - { "td.connect.port", "6030" }, - { "client.id", "tmq_example" }, - { "enable.auto.commit", "true" }, - { "msg.with.table.name", "false" }, -}; -var consumer = new ConsumerBuilder>(cfg).Build(); -``` - -WebSocket 样例 - -```csharp -var cfg = new Dictionary() -{ - { "td.connect.type", "WebSocket" }, - { "group.id", "group1" }, - { "auto.offset.reset", "latest" }, - { "td.connect.ip", "localhost" }, - { "td.connect.port", "6041" }, - { "useSSL", "false" }, - { "td.connect.user", "root" }, - { "td.connect.pass", "taosdata" }, - { "client.id", "tmq_example" }, - { "enable.auto.commit", "true" }, - { "msg.with.table.name", "false" }, -}; -var consumer = new ConsumerBuilder>(cfg).Build(); -``` - -consumer 支持的配置参数如下: -* td.connect.type: 连接类型,可选值为 Native 或 WebSocket,默认为 Native -* td.connect.ip: TDengine 或 taosadapter 运行实例的地址 -* td.connect.port: TDengine 或 taosadapter 运行实例的端口 - * 当 td.connect.type 为 WebSocket 且 useSSL 为 false 时,td.connect.port 默认为 6041 - * 当 td.connect.type 为 WebSocket 且 useSSL 为 true 时,td.connect.port 默认为 443 -* useSSL: 是否使用 SSL 连接,仅当 td.connect.type 为 WebSocket 时有效,默认为 false -* token: 连接 TDengine cloud 的 token,仅当 td.connect.type 为 WebSocket 时有效 -* td.connect.user: 连接 TDengine 的用户名 -* td.connect.pass: 连接 TDengine 的密码 -* group.id: 消费者组 ID -* client.id: 消费者 ID -* enable.auto.commit: 是否自动提交 offset,默认为 true -* auto.commit.interval.ms: 自动提交 offset 的间隔时间,默认为 5000 毫秒 -* auto.offset.reset: 当 offset 不存在时,从哪里开始消费,可选值为 earliest 或 latest,默认为 latest -* msg.with.table.name: 消息是否包含表名 -* ws.message.enableCompression: 是否启用 WebSocket 压缩(dotnet 版本 6 及以上,连接器版本 3.1.1 及以上生效),默认为 false -* ws.autoReconnect: 是否自动重连(连接器版本 3.1.3 及以上生效),默认为 false -* ws.reconnect.retry.count: 重连次数(连接器版本 3.1.3 及以上生效),默认为 3 -* ws.reconnect.interval.ms: 重连间隔时间(连接器版本 3.1.3 及以上生效),默认为 2000 - -支持订阅结果集 `Dictionary` key 为列名,value 为列值。 - -如果使用 object 接收列值,需要注意: -* 原始 C# 列类型与 TDengine 列类型的需要一一对应,具体对应关系请参考 [TDengine DataType 和 C# DataType](#tdengine-datatype-和-c-datatype)。 -* 列名与 class 属性名一致,并可读写。 -* 明确设置 value 解析器`ConsumerBuilder.SetValueDeserializer(new ReferenceDeserializer());` - -样例如下 - -结果 class - -```csharp - class Result - { - public DateTime ts { get; set; } - public float current { get; set; } - public int voltage { get; set; } - public float phase { get; set; } - } -``` - -设置解析器 - -```csharp -var tmqBuilder = new ConsumerBuilder(cfg); -tmqBuilder.SetValueDeserializer(new ReferenceDeserializer()); -var consumer = tmqBuilder.Build(); -``` - -也可实现自定义解析器,实现 `IDeserializer` 接口并通过`ConsumerBuilder.SetValueDeserializer`方法传入。 +## 3. 文档 -```csharp - public interface IDeserializer - { - T Deserialize(ITMQRows data, bool isNull, SerializationContext context); - } -``` - -#### 订阅消费数据 - -```csharp -consumer.Subscribe(new List() { "topic_meters" }); -while (true) -{ - using (var cr = consumer.Consume(500)) - { - if (cr == null) continue; - foreach (var message in cr.Message) - { - Console.WriteLine( - $"message {{{((DateTime)message.Value["ts"]).ToString("yyyy-MM-dd HH:mm:ss.fff")}, " + - $"{message.Value["current"]}, {message.Value["voltage"]}, {message.Value["phase"]}}}"); - } - } -} -``` - -#### 指定订阅 Offset - -```csharp -consumer.Assignment.ForEach(a => -{ - Console.WriteLine($"{a}, seek to 0"); - consumer.Seek(new TopicPartitionOffset(a.Topic, a.Partition, 0)); - Thread.Sleep(TimeSpan.FromSeconds(1)); -}); -``` - -#### 提交 Offset +- 开发示例请见 [开发指南](https://docs.taosdata.com/develop/),包含了数据写入、查询、无模式写入、参数绑定和数据订阅等示例。 +- 其他参考信息请见 [参考手册](https://docs.taosdata.com/reference/connector/csharp/),包含了版本历史、TDengine 对应版本以及 API 说明和常见问题等。 -```csharp -public void Commit(ConsumeResult consumerResult) -public List Commit() -public void Commit(IEnumerable offsets) -``` +## 4. 前置条件 -#### 关闭订阅 +* 安装 [.NET SDK](https://dotnet.microsoft.com/download) +* [Nuget 客户端](https://docs.microsoft.com/en-us/nuget/install-nuget-client-tools) (可选安装) +* 安装 TDengine 客户端驱动,具体步骤请参考 [安装客户端驱动](https://docs.taosdata.com/develop/connect/#%E5%AE%89%E8%A3%85%E5%AE%A2%E6%88%B7%E7%AB%AF%E9%A9%B1%E5%8A%A8-taosc) -```csharp -consumer.Unsubscribe(); -consumer.Close(); -``` +## 5. 构建 -#### 完整示例 +1. `dotnet restore` 还原项目依赖。 +2. `dotnet build --no-restore` 构建项目。 -Native 样例 +## 6. 测试 -```csharp -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using TDengine.Driver; -using TDengine.Driver.Client; -using TDengine.TMQ; +### 6.1 运行测试 -namespace NativeSubscription -{ - internal class Program - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("CREATE DATABASE power"); - client.Exec("USE power"); - client.Exec( - "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); - client.Exec("CREATE TOPIC topic_meters as SELECT * from power.meters"); - var cfg = new Dictionary() - { - { "group.id", "group1" }, - { "auto.offset.reset", "latest" }, - { "td.connect.ip", "127.0.0.1" }, - { "td.connect.user", "root" }, - { "td.connect.pass", "taosdata" }, - { "td.connect.port", "6030" }, - { "client.id", "tmq_example" }, - { "enable.auto.commit", "true" }, - { "msg.with.table.name", "false" }, - }; - var consumer = new ConsumerBuilder>(cfg).Build(); - consumer.Subscribe(new List() { "topic_meters" }); - Task.Run(InsertData); - while (true) - { - using (var cr = consumer.Consume(500)) - { - if (cr == null) continue; - foreach (var message in cr.Message) - { - Console.WriteLine( - $"message {{{((DateTime)message.Value["ts"]).ToString("yyyy-MM-dd HH:mm:ss.fff")}, " + - $"{message.Value["current"]}, {message.Value["voltage"]}, {message.Value["phase"]}}}"); - } - } - } - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - - static void InsertData() - { - var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - while (true) - { - client.Exec("INSERT into power.d1001 using power.meters tags(2,'California.SanFrancisco') values(now,11.5,219,0.30)"); - Task.Delay(1000).Wait(); - } - } - } - } -} -``` +1. 执行测试前确保已经安装 TDengine 服务端,并且已经启动 taosd 与 taosAdapter,数据库干净无数据。 +2. 项目目录下执行 `dotnet test` 运行测试,测试会连接到本地的 TDengine 服务器与 taosAdapter 进行测试。 +3. 测试成功会打印 `Test Run Successful`,测试失败会打印失败信息 `Test Run Failed`。 -WebSocket 样例 +### 6.2 添加用例 -```csharp -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using TDengine.Driver; -using TDengine.Driver.Client; -using TDengine.TMQ; +在 `test` 目录下添加测试用例,ADO.NET 的测试用例添加到 `test/Data.Tests`,客户端驱动的测试用例添加到 `test/Driver.Test/Client`。 +用例使用 xunit 框架。 -namespace WSSubscription -{ - internal class Program - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("CREATE DATABASE power"); - client.Exec("USE power"); - client.Exec( - "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); - client.Exec("CREATE TOPIC topic_meters as SELECT * from power.meters"); - var cfg = new Dictionary() - { - { "td.connect.type", "WebSocket" }, - { "group.id", "group1" }, - { "auto.offset.reset", "latest" }, - { "td.connect.ip", "localhost" }, - { "td.connect.port", "6041" }, - { "useSSL", "false" }, - { "td.connect.user", "root" }, - { "td.connect.pass", "taosdata" }, - { "client.id", "tmq_example" }, - { "enable.auto.commit", "true" }, - { "msg.with.table.name", "false" }, - }; - var consumer = new ConsumerBuilder>(cfg).Build(); - consumer.Subscribe(new List() { "topic_meters" }); - Task.Run(InsertData); - while (true) - { - using (var cr = consumer.Consume(500)) - { - if (cr == null) continue; - foreach (var message in cr.Message) - { - Console.WriteLine( - $"message {{{((DateTime)message.Value["ts"]).ToString("yyyy-MM-dd HH:mm:ss.fff")}, " + - $"{message.Value["current"]}, {message.Value["voltage"]}, {message.Value["phase"]}}}"); - } - } - } - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - - static void InsertData() - { - var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - while (true) - { - client.Exec("INSERT into power.d1001 using power.meters tags(2,'California.SanFrancisco') values(now,11.5,219,0.30)"); - Task.Delay(1000).Wait(); - } - } - } - } -} -``` +### 6.3 性能测试 -### ADO.NET +性能测试还在开发中。 -C# 连接器支持 ADO.NET 接口,可以通过 ADO.NET 接口连接 TDengine 运行实例,进行数据写入、查询等操作。 +## 7. 提交 Issue -Native 样例 +我们欢迎提交 [GitHub Issue](https://github.com/taosdata/taos-connector-dotnet/issues/new?template=Blank+issue)。 提交时请说明下面信息: -```csharp -using System; -using TDengine.Data.Client; +- 问题描述,是否必现。 +- 驱动版本。 +- 连接参数(不需要服务器地址、用户名和密码)。 +- TDengine 版本。 -namespace NativeADO -{ - internal class Program - { - public static void Main(string[] args) - { - const string connectionString = "host=localhost;port=6030;username=root;password=taosdata"; - using (var connection = new TDengineConnection(connectionString)) - { - try - { - connection.Open(); - using (var command = new TDengineCommand(connection)) - { - command.CommandText = "create database power"; - command.ExecuteNonQuery(); - connection.ChangeDatabase("power"); - command.CommandText = - "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"; - command.ExecuteNonQuery(); - command.CommandText = "INSERT INTO " + - "power.d1001 USING power.meters TAGS(2,'California.SanFrancisco') " + - "VALUES " + - "(?,?,?,?)"; - var parameters = command.Parameters; - parameters.Add(new TDengineParameter("@0", new DateTime(2023,10,03,14,38,05,000))); - parameters.Add(new TDengineParameter("@1", (float)10.30000)); - parameters.Add(new TDengineParameter("@2", (int)219)); - parameters.Add(new TDengineParameter("@3", (float)0.31000)); - command.ExecuteNonQuery(); - command.Parameters.Clear(); - command.CommandText = "SELECT * FROM meters"; - using (var reader = command.ExecuteReader()) - { - while (reader.Read()) - { - Console.WriteLine( - $"{((DateTime) reader.GetValue(0)):yyyy-MM-dd HH:mm:ss.fff}, {reader.GetValue(1)}, {reader.GetValue(2)}, {reader.GetValue(3)}, {reader.GetValue(4)}, {System.Text.Encoding.UTF8.GetString((byte[]) reader.GetValue(5))}"); - } - } - } - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } - } - } - } -} -``` +## 8. 提交 PR -WebSocket 样例 +我们欢迎开发者一起开发本项目,提交 PR 时请参考下面步骤: -```csharp -using System; -using TDengine.Data.Client; +1. Fork 本项目,请参考 ([how to fork a repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo)) +2. 从 main 分支创建一个新分支,请使用有意义的分支名称 (`git checkout -b my_branch`)。 +3. 修改代码,保证所有单元测试通过,并增加新的单元测试验证修改。 +4. 提交修改到远端分支 (`git push origin my_branch`)。 +5. 在 GitHub 上创建一个 Pull + Request ([how to create a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request))。 +6. 提交 PR 后,如果 CI 通过,可以在 [codecov](https://app.codecov.io/gh/taosdata/taos-connector-dotnet/pulls) 页面找到自己 PR 查看覆盖率。 -namespace WSADO -{ - internal class Program - { - public static void Main(string[] args) - { - const string connectionString = "protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"; - using (var connection = new TDengineConnection(connectionString)) - { - try - { - connection.Open(); - using (var command = new TDengineCommand(connection)) - { - command.CommandText = "create database power"; - command.ExecuteNonQuery(); - connection.ChangeDatabase("power"); - command.CommandText = - "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"; - command.ExecuteNonQuery(); - command.CommandText = "INSERT INTO " + - "power.d1001 USING power.meters TAGS(2,'California.SanFrancisco') " + - "VALUES " + - "(?,?,?,?)"; - var parameters = command.Parameters; - parameters.Add(new TDengineParameter("@0", new DateTime(2023,10,03,14,38,05,000))); - parameters.Add(new TDengineParameter("@1", (float)10.30000)); - parameters.Add(new TDengineParameter("@2", (int)219)); - parameters.Add(new TDengineParameter("@3", (float)0.31000)); - command.ExecuteNonQuery(); - command.Parameters.Clear(); - command.CommandText = "SELECT * FROM meters"; - using (var reader = command.ExecuteReader()) - { - while (reader.Read()) - { - Console.WriteLine( - $"{((DateTime) reader.GetValue(0)):yyyy-MM-dd HH:mm:ss.fff}, {reader.GetValue(1)}, {reader.GetValue(2)}, {reader.GetValue(3)}, {reader.GetValue(4)}, {System.Text.Encoding.UTF8.GetString((byte[]) reader.GetValue(5))}"); - } - } - } - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } - } - } - } -} -``` +## 9. 引用 -* 连接参数与[建立连接](#建立连接)中的连接参数一致。 -* TDengineParameter 的 name 需要以 @ 开头,如 @0、@1、@2 等,value 需要 C# 列类型与 TDengine - 列类型一一对应,具体对应关系请参考 [TDengine DataType 和 C# DataType](#tdengine-datatype-和-c-datatype)。 +- [TDengine 官网](https://www.taosdata.com/) +- [TDengine GitHub](https://github.com/taosdata/TDengine) -### 更多示例程序 +## 10. 许可证 -[示例程序](https://github.com/taosdata/taos-connector-dotnet/tree/3.0/examples) \ No newline at end of file +[MIT License](./LICENSE) \ No newline at end of file diff --git a/README.md b/README.md index d5de295..96a7cf2 100644 --- a/README.md +++ b/README.md @@ -1,74 +1,60 @@ -# C# Connector - -[中文版](./README-CN.md) + +# TDengine C# Connector + + +| Github Action Tests | CodeCov | +|-----------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| ![actions](https://github.com/taosdata/taos-connector-dotnet/actions/workflows/linux.yml/badge.svg) | [![codecov](https://codecov.io/gh/taosdata/taos-connector-dotnet/graph/badge.svg?token=U30JZYDGMS)](https://codecov.io/gh/taosdata/taos-connector-dotnet) | + +English | [简体中文](README-CN.md) + + +## Table of Contents + + +- [1. Introduction](#1-introduction) + - [1.1 Connection Methods](#11-connection-methods) + - [1.2 .NET Version Compatibility](#12-net-version-compatibility) + - [1.3 Supported Platforms](#13-supported-platforms) +- [2. Get the Driver](#2-get-the-driver) +- [3. Documentation](#3-documentation) +- [4. Prerequisites](#4-prerequisites) +- [5. Build](#5-build) +- [6. Testing](#6-testing) + - [6.1 Test Execution](#61-test-execution) + - [6.2 Test Case Addition](#62-test-case-addition) + - [6.3 Performance Testing](#63-performance-testing) +- [7. Submitting Issues](#7-submitting-issues) +- [8. Submitting PRs](#8-submitting-prs) +- [9. References](#9-references) +- [10. License](#10-license) + + +## 1. Introduction `TDengine.Connector` is the C# language connector provided by TDengine. C# developers can use it to develop C# application software that accesses TDengine cluster data. -The `TDengine.Connector` connector supports establishing a connection with the TDengine running instance through the TDengine client driver (taosc), and provides functions such as data writing, query, data subscription, schemaless data writing, and parameter binding interface data writing. `TDengine.Connector` also supports WebSocket since v3.0.1, establishes WebSocket connection, and provides functions such as data writing, query, and parameter binding interface data writing. - -This article introduces how to install `TDengine.Connector` in a Linux or Windows environment, and connect to the TDengine cluster through `TDengine.Connector` to perform basic operations such as data writing and querying. - -**Notice**: - -* `TDengine.Connector` 3.x is not compatible with TDengine 2.x. If you need to use the C# connector in an environment running TDengine 2.x version, please use the 1.x version of TDengine.Connector. -* `TDengine.Connector` version 3.1.0 has been completely refactored and is no longer compatible with 3.0.2 and previous versions. For 3.0.2 documents, please refer to [nuget](https://www.nuget.org/packages/TDengine.Connector/3.0.2) - -The source code of `TDengine.Connector` is hosted on [GitHub](https://github.com/taosdata/taos-connector-dotnet/tree/3.0). - -## Supported platforms - -The supported platforms are the same as those supported by the TDengine client driver. - -Note TDengine no longer supports 32-bit Windows platforms. +### 1.1 Connection Methods -## Version support +- Native Connection: Establishes a connection directly with the server program taosd through the client driver taosc. + This method requires the client driver taosc and the server taosd to be of the same version. +- WebSocket Connection: Establishes a connection with taosd through the WebSocket API provided by the taosAdapter + component, without relying on the TDengine client driver. -| **Connector version** | **TDengine version** | **major features** | -|-----------------------|--------------------------------------|----------------------------------------------------------------------| -| 3.1.5 | 3.3.2.0 and above/3.1.2.0 and above | fix the length calculation error when WebSocket sql contains Chinese | -| 3.1.4 | 3.3.2.0 and above/3.1.2.0 and above | WebSocket performance improvements | -| 3.1.3 | 3.2.1.0 and above/3.1.1.18 and above | support Websocket reconnect | -| 3.1.2 | 3.2.1.0 and above/3.1.1.18 and above | fix schemaless result release | -| 3.1.1 | 3.2.1.0 and above/3.1.1.18 and above | support varbinary and geometry | -| 3.1.0 | 3.2.1.0 and above/3.1.1.18 and above | WebSocket uses native implementation | +We recommend using the WebSocket connection method. For detailed instructions, please refer +to: [Connection Methods](https://docs.tdengine.com/developer-guide/connecting-to-tdengine/#connection-methods). -## Handling exceptions +### 1.2 .NET Version Compatibility -`TDengine.Connector` will throw an exception and the application needs to handle the exception. The taosc exception type `TDengineError` contains error code and error information, and the application can handle it based on the error code and error information. +- .NET Framework 4.6 and above +- .NET 5.0 and above -## TDengine DataType vs. C# DataType +### 1.3 Supported Platforms -| TDengine DataType | C# Type | -|-------------------|-------------------------| -| TIMESTAMP | DateTime | -| TINYINT | sbyte | -| SMALLINT | short | -| INT | int | -| BIGINT | long | -| TINYINT UNSIGNED | byte | -| SMALLINT UNSIGNED | ushort | -| INT UNSIGNED | uint | -| BIGINT UNSIGNED | ulong | -| FLOAT | float | -| DOUBLE | double | -| BOOL | bool | -| BINARY | byte[] | -| NCHAR | string (utf-8 encoding) | -| JSON | byte[] | -| VARBINARY | byte[] | -| GEOMETRY | byte[] | +- The platforms supported by the native connection are consistent with those supported by the TDengine client driver. +- WebSocket connections support all platforms that can run .NET. -**Note**: JSON type is only supported in tag. - -## Installation Steps - -### Pre-installation preparation - -* Install [.NET SDK](https://dotnet.microsoft.com/download) -* [Nuget Client](https://docs.microsoft.com/en-us/nuget/install-nuget-client-tools) (optional installation) -* Install the TDengine client driver. For specific steps, please refer to [Installing the client driver](https://docs.tdengine.com/develop/connect/#install-client-driver-taosc) - -### Install the connectors +## 2. Get the Driver Nuget package `TDengine.Connector` can be added to the current project through dotnet CLI under the path of the current .NET project. @@ -84,1057 +70,77 @@ You can also modify the `.csproj` file of the current project and add the follow ``` -## Establishing a connection - -Native connection - -``` csharp -var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); -using (var client = DbDriver.Open(builder)) -{ - Console.WriteLine("connected"); -} -``` - -WebSocket connection - -```csharp -var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); -using (var client = DbDriver.Open(builder)) -{ - Console.WriteLine("connected"); -} -``` - -The parameters supported by `ConnectionStringBuilder` are as follows: -* protocol: connection protocol, optional value is Native or WebSocket, default is Native -* host: the address of the running instance of TDengine or taosadapter, -* port: The port of the running instance of TDengine or taosadapter. - * When using WebSocket without SSL, the default is 6041. - * When using WebSocket with SSL, the default is 443. -* useSSL: Whether to use SSL, the default is false, only valid when the protocol is WebSocket -* token: The token used to connect to TDengine Cloud, only valid when the protocol is WebSocket -* username: username to connect to TDengine -* password: password to connect to TDengine -* db: database connected to TDengine -* timezone: The time zone for parsing time results, the default is `TimeZoneInfo.Local`, use the `TimeZoneInfo.FindSystemTimeZoneById` method to parse the string into a `TimeZoneInfo` object. -* connTimeout: WebSocket connection timeout, only valid when the protocol is WebSocket, the default is 1 minute, use the `TimeSpan.Parse` method to parse the string into a `TimeSpan` object. -* readTimeout: WebSocket read timeout, only valid when the protocol is WebSocket, the default is 5 minutes, use the `TimeSpan.Parse` method to parse the string into a `TimeSpan` object. -* writeTimeout: WebSocket write timeout, only valid when the protocol is WebSocket, the default is 10 seconds, use the `TimeSpan.Parse` method to parse the string into a `TimeSpan` object. -* enableCompression: Whether to enable WebSocket compression (effective for dotnet version 6 and above, connector version 3.1.1 and above). The default is false. -* autoReconnect: Whether to enable WebSocket reconnect (connector version 3.1.3 and above). The default is false. -* reconnectRetryCount: The number of reconnection retries (connector version 3.1.3 and above). The default is 3. -* reconnectIntervalMs: The interval between reconnection retries (connector version 3.1.3 and above). The default is - 2000. - -### Specify the URL and Properties to get the connection - -The C# connector does not support this feature - -### Priority of configuration parameters - -The C# connector does not support this feature - -## Usage examples - -### Create database and tables - -Native Example - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace NativeQuery -{ - internal class Query - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("create database power"); - client.Exec("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -WebSocket Example - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace WSQuery -{ - internal class Query - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("create database power"); - client.Exec("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -### Insert data - -Native Example - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace NativeQuery -{ - internal class Query - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - string insertQuery = - "INSERT INTO " + - "power.d1001 USING power.meters TAGS(2,'California.SanFrancisco') " + - "VALUES " + - "('2023-10-03 14:38:05.000', 10.30000, 219, 0.31000) " + - "('2023-10-03 14:38:15.000', 12.60000, 218, 0.33000) " + - "('2023-10-03 14:38:16.800', 12.30000, 221, 0.31000) " + - "power.d1002 USING power.meters TAGS(3, 'California.SanFrancisco') " + - "VALUES " + - "('2023-10-03 14:38:16.650', 10.30000, 218, 0.25000) " + - "power.d1003 USING power.meters TAGS(2,'California.LosAngeles') " + - "VALUES " + - "('2023-10-03 14:38:05.500', 11.80000, 221, 0.28000) " + - "('2023-10-03 14:38:16.600', 13.40000, 223, 0.29000) " + - "power.d1004 USING power.meters TAGS(3,'California.LosAngeles') " + - "VALUES " + - "('2023-10-03 14:38:05.000', 10.80000, 223, 0.29000) " + - "('2023-10-03 14:38:06.500', 11.50000, 221, 0.35000)"; - client.Exec(insertQuery); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -WebSocket Example - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace WSQuery -{ - internal class Query - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - string insertQuery = - "INSERT INTO " + - "power.d1001 USING power.meters TAGS(2,'California.SanFrancisco') " + - "VALUES " + - "('2023-10-03 14:38:05.000', 10.30000, 219, 0.31000) " + - "('2023-10-03 14:38:15.000', 12.60000, 218, 0.33000) " + - "('2023-10-03 14:38:16.800', 12.30000, 221, 0.31000) " + - "power.d1002 USING power.meters TAGS(3, 'California.SanFrancisco') " + - "VALUES " + - "('2023-10-03 14:38:16.650', 10.30000, 218, 0.25000) " + - "power.d1003 USING power.meters TAGS(2,'California.LosAngeles') " + - "VALUES " + - "('2023-10-03 14:38:05.500', 11.80000, 221, 0.28000) " + - "('2023-10-03 14:38:16.600', 13.40000, 223, 0.29000) " + - "power.d1004 USING power.meters TAGS(3,'California.LosAngeles') " + - "VALUES " + - "('2023-10-03 14:38:05.000', 10.80000, 223, 0.29000) " + - "('2023-10-03 14:38:06.500', 11.50000, 221, 0.35000)"; - client.Exec(insertQuery); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -### Querying data - -Native Example - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace NativeQuery -{ - internal class Query - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("use power"); - string query = "SELECT * FROM meters"; - using (var rows = client.Query(query)) - { - while (rows.Read()) - { - Console.WriteLine($"{((DateTime)rows.GetValue(0)):yyyy-MM-dd HH:mm:ss.fff}, {rows.GetValue(1)}, {rows.GetValue(2)}, {rows.GetValue(3)}, {rows.GetValue(4)}, {Encoding.UTF8.GetString((byte[])rows.GetValue(5))}"); - } - } - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -WebSocket Example - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace WSQuery -{ - internal class Query - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("use power"); - string query = "SELECT * FROM meters"; - using (var rows = client.Query(query)) - { - while (rows.Read()) - { - Console.WriteLine($"{((DateTime)rows.GetValue(0)):yyyy-MM-dd HH:mm:ss.fff}, {rows.GetValue(1)}, {rows.GetValue(2)}, {rows.GetValue(3)}, {rows.GetValue(4)}, {Encoding.UTF8.GetString((byte[])rows.GetValue(5))}"); - } - } - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -### execute SQL with reqId - -Native Example - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace NativeQueryWithReqID -{ - internal abstract class QueryWithReqID - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec($"create database if not exists test_db",ReqId.GetReqId()); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -WebSocket Example - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace WSQueryWithReqID -{ - internal abstract class QueryWithReqID - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec($"create database if not exists test_db",ReqId.GetReqId()); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -### Writing data via parameter binding - -Native Example - -```csharp -using System; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace NativeStmt -{ - internal abstract class NativeStmt - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("create database power"); - client.Exec( - "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); - using (var stmt = client.StmtInit()) - { - stmt.Prepare( - "Insert into power.d1001 using power.meters tags(2,'California.SanFrancisco') values(?,?,?,?)"); - var ts = new DateTime(2023, 10, 03, 14, 38, 05, 000); - stmt.BindRow(new object[] { ts, (float)10.30000, (int)219, (float)0.31000 }); - stmt.AddBatch(); - stmt.Exec(); - var affected = stmt.Affected(); - Console.WriteLine($"affected rows: {affected}"); - } - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } - } - } - } -} -``` - -WebSocket Example - -```csharp -using System; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace WSStmt -{ - internal abstract class WSStmt - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("create database power"); - client.Exec( - "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); - using (var stmt = client.StmtInit()) - { - stmt.Prepare( - "Insert into power.d1001 using power.meters tags(2,'California.SanFrancisco') values(?,?,?,?)"); - var ts = new DateTime(2023, 10, 03, 14, 38, 05, 000); - stmt.BindRow(new object[] { ts, (float)10.30000, (int)219, (float)0.31000 }); - stmt.AddBatch(); - stmt.Exec(); - var affected = stmt.Affected(); - Console.WriteLine($"affected rows: {affected}"); - } - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } - } - } - } -} -``` - -Note: When using BindRow, you need to pay attention to the one-to-one correspondence between the original C# column type and the TDengine column type. For the specific correspondence, please refer to [TDengine DataType and C# DataType](#tdengine-datatype-vs-c-datatype). - -### Schemaless Writing - -Native Example - -```csharp -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace NativeSchemaless -{ - internal class Program - { - public static void Main(string[] args) - { - var builder = - new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - client.Exec("create database sml"); - client.Exec("use sml"); - var influxDBData = - "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000"; - client.SchemalessInsert(new string[] { influxDBData }, - TDengineSchemalessProtocol.TSDB_SML_LINE_PROTOCOL, - TDengineSchemalessPrecision.TSDB_SML_TIMESTAMP_NANO_SECONDS, 0, ReqId.GetReqId()); - var telnetData = "stb0_0 1626006833 4 host=host0 interface=eth0"; - client.SchemalessInsert(new string[] { telnetData }, - TDengineSchemalessProtocol.TSDB_SML_TELNET_PROTOCOL, - TDengineSchemalessPrecision.TSDB_SML_TIMESTAMP_MILLI_SECONDS, 0, ReqId.GetReqId()); - var jsonData = - "{\"metric\": \"meter_current\",\"timestamp\": 1626846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}"; - client.SchemalessInsert(new string[] { jsonData }, TDengineSchemalessProtocol.TSDB_SML_JSON_PROTOCOL, - TDengineSchemalessPrecision.TSDB_SML_TIMESTAMP_MILLI_SECONDS, 0, ReqId.GetReqId()); - } - } - } -} -``` - -WebSocket Example - -```csharp -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace WSSchemaless -{ - internal class Program - { - public static void Main(string[] args) - { - var builder = - new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - client.Exec("create database sml"); - client.Exec("use sml"); - var influxDBData = - "st,t1=3i64,t2=4f64,t3=\"t3\" c1=3i64,c3=L\"passit\",c2=false,c4=4f64 1626006833639000000"; - client.SchemalessInsert(new string[] { influxDBData }, - TDengineSchemalessProtocol.TSDB_SML_LINE_PROTOCOL, - TDengineSchemalessPrecision.TSDB_SML_TIMESTAMP_NANO_SECONDS, 0, ReqId.GetReqId()); - var telnetData = "stb0_0 1626006833 4 host=host0 interface=eth0"; - client.SchemalessInsert(new string[] { telnetData }, - TDengineSchemalessProtocol.TSDB_SML_TELNET_PROTOCOL, - TDengineSchemalessPrecision.TSDB_SML_TIMESTAMP_MILLI_SECONDS, 0, ReqId.GetReqId()); - var jsonData = - "{\"metric\": \"meter_current\",\"timestamp\": 1626846400,\"value\": 10.3, \"tags\": {\"groupid\": 2, \"location\": \"California.SanFrancisco\", \"id\": \"d1001\"}}"; - client.SchemalessInsert(new string[] { jsonData }, TDengineSchemalessProtocol.TSDB_SML_JSON_PROTOCOL, - TDengineSchemalessPrecision.TSDB_SML_TIMESTAMP_MILLI_SECONDS, 0, ReqId.GetReqId()); - } - } - } -} -``` - -### Schemaless with reqId - -```csharp -public void SchemalessInsert(string[] lines, TDengineSchemalessProtocol protocol, - TDengineSchemalessPrecision precision, - int ttl, long reqId) -``` - -### Data Subscription - -#### Create a Topic - -Native Example - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace NativeSubscription -{ - internal class Program - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("create database power"); - client.Exec("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); - client.Exec("CREATE TOPIC topic_meters as SELECT * from power.meters"); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -WebSocket Example - -```csharp -using System; -using System.Text; -using TDengine.Driver; -using TDengine.Driver.Client; - -namespace WSSubscription -{ - internal class Program - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("create database power"); - client.Exec("CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); - client.Exec("CREATE TOPIC topic_meters as SELECT * from power.meters"); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - } -} -``` - -#### Create a Consumer - -Native Example - -```csharp -var cfg = new Dictionary() -{ - { "group.id", "group1" }, - { "auto.offset.reset", "latest" }, - { "td.connect.ip", "127.0.0.1" }, - { "td.connect.user", "root" }, - { "td.connect.pass", "taosdata" }, - { "td.connect.port", "6030" }, - { "client.id", "tmq_example" }, - { "enable.auto.commit", "true" }, - { "msg.with.table.name", "false" }, -}; -var consumer = new ConsumerBuilder>(cfg).Build(); -``` - -WebSocket Example - -```csharp -var cfg = new Dictionary() -{ - { "td.connect.type", "WebSocket" }, - { "group.id", "group1" }, - { "auto.offset.reset", "latest" }, - { "td.connect.ip", "localhost" }, - { "td.connect.port","6041"}, - { "useSSL", "false" }, - { "td.connect.user", "root" }, - { "td.connect.pass", "taosdata" }, - { "client.id", "tmq_example" }, - { "enable.auto.commit", "true" }, - { "msg.with.table.name", "false" }, -}; -var consumer = new ConsumerBuilder>(cfg).Build(); -``` - -The configuration parameters supported by consumer are as follows: -* td.connect.type: connection type, optional value is Native or WebSocket, default is Native -* td.connect.ip: The address of the TDengine running instance or taosadapter -* td.connect.port: The port of the running instance of TDengine or taosadapter. - * When using WebSocket without SSL, the default is 6041. - * When using WebSocket with SSL, the default is 443. -* useSSL: Whether to use SSL, the default is false, only valid when the protocol is WebSocket -* token: The token used to connect to TDengine Cloud, only valid when the protocol is WebSocket -* td.connect.user: username to connect to TDengine -* td.connect.pass: Password for connecting to TDengine -* group.id: consumer group ID -* client.id: consumer ID -* enable.auto.commit: Whether to automatically commit offset, the default is true -* auto.commit.interval.ms: The interval for automatically submitting offsets, the default is 5000 milliseconds -* auto.offset.reset: When offset does not exist, where to start consumption, the optional value is earliest or latest, the default is latest -* msg.with.table.name: Whether the message contains the table name -* ws.message.enableCompression: Whether to enable WebSocket compression (effective for dotnet version 6 and above, connector version 3.1.1 and above). The default is false. -* ws.autoReconnect: Whether to enable WebSocket reconnect (connector version 3.1.3 and above). The default is false. -* ws.reconnect.retry.count: The number of reconnection retries (connector version 3.1.3 and above). The default is 3. -* ws.reconnect.interval.ms: The interval between reconnection retries (connector version 3.1.3 and above). The default - is 2000. - -Supports subscribing to the result set `Dictionary` where the key is the column name and the value is the column value. - -If you use object to receive column values, you need to pay attention to: -* There needs to be a one-to-one correspondence between the original C# column type and the TDengine column type. For specific correspondence, please refer to [TDengine DataType and C# DataType] (#tdengine-datatype-and-c-datatype). -* The column name is consistent with the class attribute name and can be get and set. -* Explicitly set the value parser `ConsumerBuilder.SetValueDeserializer(new ReferenceDeserializer());` - -An example is as follows - -Result class - -```csharp - class Result - { - public DateTime ts { get; set; } - public float current { get; set; } - public int voltage { get; set; } - public float phase { get; set; } - } -``` - -Set up parser - -```csharp -var tmqBuilder = new ConsumerBuilder(cfg); -tmqBuilder.SetValueDeserializer(new ReferenceDeserializer()); -var consumer = tmqBuilder.Build(); -``` - -You can also implement a custom deserializer, implement the `IDeserializer` interface and pass it in through the `ConsumerBuilder.SetValueDeserializer` method. +## 3. Documentation -```csharp - public interface IDeserializer - { - T Deserialize(ITMQRows data, bool isNull, SerializationContext context); - } -``` - -#### Subscribe to consume data - -```csharp -consumer.Subscribe(new List() { "topic_meters" }); -while (true) -{ - using (var cr = consumer.Consume(500)) - { - if (cr == null) continue; - foreach (var message in cr.Message) - { - Console.WriteLine( - $"message {{{((DateTime)message.Value["ts"]).ToString("yyyy-MM-dd HH:mm:ss.fff")}, " + - $"{message.Value["current"]}, {message.Value["voltage"]}, {message.Value["phase"]}}}"); - } - } -} -``` - -#### Assignment subscription Offset - -```csharp -consumer.Assignment.ForEach(a => -{ - Console.WriteLine($"{a}, seek to 0"); - consumer.Seek(new TopicPartitionOffset(a.Topic, a.Partition, 0)); - Thread.Sleep(TimeSpan.FromSeconds(1)); -}); -``` - -#### Commit offset +- For development examples, see [Developer Guide](https://docs.tdengine.com/developer-guide/), which includes examples + of data writing, querying, schemaless writing, parameter binding, and data subscription. +- For other reference information, + see [Reference Manual](https://docs.tdengine.com/tdengine-reference/client-libraries/csharp/), which includes version + history, data types, example programs, API descriptions, and FAQs. -```csharp -public void Commit(ConsumeResult consumerResult) -public List Commit() -public void Commit(IEnumerable offsets) -``` +## 4. Prerequisites -#### Close subscriptions +* Install [.NET SDK](https://dotnet.microsoft.com/download) +* [Nuget Client](https://docs.microsoft.com/en-us/nuget/install-nuget-client-tools) (optional installation) +* Install the TDengine client driver. For specific steps, please refer + to [Installing the client driver](https://docs.tdengine.com/develop/connect/#install-client-driver-taosc) -```csharp -consumer.Unsubscribe(); -consumer.Close(); -``` +## 5. Build -#### Full Sample Code +1. `dotnet restore` Restore the project's dependencies. +2. `dotnet build --no-restore` Build the project. -Native Example +## 6. Testing -```csharp -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using TDengine.Driver; -using TDengine.Driver.Client; -using TDengine.TMQ; +### 6.1 Test Execution -namespace NativeSubscription -{ - internal class Program - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("CREATE DATABASE power"); - client.Exec("USE power"); - client.Exec( - "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); - client.Exec("CREATE TOPIC topic_meters as SELECT * from power.meters"); - var cfg = new Dictionary() - { - { "group.id", "group1" }, - { "auto.offset.reset", "latest" }, - { "td.connect.ip", "127.0.0.1" }, - { "td.connect.user", "root" }, - { "td.connect.pass", "taosdata" }, - { "td.connect.port", "6030" }, - { "client.id", "tmq_example" }, - { "enable.auto.commit", "true" }, - { "msg.with.table.name", "false" }, - }; - var consumer = new ConsumerBuilder>(cfg).Build(); - consumer.Subscribe(new List() { "topic_meters" }); - Task.Run(InsertData); - while (true) - { - using (var cr = consumer.Consume(500)) - { - if (cr == null) continue; - foreach (var message in cr.Message) - { - Console.WriteLine( - $"message {{{((DateTime)message.Value["ts"]).ToString("yyyy-MM-dd HH:mm:ss.fff")}, " + - $"{message.Value["current"]}, {message.Value["voltage"]}, {message.Value["phase"]}}}"); - } - } - } - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - - static void InsertData() - { - var builder = new ConnectionStringBuilder("host=localhost;port=6030;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - while (true) - { - client.Exec("INSERT into power.d1001 using power.meters tags(2,'California.SanFrancisco') values(now,11.5,219,0.30)"); - Task.Delay(1000).Wait(); - } - } - } - } -} -``` +1. Before running tests, ensure that the TDengine server is installed and that `taosd` and `taosAdapter` are running. + The database should be empty. +2. In the project directory, run `dotnet test` to execute the tests. The tests will connect to the local TDengine + server and taosAdapter for testing. +3. If the tests pass, `Test Run Successful` will be printed. If the tests fail, the failure information + `Test Run Failed` will be printed. -WebSocket Example +### 6.2 Test Case Addition -```csharp -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using TDengine.Driver; -using TDengine.Driver.Client; -using TDengine.TMQ; +Add test cases in the `test` directory. Add ADO.NET test cases to `test/Data.Tests` and client driver test cases to +`test/Driver.Test/Client`. +The test cases use the xunit framework. -namespace WSSubscription -{ - internal class Program - { - public static void Main(string[] args) - { - var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - try - { - client.Exec("CREATE DATABASE power"); - client.Exec("USE power"); - client.Exec( - "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"); - client.Exec("CREATE TOPIC topic_meters as SELECT * from power.meters"); - var cfg = new Dictionary() - { - { "td.connect.type", "WebSocket" }, - { "group.id", "group1" }, - { "auto.offset.reset", "latest" }, - { "td.connect.ip", "localhost" }, - { "td.connect.port","6041"}, - { "useSSL", "false" }, - { "td.connect.user", "root" }, - { "td.connect.pass", "taosdata" }, - { "client.id", "tmq_example" }, - { "enable.auto.commit", "true" }, - { "msg.with.table.name", "false" }, - }; - var consumer = new ConsumerBuilder>(cfg).Build(); - consumer.Subscribe(new List() { "topic_meters" }); - Task.Run(InsertData); - while (true) - { - using (var cr = consumer.Consume(500)) - { - if (cr == null) continue; - foreach (var message in cr.Message) - { - Console.WriteLine( - $"message {{{((DateTime)message.Value["ts"]).ToString("yyyy-MM-dd HH:mm:ss.fff")}, " + - $"{message.Value["current"]}, {message.Value["voltage"]}, {message.Value["phase"]}}}"); - } - } - } - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - throw; - } - } - } - - static void InsertData() - { - var builder = new ConnectionStringBuilder("protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"); - using (var client = DbDriver.Open(builder)) - { - while (true) - { - client.Exec("INSERT into power.d1001 using power.meters tags(2,'California.SanFrancisco') values(now,11.5,219,0.30)"); - Task.Delay(1000).Wait(); - } - } - } - } -} -``` +### 6.3 Performance Testing -### ADO.NET +Performance testing is in progress. -The C# connector supports the ADO.NET interface, and you can connect to the TDengine running instance through the ADO.NET interface to perform operations such as data writing and querying. +## 7. Submitting Issues -Native Example +We welcome the submission +of [GitHub Issue](https://github.com/taosdata/taos-connector-dotnet/issues/new?template=Blank+issue). When +submitting, please provide the following information: -```csharp -using System; -using TDengine.Data.Client; +- Description of the issue and whether it is consistently reproducible +- Driver version +- Connection parameters (excluding server address, username, and password) +- TDengine version -namespace NativeADO -{ - internal class Program - { - public static void Main(string[] args) - { - const string connectionString = "host=localhost;port=6030;username=root;password=taosdata"; - using (var connection = new TDengineConnection(connectionString)) - { - try - { - connection.Open(); - using (var command = new TDengineCommand(connection)) - { - command.CommandText = "create database power"; - command.ExecuteNonQuery(); - connection.ChangeDatabase("power"); - command.CommandText = - "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"; - command.ExecuteNonQuery(); - command.CommandText = "INSERT INTO " + - "power.d1001 USING power.meters TAGS(2,'California.SanFrancisco') " + - "VALUES " + - "(?,?,?,?)"; - var parameters = command.Parameters; - parameters.Add(new TDengineParameter("@0", new DateTime(2023,10,03,14,38,05,000))); - parameters.Add(new TDengineParameter("@1", (float)10.30000)); - parameters.Add(new TDengineParameter("@2", (int)219)); - parameters.Add(new TDengineParameter("@3", (float)0.31000)); - command.ExecuteNonQuery(); - command.Parameters.Clear(); - command.CommandText = "SELECT * FROM meters"; - using (var reader = command.ExecuteReader()) - { - while (reader.Read()) - { - Console.WriteLine( - $"{((DateTime) reader.GetValue(0)):yyyy-MM-dd HH:mm:ss.fff}, {reader.GetValue(1)}, {reader.GetValue(2)}, {reader.GetValue(3)}, {reader.GetValue(4)}, {System.Text.Encoding.UTF8.GetString((byte[]) reader.GetValue(5))}"); - } - } - } - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } - } - } - } -} -``` +## 8. Submitting PRs -WebSocket Example +We welcome developers to contribute to this project. Please follow the steps below to submit a PR: -```csharp -using System; -using TDengine.Data.Client; +1. Fork this project. Please refer + to [how to fork a repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo). +2. Create a new branch from the main branch with a meaningful branch name (`git checkout -b my_branch`). +3. Modify the code, ensure all unit tests pass, and add new unit tests to verify the changes. +4. Push the changes to the remote branch (`git push origin my_branch`). +5. Create a Pull Request on GitHub. Please refer + to [how to create a pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). +6. After submitting the PR, if the CI passes, you can find your PR on + the [codecov](https://app.codecov.io/gh/taosdata/taos-connector-dotnet/pulls) page to check the coverage. -namespace WSADO -{ - internal class Program - { - public static void Main(string[] args) - { - const string connectionString = "protocol=WebSocket;host=localhost;port=6041;useSSL=false;username=root;password=taosdata"; - using (var connection = new TDengineConnection(connectionString)) - { - try - { - connection.Open(); - using (var command = new TDengineCommand(connection)) - { - command.CommandText = "create database power"; - command.ExecuteNonQuery(); - connection.ChangeDatabase("power"); - command.CommandText = - "CREATE STABLE power.meters (ts TIMESTAMP, current FLOAT, voltage INT, phase FLOAT) TAGS (groupId INT, location BINARY(24))"; - command.ExecuteNonQuery(); - command.CommandText = "INSERT INTO " + - "power.d1001 USING power.meters TAGS(2,'California.SanFrancisco') " + - "VALUES " + - "(?,?,?,?)"; - var parameters = command.Parameters; - parameters.Add(new TDengineParameter("@0", new DateTime(2023,10,03,14,38,05,000))); - parameters.Add(new TDengineParameter("@1", (float)10.30000)); - parameters.Add(new TDengineParameter("@2", (int)219)); - parameters.Add(new TDengineParameter("@3", (float)0.31000)); - command.ExecuteNonQuery(); - command.Parameters.Clear(); - command.CommandText = "SELECT * FROM meters"; - using (var reader = command.ExecuteReader()) - { - while (reader.Read()) - { - Console.WriteLine( - $"{((DateTime) reader.GetValue(0)):yyyy-MM-dd HH:mm:ss.fff}, {reader.GetValue(1)}, {reader.GetValue(2)}, {reader.GetValue(3)}, {reader.GetValue(4)}, {System.Text.Encoding.UTF8.GetString((byte[]) reader.GetValue(5))}"); - } - } - } - } - catch (Exception e) - { - Console.WriteLine(e); - throw; - } - } - } - } -} -``` +## 9. References -* The connection parameters are consistent with those in [Establishing a connection](#establishing-a-connection). -* The name of TDengineParameter needs to start with @, such as @0, @1, @2, etc. The value needs to have a one-to-one correspondence between the C# column type and the TDengine column type. For the specific correspondence, please refer to [TDengine DataType and C# DataType](#tdengine-datatype-vs-c-datatype). +- [TDengine Official Website](https://tdengine.com/) +- [TDengine GitHub](https://github.com/taosdata/TDengine) -### More sample programs +## 10. License -[sample program](https://github.com/taosdata/taos-connector-dotnet/tree/3.0/examples) \ No newline at end of file +[MIT License](./LICENSE) \ No newline at end of file