{"id":20507295,"url":"https://github.com/kawansoft/aceql.client2","last_synced_at":"2025-04-13T21:52:32.340Z","repository":{"id":53011021,"uuid":"312410364","full_name":"kawansoft/AceQL.Client2","owner":"kawansoft","description":"C# Client toolkit for easy access of remote SQL databases managed with AceQL HTTP.","archived":false,"fork":false,"pushed_at":"2024-08-12T18:08:00.000Z","size":1112,"stargazers_count":3,"open_issues_count":5,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-12T12:07:12.603Z","etag":null,"topics":["http-client","mariadb","mariadb-database","mysql","mysql-database","oracle","oracle-database","postgresql","postgresql-database","remote-database","sql","sqlserver"],"latest_commit_sha":null,"homepage":"https://www.aceql.com","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kawansoft.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["kawansoft"]}},"created_at":"2020-11-12T22:24:36.000Z","updated_at":"2024-08-12T18:03:14.000Z","dependencies_parsed_at":"2023-01-27T22:15:28.764Z","dependency_job_id":null,"html_url":"https://github.com/kawansoft/AceQL.Client2","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kawansoft%2FAceQL.Client2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kawansoft%2FAceQL.Client2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kawansoft%2FAceQL.Client2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kawansoft%2FAceQL.Client2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kawansoft","download_url":"https://codeload.github.com/kawansoft/AceQL.Client2/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248788867,"owners_count":21161726,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["http-client","mariadb","mariadb-database","mysql","mysql-database","oracle","oracle-database","postgresql","postgresql-database","remote-database","sql","sqlserver"],"created_at":"2024-11-15T20:13:11.860Z","updated_at":"2025-04-13T21:52:32.311Z","avatar_url":"https://github.com/kawansoft.png","language":"C#","readme":"![GitHub top language](https://img.shields.io/github/languages/top/kawansoft/AceQL.Client2) ![GitHub issues](https://img.shields.io/github/issues/kawansoft/AceQL.Client2) ![GitHub](https://img.shields.io/github/license/kawansoft/AceQL.Client2) \n![Nuget](https://img.shields.io/nuget/dt/AceQL.Client)![GitHub last commit (branch)](https://img.shields.io/github/last-commit/kawansoft/AceQL.Client2/master)\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/cf679ce3b884458d9a0212345d51baab)](https://www.codacy.com/gh/kawansoft/AceQL.Client2/dashboard?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=kawansoft/AceQL.Client2\u0026amp;utm_campaign=Badge_Grade)\n![GitHub contributors](https://img.shields.io/github/contributors/kawansoft/AceQL.Client)\n\n# AceQL HTTP \n\n## C# Client SDK v7.8 - User Guide\n\n## August 12, 2024\n\n\u003cimg src=\"https://docs.aceql.com/favicon.png\" alt=\"AceQ HTTP Icon\"/\u003e\n\n\u003cimg src=\"https://docs.aceql.com/img/AceQL-Schema-min.jpg\" alt=\"AceQL Draw\"/\u003e\n\n* [Fundamentals](#fundamentals)\n   * [AceQL C# Client SDK Online Documentation](#aceql-c-client-sdk-online-documentation)\n   * [Contributors](#contributors)\n   * [Technical operating environment](#technical-operating-environment)\n   * [License](#license)\n   * [AceQL Server side compatibility](#aceql-server-side-compatibility)\n   * [AceQL C# Client SDK installation](#aceql-c-client-sdk-installation)\n   * [Data transport](#data-transport)\n      * [Transport format](#transport-format)\n      * [Content streaming and memory management](#content-streaming-and-memory-management)\n   * [Best practices for fast response time](#best-practices-for-fast-response-time)\n* [Implementation Info](#implementation-info)\n   * [The AceQL SDK classes and methods](#the-aceql-sdk-classes-and-methods)\n      * [Asynchronous implementation](#asynchronous-implementation)\n   * [Data types](#data-types)\n* [Using the AceQL C# Client SDK](#using-the-aceql-c-client-sdk)\n   * [The connection string](#the-connection-string)\n      * [Using NTLM](#using-ntlm)\n      * [Using a Web Proxy](#using-a-web-proxy)\n         * [Using CredentialCache values for an authenticated proxy](#using-credentialcache-values-for-an-authenticated-proxy)\n   * [Handling Exceptions](#handling-exceptions)\n      * [The error type](#the-error-type)\n      * [Most common AceQL server messages](#most-common-aceql-server-messages)\n      * [HTTP Status Codes](#http-status-codes)\n   * [AceQLConnection: Connection Creation \u0026amp; Close](#aceqlconnection-connection-creation--close)\n   * [AceQLCommand: executing SQL statements](#aceqlcommand-executing-sql-statements)\n      * [Inserting NULL values](#inserting-null-values)\n   * [AceQLDataReader: getting queries result](#aceqldatareader-getting-queries-result)\n      * [Reading NULL values](#reading-null-values)\n   * [AceQLTransaction](#aceqltransaction)\n      * [Precisions on transactions](#precisions-on-transactions)\n   * [Using Stored Procedures](#using-stored-procedures)\n      * [Using Oracle Database stored procedures with SELECT calls](#using-oracle-database-stored-procedures-with-select-calls)\n   * [Batch management](#batch-management)\n   * [BLOB management](#blob-management)\n      * [BLOB creation](#blob-creation)\n      * [BLOB reading](#blob-reading)\n      * [Managing BLOB upload progress](#managing-blob-upload-progress)\n   * [Advanced Features](#advanced-features)\n      * [Calling AceQL Java stored procedures](#calling-aceql-java-stored-procedures)\n      * [Using outer authentication without a password  and with an AceQL Session ID](#using-outer-authentication-without-a-password--and-with-an-aceql-session-id)\n      * [Enable default system authentication](#enable-default-system-authentication)\n   * [Using the Metadata Query API](#using-the-metadata-query-api)\n      * [Downloading database schema into a file](#downloading-database-schema-into-a-file)\n      * [Accessing remote database main properties](#accessing-remote-database-main-properties)\n      * [Getting Details of Tables and Columns](#getting-details-of-tables-and-columns)\n* [Implementing Retry Logic in HTTP Requests (Experimental)](#implementing-retry-logic-in-http-requests-experimental)\n\n# Fundamentals \n\nThis document describes how to use the AceQL C# Client SDK and gives some details about how it operates with the AceQL Server side.\n\nThe SDK allows you to wrap the [AceQL HTTP APIs](https://github.com/kawansoft/aceql-http/blob/master/aceql-http-user-guide-api.md) and eliminate the tedious work of handling communication errors and parsing JSON results.\n\nC# application developers can access remote SQL databases and/or SQL databases in the cloud by simply including standard C# SQL calls in their code, just like they would do for an SQL Server database. There is zero learning curve and usage is straight forward.\n\nThe AceQL Server operation is described in [AceQL HTTP Server Installation and Configuration Guide](https://github.com/kawansoft/aceql-http/blob/master/README.md), whose content is sometimes referred to in this User Guide. \n\nOn the remote side, like the AceQL Server access to the SQL database using Java JDBC, we will sometimes use the JDBC terminology (ResultSet, etc.) in this document. Nevertheless, knowledge of Java or JDBC is *not* a requirement.\n\n## AceQL C# Client SDK Online Documentation\n\nThe Online Documentation is accessible [here](https://docs.aceql.com/rest/soft_csharp/7.8/csharpdoc_sdk/html/N-AceQL.Client.Api.htm).\n\n## Contributors\n\nThanks to Gilles Echégut for the great input on the development and the documentation of [Advanced Features](#advanced-features).\n\n## Technical operating environment\n\nThe AceQL C# Client SDK is entirely written in C# and is packaged as a [.Net Standard 2.0](https://docs.microsoft.com/en-us/dotnet/standard/net-standard) Library, which works on Windows Desktop and on Android, iOS \u0026 macOS with Xamarin.\n\nThe targets of the library are:\n\n- .NET Core and .NET 5 2.0\n- NetFramework 4.6.1\n- Xamarin.Android 8.0\n- Xamarin.iOS/ Xamarin.iOS Classic 10.14\n- Xamarin.Mac 3.8\n- Windows Universal Platform 10.0.16299\n\n## License\n\nThe SDK is licensed with the liberal [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0) license.\n\n## AceQL Server side compatibility\n\nThis version requires AceQL HTTP version 12.2+ on the server side.\n\n## AceQL C# Client SDK installation\n\nInstall the [NuGet Package](https://www.nuget.org/packages/AceQL.Client).\n\n## Data transport\n\n### Transport format\n\nAceQL transfers the least possible amount of meta-information:\n\n- Request parameters are transported in UTF-8 format\n- JSON format is used for data and class transport (using [Newtonsoft.Json](https://www.newtonsoft.com/json) NuGet package).\n\n### Content streaming and memory management\n\nAll requests are streamed:\n\n- Output requests (from the client side)   are streamed directly from the socket to the server to avoid buffering any content body\n- Input responses (for the client side)  are streamed directly from the socket to the server to efficiently read  the response body.\n\nLarge content (query results, BLOBs/CLOBs, etc.) is transferred using files. It is never loaded in memory. Streaming techniques are always used to read and write this content.\n\n## Best practices for fast response time\n\nEvery HTTP exchange between the client and server side is time-consuming, because the client side  waits for the server's response.\n\nTry to avoid coding SQL calls inside loops, as this can reduce execution speed. Each SQL call will send an http request and wait for the response from the server. \n\nNote that AceQL is optimized as much as possible:\n\n- A SELECT call returning a huge data volume will not consume memory on the server or client side:  AceQL uses input stream and output stream I/O for data  transfer.\n- Server JDBC `ResultSet` retrieval is as fast as possible:\n  - The `ResultSet` creation is done once on the server by the `executeQuery` order.\n  - The rows are all dumped at once on the servlet output stream by the server\n  - The client side gets the `ResultSet` content as a file.\n  - All data reading commands are executed locally on the client side with forward-only reading\n- **It is highly recommended to always use  batch commands  when you have many rows to INSERT or UPDATE.**\n\n\n\n---\n\n# Implementation Info\n\n## The AceQL SDK classes and methods\n\nUsing the SDK classes is as intuitive and straight forward as possible.\n\nMost SDK class names are the equivalent of Microsoft SQL Server `System.Data.SqlClient` namespace: they share the same suffix name for the classes, and the same method names.\n\nHere is the correspondence table for the common classes:\n\n| AceQL Client  \u003cbr /\u003eAceQL.Client.Api namespace | SQL Server Client  \u003cbr /\u003eSystem.Data.SqlClient namespace |\n| ---------------------------------------------- | :------------------------------------------------------- |\n| `AceQLCommand`                                 | `SqlCommand`                                             |\n| `AceQLConnection`                              | `SqlConnection`                                          |\n| `AceQLCredential`                              | `SqlCredential`                                          |\n| `AceQLDataReader`                              | `SqlDataReader`                                          |\n| `AceQLParameter`                               | `SqlParameter`                                           |\n| `AceQLParameterCollection`                     | `SqlParameterCollection`                                 |\n| `AceQLTransaction`                             | `SqlTransaction`                                         |\n| `CommandType`                                  | `CommandType`                                            |\n| `IsolationLevel`                               | `IsolationLevel`                                         |\n| `ParameterDirection`                           | `ParameterDirection`                                     |\n\nThe AceQL SDK exposes 2 specific public classes and 1 enumeration:\n\n| Name                                | Role                                                         |\n| ----------------------------------- | ------------------------------------------------------------ |\n| `AceQLException`                    | Generic Exception implementation  for error reporting.  \u003cbr /\u003eSee [Handling Exceptions](#handling-exceptions). |\n| `AceQLNullType`\u003cbr\u003e`AceQLNullValue` | Enum and class that allows you to define the type of NULL  values for database updates.  See [Inserting NULL values](#inserting-null-values). |\n| `AceQLProgressIndicator`            | Allows you to  retrieve Blob upload progress as a percentage. See [Managing BLOB upload progress](#managing-blob-upload-progress). |\n\n### Asynchronous implementation \n\nAll SQL calls methods are asynchronous only.\n\nThe reason for this is that the AceQL SDK uses the Microsoft `System.Net.Http.HttpClient` class for all HTTP operations, which is entirely asynchronous.\n\nAll calls are cancelable before the defined `HttpClient` timeout, as all Async methods offer an overloaded version with a `System.Threading.CancellationToken` parameter.\n\nExample in `AceQLCommand`:\n\n```c#\n// Versions of ExecuteNonQueryAsync\npublic async Task\u003cint\u003e ExecuteNonQueryAsync();\npublic async Task\u003cint\u003e ExecuteNonQueryAsync(CancellationToken cancellationToken);\n```\n\nNote that cancellation action impacts only the methods calls of the `System.Net.Http.HttpClient` class. Cancellation action has no impact on remote SQL database state.  \n\n## Data types\n\nThe main server side JDBC data types for columns are supported: \n\nBoolean, Blob/Clob, Integer, Short, Double, Float, BigDecimal, Long, String, Date, Time, Timestamp, URL and Array.\n\nNote that the AceQL SDK does not allow you to specify data types to use; data types are implicitly chosen with the `AceQLParameter` values.\n\n## *[Advertisement] 📢*\n\n## *Transform Your Development with ChatMotor API! 🚀*\n\n[ChatMotor](https://www.chatmotor.ai/) API is designed to make your development life easier by handling the complexities of OpenAI and ChatGPT:\n\n- 🌟 **Seamless Integration**: No need to learn the intricacies of OpenAI APIs and their limitations. ChatMotor handles chunking, HTTP errors, and retry management.\n- 📄 **Excel Handling**: Allow your end user to easily process and manipulate large Excel files using simple prompts. Much easier for them than endless VBA or painful Python coding.\n- 📝 **Unlimited Input Sizes**: Seamlessly handle prompts that exceed 4096 tokens with automatic sequencing and parallel task threading for speed, allowing you to treat large inputs without dwelling on the details.\n- 🎙️ **Hassle-Free Transcriptions**: Simply provide the audio files for transcription, regardless of format and size. ChatMotor handles everything, including gigantic files, and delivers a clean, formatted text file.\n- 🌐 **Advanced Translation**: Handle documents of any size with ease. Just pass them to the API, and it will manage everything, delivering accurate and fast results.\n\n**✨ Faster and Easier Delivery for Your End User: Save Time, Reduce Complexity, and Focus on What Matters!**\n\n👉 Explore [**ChatMotor**](https://www.chatmotor.ai) and revolutionize your development workflow!\n\n# Using the AceQL C# Client SDK\n\n## The connection string\n\nThe standard connection string is:\n\n```c#\n\"Server=https://www.acme.com:9443/aceql; Database=myDataBase; Username=myUsername; Password=myPassword\"\n```\n\nWhere:\n\n- The Server value is the AceQL Server servlet path and includes the port if necessary\n- The Username and Password are used for authentication by the remote AceQL Server\n- The Database value is the name of the remote database to use for the session. See [AceQL HTTP Server Installation and Configuration Guide](https://github.com/kawansoft/aceql-http/blob/master/README.md) for more information.\n\n### Using NTLM\n\nYou can specify using NTLM with `NTLM=true` \n\n```c#\n\"Server=https://www.acme.com:9443/aceql; Database=myDataBase; Username=myUsername; Password=myPassword; NTLM=true\"\n```\n\n### Using a Web Proxy\n\n`System.Net.WebRequest.DefaultWebProxy` value is used by default.\n\nYou can specify the credentials of a proxy that requires authentication with `ProxyUsernme` and `ProxyPassword`:\n\n```c#\n\"Server=https://www.acme.com:9443/aceql; Database=myDataBase; Username=myUsername; Password=myPassword; ProxyUsername=MyProxyUser; ProxyPassword=MyProxyPassword\"\n```\n\nIf `ProxyUri` is specified, the value will be used instead of the default `System.Net.WebRequest.DefaultWebProxy` value:\n\n```c#\n\"Server=https://www.acme.com:9443/aceql; Database = myDataBase; Username =myUsername; Password=myPassword; ProxyUri=http://localhost:8080 ProxyUsername=proxyuser1;ProxyPassword=proxyuser1\"\n```\n\nRead/Write http timeout may be specified with `Timeout` in milliseconds:\n\n```c#\n\"Server=https://www.acme.com:9443/aceql; Database=myDataBase; Username=myUsername; Password=myPassword; Timeout=300000\"\n```\n\nIf Timeout is not specified or equals 0, Microsoft  [HttpClient.Timeout](https://msdn.microsoft.com/en-us/library/system.net.http.httpclient.timeout) default value will be used.\n\nThe semicolon\";\" character is supported in password, but must be escaped: \n\n```\nPassword = my\\;Password; \n```\n\n#### Using CredentialCache values for an authenticated proxy\n\nJust add `UseCredentialCache=True` in the connection string, and AceQL will automatically retrieve the `Credential` to use for the proxy configuration:\n\n```C#\n\"Server=https://www.acme.com:9443/aceql; Database=myDataBase; Username=myUsername; Password=myPassword; UseCredentialCache=True\"\n```\n\n`\n\n## Handling Exceptions\n\nExcept for `ArgumentNullException`, Exceptions thrown are always an instance of `AceQLException`.\n\nThe `AceQLException` contains 5 pieces of information: \n\n| Info             | Description                              |\n| ---------------- | ---------------------------------------- |\n| Reason           | The error message. Retrieved with `Reason` property. |\n| Error Type       | See below for description. Retrieved with `ErrorType` property. |\n| Exception        | The original Exception that is the cause, if any.\u003cbr /\u003eRetrieved with  `ExceptionCause` property. |\n| Http Status Code | See below for description. Retrieved with `HttpStatusCode` property. |\n| Server Exception | The Java Exception Stack Trace thrown on server side,  if any. \u003cbr /\u003eRetrieved with `RemoteStackTrace` property. |\n\n### The error type\n\nThe error type allows you to get the type of error, and where the error occurred. It is retrieved with the `AceQLException.ErrorType` property.\n\n| Error Type  Value | Description                              |\n| ----------------- | :--------------------------------------- |\n| 0                 | The error occurred locally on the client side.  See `HttpStatusCode` property for more info.  Typical cases: no Internet connection, proxy  authentication required. |\n| 1                 | The error is due to a JDBC Exception.  It was raised by the remote JDBC Driver and is rerouted  by AceQL as is.  The JDBC error message is accessible via `Reason` property. Typical case: an error in the SQL statement.  Examples: wrong table or column name. |\n| 2                 | The error was raised by the AceQL Server.  This means that the AceQL Server expected a value or  parameter that was not sent by the client side.  Typical cases: misspelling in URL parameter, missing  required request parameters,  JDBC  Connection expiration, etc. The detailed error message is accessible via `Reason` property.  See below for most common AceQL Server error  messages. |\n| 3                 | The AceQL Server forbade the execution of the SQL  statement for a security reason.  For security reasons, `Reason` property gives access to voluntarily vague  details. |\n| 4                 | The AceQL Server is on failure and raised an  unexpected Java Exception.  The stack trace is included and accessible via `RemoteStackTrace` property. |\n\n### Most common AceQL server messages\n\n| AceQL Sever  Error Messages   (AceQLException.ErrorType  = 2) |\n| ------------------------------------------------------------ |\n| AceQL main  servlet not found in path                        |\n| An error occurred  during Blob download                      |\n| An error occurred  during Blob upload                        |\n| Blob directory  defined in `DatabaseConfigurator.getBlobDirectory()` does not exist |\n| Connection is  invalidated (probably expired)                |\n| Database does not  exist                                     |\n| Invalid blob_id.  Cannot be used to create a file            |\n| Invalid blob_id. No Blob corresponding to blob_id            |\n| Invalid  session_id                                          |\n| Invalid username  or password                                |\n| No action found  in request                                  |\n| Unable to get a  Connection                                  |\n| Unknown SQL  action or not supported by software             |\n\n### HTTP Status Codes\n\nThe Http StatusCode is accessible with the `AceQLException.HttpStatusCode` property.\n\nThe HTTP StatusCode is 200 (OK) on successful completion calls.\n\nWhen an error occurs: \n\nIf `errortype` is 0, the HTTP Status Code is returned by the client side and may take all possible values in a malformed HTTP call.\n\nIf `errortype` is \u003e 0, the HTTP Status Code can take one the following values returned by the server side:\n\n| HTTP  Status Code            | Description                              |\n| ---------------------------- | ---------------------------------------- |\n| 400  (BAD REQUEST)           | Missing element in URL path  \u003cbr /\u003eMissing request parameters   \u003cbr /\u003eAll JDBC errors raised by the remote JDBC  Driver |\n| 401  (UNAUTHORIZED)          | Invalid username or password in connect.\u003cbr /\u003eInvalid session_id. \u003cbr /\u003eThe AceQL Server forbade the execution of  the SQL statement for security reasons . |\n| 404 (NOT_FOUND)              | BLOB directory does not exist on server. \u003cbr /\u003e BLOB file not found on server. |\n| 500  (INTERNAL_SERVER_ERROR) | The AceQL Server is on failure and raised  an unexpected Java Exception. |\n\n## AceQLConnection: Connection Creation \u0026 Close\n\nConnection to a remote database is done with `AceQLConnection` class:\n\n```C#\n// Port number is the port number used to start the Web Server:\nstring server = \"https://www.acme.com:9443/aceql\";\nstring database = \"kawansoft_example\";\n\nstring username = \"MyUsername\";\nstring password = \"MySecret\";\n\nstring connectionString = $\"Server={server}; Database={database}; \"\n    + $\"Username={username}; Password={password}\";\n\nAceQLConnection connection = new AceQLConnection(connectionString);\n\n// Opens the connection with the remote database\nawait connection.OpenAsync();\n```\nIt’s possible to string the credential information out of the connection and use a more secure `AceQLCredential`: \n\n```C#\n// Port number is the port number used to start the Web Server:\nstring server = \"https://www.acme.com:9443/aceql\";\nstring database = \"kawansoft_example\";\n\nstring connectionString = $\"Server={server}; Database={database}\";\n\nstring username = \"username\";\nchar[] password = GetFromUserInput();\n\nAceQLConnection connection = new AceQLConnection(connectionString)\n{\n    Credential = new AceQLCredential(username, password)\n};\n\n// Opens the connection with the remote database\nawait connection.OpenAsync();\n```\nConnection should always be closed in order to close and release the remote server JDBC connection into the pool .\n\nThe preferred way is to explicitly call `CloseAsync`:\n\n```C#\ntry\n{\n    connection = new AceQLConnection(connectionString);\n    await connection.OpenAsync();\n    // SQL stuff...\n}\nfinally\n{\n    await connection.CloseAsync();\n}\n```\nAs `AceQLConnection` is disposable, an alternate way is to dispose the instance. It will call  `CloseAsync`,but as `System.IDisposable.Dipose()` is synchronous, call will *not* be awaited:\n\n```C#\nusing (AceQLConnection connection = new AceQLConnection(connectionString))\n{\n    // SQL stuff...\n}\n```\nNote that it’s possible to combine both in your code:  `CloseAsync()` may safely be called more than once.\n\n## AceQLCommand: executing SQL statements\n\nLet’s do an INSERT in the CUSTOMER table:\n\n```C#\nstring sql = \"insert into customer values \" + \"\" +\n        \"(@customer_id, @customer_title, @fname, \" +\n        \"@lname, @addressline, @town, @zipcode, @phone)\";\n\nAceQLCommand command = new AceQLCommand(sql, connection);\ncommand.Prepare(); // Optional\n\ncommand.Parameters.AddWithValue(\"@customer_id\", 1);\ncommand.Parameters.AddWithValue(\"@customer_title\", \"Sir\");\ncommand.Parameters.AddWithValue(\"@fname\", \"Doe\");\ncommand.Parameters.AddWithValue(\"@lname\", \"John\");\n// Alternate syntax\ncommand.Parameters.Add(new AceQLParameter(\"@addressline\", \"1 Madison Ave\"));\ncommand.Parameters.AddWithValue(\"@town\", \"New York\");\ncommand.Parameters.AddWithValue(\"@zipcode\", \"NY 10010\");\ncommand.Parameters.AddWithValue(\"@phone\", \"+1 (212) 586-71XX\");\n\nint rows = await command.ExecuteNonQueryAsync();\n```\nNotes:\n\nIt is unnecessary to dispose an `AceQLCommand`. `AceQLCommand.Dispose` calls do nothing and `AceQLCommand` is Disposable for ease of existing code migration.\n\n`Prepare` call is optional: statement will always be prepared on the server side if a statement has parameters. This is the way JDBC works.\n\n### Inserting NULL values\n\nNULL values are handled in a specific way, because the remote server must know the type of the NULL value.\n\nUse the `AceQLNullValue` class together with the `AceQLNullType` `enum` to pass the NULL type to the `AceQLParameter`. An `AceQLNullValue` instance is created by passing the parameter's `AceQLNullType` value to the constructor: \n\n```c#\n// We don't know the phone number:\ncommand.Parameters.Add(new AceQLParameter(\"@phone\", new AceQLNullValue(AceQLNullType.VARCHAR)));\n```\n\n## AceQLDataReader: getting queries result\n\nLet’s do a query on a remote database:\n\n```c#\nstring sql = \"select * from customer\";\nAceQLCommand command = new AceQLCommand(sql, connection);\n\n// Our dataReader should be disposed to delete underlying downloaded files\nusing (AceQLDataReader dataReader = await command.ExecuteReaderAsync())\n{\n    // Read is synchronous because all data to read are already downloaded\n    // when AceQLDataReader instance is created. Read accesses a StreamReader.\n    while (dataReader.Read())\n    {\n        //customer_id integer     not null,\n        Console.WriteLine();\n        int i = 0;\n        Console.WriteLine(\"customer_id   : \" + dataReader.GetValue(i++));\n        Console.WriteLine(\"customer_title: \" + dataReader.GetValue(i++));\n        Console.WriteLine(\"fname         : \" + dataReader.GetValue(i++));\n        Console.WriteLine(\"lname         : \" + dataReader.GetValue(i++));\n        Console.WriteLine(\"addressline   : \" + dataReader.GetValue(i++));\n        Console.WriteLine(\"town          : \" + dataReader.GetValue(i++));\n        Console.WriteLine(\"zipcode       : \" + dataReader.GetValue(i++));\n        Console.WriteLine(\"phone         : \" + dataReader.GetValue(i++));\n    }\n}\n```\n### Reading NULL values\n\nUse `AceQLDataReader.IsDBNull` if necessary to check if a value is NULL before accessing it:\n\n```c#\n    Console.WriteLine(\"Is phone NULL? : \" + dataReader.IsDBNull(7));\n```\n\nFor string columns (CHAR, VARCHAR, etc.), note that when `AceQLDataReader.IsDBNull` returns true, there is no way to differentiate if the remote column is really NULL or if the column’s value is the “NULL” string. This is a limitation in this AceQL SDK version.\n\n## AceQLTransaction\n\nThe AceQL SDK supports SQL transactions:\n\n```c#\n// Create a transaction\nAceQLTransaction transaction = await connection.BeginTransactionAsync();\n\nstring sql = \"insert into customer values \" +\n            \"(@customer_id, @customer_title, @fname, \" +\n        \"@lname, @addressline, @town, @zipcode, @phone)\";\n\ntry\n{\n    AceQLCommand command = new AceQLCommand(sql, connection)\n    {\n        Transaction = transaction // Not required, will do nothing.\n    };\n\n    command.Parameters.AddWithValue(\"@customer_id\", customerId);\n    command.Parameters.AddWithValue(\"@customer_title\", \"Sir\");\n    command.Parameters.AddWithValue(\"@fname\", \"Doe\");\n    command.Parameters.AddWithValue(\"@lname\", \"John\");\n    command.Parameters.AddWithValue(\"@addressline\", \"1 Madison Ave\");\n    command.Parameters.AddWithValue(\"@town\", \"New York\");\n    command.Parameters.AddWithValue(\"@zipcode\", \"NY 10010\");\n    \n    AceQLNullValue aceQLNullValue = new AceQLNullValue(AceQLNullType.VARCHAR);\n    command.Parameters.Add(new AceQLParameter(\"@phone\", aceQLNullValue));\n\n    await command.ExecuteNonQueryAsync();\n\n    sql = \"insert into orderlog values \" +\n                \"(@customer_id, @item_id, @description, \" +\n                    \"@item_cost, @date_placed, @date_shipped, \" +\n                    \"@jpeg_image, @is_delivered, @quantity)\";\n\n    command = new AceQLCommand(sql, connection);\n\n    Console.WriteLine(\"insert into orderlog...\");\n\n    command.Parameters.AddWithValue(\"@customer_id\", customerId);\n    command.Parameters.AddWithValue(\"@item_id\", itemId);\n    command.Parameters.AddWithValue(\"@description\", \"Item Description\");\n    command.Parameters.AddWithValue(\"@item_cost\", 99D);\n    command.Parameters.AddWithValue(\"@date_placed\", DateTime.Now);\n    command.Parameters.AddWithValue(\"@date_shipped\", DateTime.Now);\n    \n    // No BLOB for now\n    AceQLNullValue aceQLNullValue = new AceQLNullValue(AceQLNullType.BLOB);\n    command.Parameters.Add(new AceQLParameter(\"@jpeg_image\", aceQLNullValue);\n    \n    command.Parameters.AddWithValue(\"@is_delivered\", 1);\n    command.Parameters.AddWithValue(\"@quantity\", 1);\n\n    await command.ExecuteNonQueryAsync();\n    await transaction.CommitAsync();\n}\ncatch (Exception e)\n{\n    // Transaction must always be terminated by a CommitAsync() or RollbackAsync()\n    await transaction.RollbackAsync();\n    throw e;\n}\n```\n\n### Precisions on transactions\n\nIt is unnecessary to assign an `AceQLTransaction` to an `AceQLCommand`. (Because server side JDBCs do not handle this type of behavior). Calls are accepted to ease existing code migration.\n\n`AceQLTransaction` must be terminated by either a `RollbackAsync` or `CommitAsync` call. This is necessary to reset the server connection to `aucommit true`.\n\n`AceQLTransaction.Dispose` calls do nothing and `AceQLTransaction`  is Disposable for ease of existing code migration.\n\n## Using Stored Procedures\n\nStored procedures are supported for MySQL/MariaDB, PotsgreSQL, SQL Server, and Oracle Database. The parameter's direction may be specified.  The standard syntax is the same for all database products:\n\n```C#\nstring sql = \"{call ProcedureName(@parm1, @parm2, @parm3)}\";\n\nAceQLCommand command = new AceQLCommand(sql, connection);\ncommand.CommandType = CommandType.StoredProcedure;\n\nAceQLParameter aceQLParameter2 = new AceQLParameter(\"@parm2\", 2)\n{\n    Direction = ParameterDirection.InputOutput\n};\n\n// Direction defaults to IN\nAceQLParameter aceQLParameter1 = new AceQLParameter(\"@parm1\", 0);\n\nAceQLParameter aceQLParameter3 = new AceQLParameter(\"@parm3\")\n{\n    Direction = ParameterDirection.Output\n};\n\ncommand.Parameters.Add(aceQLParameter1);\ncommand.Parameters.Add(aceQLParameter2);\ncommand.Parameters.Add(aceQLParameter3);\n\nawait command.ExecuteNonQueryAsync(); // \n\n// Or AceQLDataReader dataReader = await command.ExecuteReaderAsync();\n// for a SELECT call.\n```\n\n### Using Oracle Database stored procedures with SELECT calls\n\nOracle Database stored procedures with SELECT calls are supported starting AceQL HTTP server version 12.0. The last parameter must be a `?` in order to signify to the AceQL HTTP server that the stored procedure is a SELECT call.\n\nAssuming this Oracle Database stored procedure that executes a SELECT call:\n\n```plsql\n-- ORACLE_SELECT_CUSTOMER stored procedure\n-- Executes a SELECT on customer table with \n-- customer_id as IN parameter\ncreate or replace PROCEDURE ORACLE_SELECT_CUSTOMER \n    (p_customer_id NUMBER, p_rc OUT sys_refcursor) AS \nBEGIN\n    OPEN p_rc\n    For select customer_id from customer where customer_id \u003e p_customer_id;\nEND ORACLE_SELECT_CUSTOMER;\n```\n\nThe C# code for calling `ORACLE_SELECT_CUSTOMER`  and print back all `customer_id` \u003e 2 would be:\n\n```C#\nstring sql = \"{ call ORACLE_SELECT_CUSTOMER(@parm1, ?) }\";\n\nAceQLCommand command = new AceQLCommand(sql, connection);\ncommand.CommandType = CommandType.StoredProcedure;\n\nAceQLParameter aceQLParameter1 = new AceQLParameter(\"@parm1\", 2);\n\ncommand.Parameters.Add(aceQLParameter1);\n\nusing AceQLDataReader dataReader = await command.ExecuteReaderAsync();\nwhile (dataReader.Read())\n{\n    int i = 0;\n    AceQLConsole.WriteLine(\"Customer ID: \" + dataReader.GetValue(i));\n}\n```\n\n## Batch management\n\nBatch commands allows to process Bulk insert:\n\n- `AceQLCommand.addBatch()` allows to add a set of parameters.\n- `AceQLCommand.executeBatchAsync()` allow to send to the server all the parameters at once and execute the batch commands on the server.\n\nAs usual, It is recommended to use batch commands inside a transaction for faster inserts on the server side:\n\n```C#\nstring sql = \"insert into customer values (@parm1, @parm2, @parm3, @parm4, @parm5, @parm6, @parm7, @parm8)\";\nAceQLCommand command = new AceQLCommand(sql, connection);\n\n// We do the INSERTs in a transaction because it's faster:\nAceQLTransaction transaction = await connection.BeginTransactionAsync();\n\ntry\n{\n    // Add first set of parameters\n    command.Parameters.AddWithValue(\"@parm1\", 1);\n    command.Parameters.AddWithValue(\"@parm2\", \"Sir\");\n    command.Parameters.AddWithValue(\"@parm3\", \"John\");\n    command.Parameters.AddWithValue(\"@parm4\", \"Smith\");\n    command.Parameters.AddWithValue(\"@parm5\", \"1 U.S. Rte 66\");\n    command.Parameters.AddWithValue(\"@parm6\", \"Hydro\");\n    command.Parameters.AddWithValue(\"@parm7\", \"OK 730482\");\n    command.Parameters.AddWithValue(\"@parm8\", \"(405) 297 - 2391\");\n    command.AddBatch();\n\n    // Add a second set of parameters\n    command.Parameters.AddWithValue(\"@parm1\", 2);\n    command.Parameters.AddWithValue(\"@parm2\", \"Miss\");\n    command.Parameters.AddWithValue(\"@parm3\", \"Melanie\");\n    command.Parameters.AddWithValue(\"@parm4\", \"Jones\");\n    command.Parameters.AddWithValue(\"@parm5\", \"1000 U.S. Rte 66\");\n    command.Parameters.AddWithValue(\"@parm6\", \"Sayre\");\n    command.Parameters.AddWithValue(\"@parm7\", \"OK 73662\");\n    command.Parameters.AddWithValue(\"@parm8\", \"(405) 299 - 3359\");\n    command.AddBatch();\n\n    // Executes the batch. All INSERT orders are uploaded at once:\n    int[] results = await command.ExecuteBatchAsync();\n    await transaction.CommitAsync();\n\n    // Do whatever with the results...\n}\ncatch (Exception)\n{\n    await transaction.RollbackAsync();\n}\n```\n\nBatch commands processing is optimized in order to run as fast as possible and consume fewer possible resources:\n\n- All batch commands \u0026 parameters are send using only one upload at the start of the `Command.ExecuteBatchAsync()`  processing. \n- The upload of the batch commands \u0026 parameters  is done using streaming techniques on the client-side. The incoming reading is also done in streaming on the server side.\n\n**It is highly recommended to always use  batch commands  when you have many rows to INSERT or UPDATE.**\n\nNote that batch commands are supported with AceQL HTTP Server version 8.0 or higher on server side.\n\n## BLOB management\n\nThe AceQL SDK supports BLOB creation and reading. \n\nMethods are implemented using streaming techniques to keep low memory consumption.\n\nCLOB are not supported in this version.\n\n### BLOB creation\n\nBLOB creation is supported by entering a `System.IO.Stream` to the `AceQLParameterCollection`:\n\n```c#\n// Create a transaction because some database engines require autocommit off\nAceQLTransaction transaction = await connection.BeginTransactionAsync();\n\ntry\n{\n    string sql = \"insert into orderlog values \" +\n            \"(@customer_id, @item_id, @description, \" +\n            \"@item_cost, @date_placed, @date_shipped, \" +\n                \"@jpeg_image, @is_delivered, @quantity)\";\n\n    AceQLCommand command = new AceQLCommand(sql, connection);\n\n    string userPath = \n        Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);\n    string blobPath = userPath + \"\\\\koala.jpg\";\n    Stream stream = new FileStream(blobPath, FileMode.Open, FileAccess.Read);\n\n    Console.WriteLine(\"blobPath: \" + blobPath);\n    Console.WriteLine(\"insert into orderlog...\");\n\n    command.Parameters.AddWithValue(\"@customer_id\", customerId);\n    command.Parameters.AddWithValue(\"@item_id\", itemId);\n    command.Parameters.AddWithValue(\"@description\", \"Item Description\");\n    command.Parameters.AddWithValue(\"@item_cost\", 99D);\n    command.Parameters.AddWithValue(\"@date_placed\", DateTime.Now);\n    command.Parameters.AddWithValue(\"@date_shipped\", DateTime.Now);\n    command.Parameters.AddWithValue(\"@jpeg_image\", stream);\n    command.Parameters.AddWithValue(\"@is_delivered\", 1);\n    command.Parameters.AddWithValue(\"@quantity\", 1);\n\n    Console.WriteLine(\"command.ExecuteNonQueryAsync()...\");\n\n    await command.ExecuteNonQueryAsync();\n    await transaction.CommitAsync();\n}\ncatch (Exception e)\n{\n    // Transaction must always be terminated by a CommitAsync() or RollbackAsync()\n    await transaction.RollbackAsync();\n    throw e;\n}\n```\n### BLOB reading \n\nBLOB reading is supported through `AceQLDataReader.GetStreamAsync` call:\n\n```c#\n// Create a transaction because some database engines require autocommit off\nAceQLTransaction transaction = await connection.BeginTransactionAsync();\n\ntry\n{\n    string sql = \"select customer_id, item_id, jpeg_image from orderlog\" +\n        \" where customer_id =  @customer_id and item_id = @item_id\";\n\n    AceQLCommand command = new AceQLCommand(sql, connection);\n    command.Parameters.AddWithValue(\"@customer_id\", customerId);\n    command.Parameters.AddWithValue(\"@item_id\", itemId);\n\n    using (AceQLDataReader dataReader = await command.ExecuteReaderAsync())\n    {\n        while (dataReader.Read())\n        {\n            int i = 0;\n            Console.WriteLine(\"customer_id   : \" + dataReader.GetValue(i++));\n            Console.WriteLine(\"item_id: \" + dataReader.GetValue(i++));\n\n            string userPath =\n                Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);\n            string blobPath = userPath + \"\\\\koala_download.jpg\";\n\n            Console.WriteLine(\"Creating file from server BLOB in: \" + blobPath);\n\n            // Download Blob\n            using (Stream stream = await dataReader.GetStreamAsync(i++))\n            {\n                using (var fileStream = File.Create(blobPath))\n                {\n                    stream.CopyTo(fileStream);\n                }\n            }\n        }\n\n        await transaction.CommitAsync();\n    }\n}\ncatch (Exception e)\n{\n    // Transaction must always be terminated by a CommitAsync() or RollbackAsync()\n    await transaction.RollbackAsync();\n    throw e;\n}\n```\n### Managing BLOB upload progress\n\nYou may want to give your users a progress bar when uploading Blob(s).\n\nThe `AceQLProgressIndicator.Percent` property allows you to get the current percent of upload. Value will be incremented automatically during upload. \n\nTo activate the update mechanism:\n\n1/ Pass the long Blob length along the Stream value when setting the Blob parameter:\n\n```c#\n  command.Parameters.AddWithValue(\"@jpeg_image\", stream, length);\n```\n\n2/ Create your `AceQLProgressIndicator` instance and enter it to the `AceQLConnection` instance just before `AceQLCommand.ExecuteNonQueryAsync`:\n\n```c#\n  AceQLProgressIndicator progressIndicator = new AceQLProgressIndicator();\n  connection.SetProgressIndicator(progressIndicator);\n```\nYou then can read `ProgressIndicator.Percent` property in your watching thread.\n\n## Advanced Features\n\n### Calling AceQL Java stored procedures\n\nThe AceQL client SDK allows executing a remote server class that implements the AceQL Server\n\n`org.kawanfw.sql.api.server.executor.ServerQueryExecutor` interface and that returns an `AceQLDataReader`.\n\nSee the `org.kawanfw.sql.api.server.executor.ServerQueryExecutor` [Javadoc](https://docs.aceql.com/rest/soft/11.0/javadoc/org/kawanfw/sql/api/server/executor/ServerQueryExecutor.html).\n\nThe usage on the client side is straightforward with the `AceQLCommand.ExecuteServerQueryAsync()` method:\n\n```C#\npublic async Task ExecuteServerQueryAsync(AceQLConnection connection)\n{\n    AceQLCommand command = new AceQLCommand(connection);\n\n    // Define the server Java class name to call\n    String serverclassName = \"com.mycompany.MyServerQueryExecutor\";\n    \n    // Define the parameters list to pass to the server\n    List\u003cobject\u003e parameters = new List\u003cobject\u003e();\n    parameters.Add(1);\n\n    // Our dataReader must be disposed to delete underlying downloaded files\n    // Call the remote com.mycompany.MyServerQueryExecutor.executeQuery method\n    // and get the result\n    using AceQLDataReader dataReader = await command.ExecuteServerQueryAsync(serverclassName, parameters);\n    while (dataReader.Read())\n    {\n        AceQLConsole.WriteLine();\n        AceQLConsole.WriteLine(\"\" + DateTime.Now);\n        int i = 0;\n        AceQLConsole.WriteLine(\n            \"customer_id   : \" + dataReader.GetValue(i++) + \"\\n\"\n            + \"customer_title: \" + dataReader.GetValue(i++) + \"\\n\"\n            + \"fname         : \" + dataReader.GetValue(i++) + \"\\n\"\n            + \"lname         : \" + dataReader.GetValue(i++));\n    }\n}\n```\n\n\n\n### Using outer authentication without a password  and with an AceQL Session ID\n\nSome working environments (Intranet, etc.) require that the client user authenticates himself without a password. Thus, it is not possible for this users to authenticate though the AceQL client SDK.\n\nIn this case, you may use directly the native HTTP [login](https://github.com/kawansoft/aceql-http/blob/master/aceql-http-user-guide-api.md#login) API to authenticate the users and retrieve the `session_id` returned by the API.\n\nThe `session_id` value will be set directly into the `connectionString`  or passed to an `AceQLCredential`:\n\n```C#\n // Port number is the port number used to start the Web Server:\nstring server = \"https://www.acme.com:9443/aceql\";\nstring database = \"kawansoft_example\";\n\nstring connectionString = $\"Server={server}; Database={database}\";\n\nstring username = \"username\";\nstring sessionId = GetSessionIdFromLoginAPI();\n\nAceQLConnection connection = new AceQLConnection(connectionString)\n{\n    // Authentication will be done without password and using the sessionId.\n    Credential = new AceQLCredential(username, sessionId);\n};\n\n// Opens the connection with the remote database\nawait connection.OpenAsync();`\n```\n\n### Enable default system authentication\n\nThe boolean `EnableDefaultSystemAuthentication` may be set in the connection string in order to enable default system authentication. \n\nThis is useful for configurations where a third party system (for instance IIS) is placed between the client and the AceQL Server and checks natively the authentication (for instance with Active Directory):\n\n```C#\n\"Server=https://www.acme.com:9443/aceql; Database=myDataBase; Username=myUsername; Password=myPassword; EnableDefaultSystemAuthentication=True\"\n```\n\n## Using the Metadata Query API \n\nThe metadata API allows:\n\n- downloading a remote database schema\n  in HTML or text format,\n- to get a remote database main properties,\n- to get the list of tables, \n- to get the details of each table. \n\nIt also allows wrapping remote tables, columns, indexes, etc. into\neasy to use provided C# classes: Table, Index, Column, etc.\n\nFirst step is to get an instance of `RemoteDatabaseMetaData`:\n\n```C#\nRemoteDatabaseMetaData remoteDatabaseMetaData = connection.GetRemoteDatabaseMetaData();\n```\n\n### Downloading database schema into a file\n\nDownloading a schema into a  `File` is done through the method. See the `RemoteDatabaseMetaData` [Documentation](https://docs.aceql.com/rest/soft_csharp/7.8/csharpdoc_sdk):\n\n```C#\nstring userPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);\nstring schemaFilePath = userPath + \"\\\\db_schema.out.html\";\n\n// Download Schema in HTML format:\nusing (Stream stream = await remoteDatabaseMetaData.DbSchemaDownloadAsync())\n{\n    using (var fileStream = File.Create(schemaFilePath))\n    {\n        stream.CopyTo(fileStream);\n    }\n}\n```\n\nSee an example of the built HTML schema:  [db_schema.out.html](https://docs.aceql.com/rest/soft_csharp/7.8/src/db_schema.out.html)\n\n### Accessing remote database main properties\n\nThe `JdbcDatabaseMetaData` class wraps instance the main value retrieved by a remote server JDBC call to `java.sql.Connection.getMetaData`():\n\n```C#\nJdbcDatabaseMetaData jdbcDatabaseMetaData = await remoteDatabaseMetaData.GetJdbcDatabaseMetaDataAsync();\nConsole.WriteLine(\"Major Version: \" + jdbcDatabaseMetaData.GetJDBCMajorVersion);\nConsole.WriteLine(\"Minor Version: \" + jdbcDatabaseMetaData.GetJDBCMinorVersion);\nConsole.WriteLine(\"IsReadOnly   : \" + jdbcDatabaseMetaData.IsReadOnly);\n```\n\n### Getting Details of Tables and Columns\n\nSee the `RemoteDatabaseMetaData` [Documentation](https://docs.aceql.com/rest/soft_csharp/7.8/csharpdoc_sdk):\n\n```C#\nConsole.WriteLine(\"Get the table names:\");\nList\u003cString\u003e tableNames = await remoteDatabaseMetaData.GetTableNamesAsync();\n\nConsole.WriteLine(\"Print the column details of each table:\");\nforeach (String tableName in tableNames)\n{\n    Table table = await remoteDatabaseMetaData.GetTableAsync(tableName);\n\n    Console.WriteLine(\"Columns:\");\n    foreach(Column column in table.Columns)\n    {\n        Console.WriteLine(column);\n    }\n}\n```\n\n## Implementing Retry Logic in HTTP Requests (Experimental)\n\nIn networked applications, transient failures can cause HTTP requests to fail. To handle this, you can configure retry logic using the `ConnectionInfo` class.\n\n- **`MaxRetries`**: Sets the number of retry attempts after a failure. For example, `ConnectionInfo.MaxRetries = 3` retries the request up to three times.\n- **`RetryIntervalMs`**: Specifies the delay between retries in milliseconds. For instance, `ConnectionInfo.RetryIntervalMs = 1000` adds a 1-second pause between retries.\n\nExample configuration:\n\n```C#\nConnectionInfo.MaxRetries = 3;\nConnectionInfo.RetryIntervalMs = 1000;\n```\n\nThis setup retries failed requests up to three times with a 1-second interval between attempts.\n\n------\n\n","funding_links":["https://github.com/sponsors/kawansoft"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkawansoft%2Faceql.client2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkawansoft%2Faceql.client2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkawansoft%2Faceql.client2/lists"}