Snowflake.Client 0.4.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Snowflake.Client --version 0.4.0
NuGet\Install-Package Snowflake.Client -Version 0.4.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Snowflake.Client" Version="0.4.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Snowflake.Client --version 0.4.0
#r "nuget: Snowflake.Client, 0.4.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Snowflake.Client as a Cake Addin
#addin nuget:?package=Snowflake.Client&version=0.4.0

// Install Snowflake.Client as a Cake Tool
#tool nuget:?package=Snowflake.Client&version=0.4.0

NuGet alternate text is missing from this package README image Targets License

Snowflake.Client

.NET client for Snowflake REST API.
Provides API to execute SQL queries and map response to your models.
Read my blog post about the ideas behind it.

Installation

Add nuget package Snowflake.Client to your project:

PM> Install-Package Snowflake.Client

Main features

  • User/Password authentication
  • Execute any SQL queries with parameters
  • Map response data to your models

Basic usage

// Creates new client
var snowflakeClient = new SnowflakeClient("user", "password", "account", "region");

// Executes query and maps response data to "Employee" class
var employees = await snowflakeClient.QueryAsync<Employee>("SELECT * FROM MASTER.PUBLIC.EMPLOYEES;");

// Executes query and returns raw response from Snowflake (rows, columns and query information)
var queryRawResponse = await snowflakeClient.QueryRawResponseAsync("SELECT * FROM MASTER.PUBLIC.EMPLOYEES;");

// Executes query and returns value of first cell as string result
string useRoleResult = await snowflakeClient.ExecuteScalarAsync("USE ROLE ACCOUNTADMIN;");

// Executes query and returns affected rows count
int affectedRows = await snowflakeClient.ExecuteAsync("INSERT INTO EMPLOYEES Title VALUES (?);", "Dev");

Comparison with Snowflake.Data

Official Snowflake.Data connector implements ADO.NET interfaces (IDbConnection, IDataReader etc), so you have to work with it as with usual database, however under the hood it actually uses Snowflake REST API. In contrast Snowflake.Client is designed as REST API client wrapper with convenient API. Read more about it.

Improvements in Snowflake.Client vs Snowflake.Data:

  • Performance: Re-uses Snowflake session, i.e. ~3x less roundtrips to SF
  • Performance: Doesn't have additional intermediate mapping from SF to DB types
  • Better API: Clean and simple API vs verbose ADO.NET
  • No third party dependencies vs 9 external packages in Snowflake.Data

New features in Snowflake.Client:

  • Map response data to entities
  • Supports describeOnly flag
  • Has option to return raw data from Snowflake response (including QueryID and more)
  • Exposes Snowflake session info
  • New SQL parameter binding API with a few options (inspired by Dapper)

Missing features in Snowflake.Client:

  • Authentication options other than basic user/password
  • GET/PUT command (was recenlty implemented in Snowflake.Data)

Parameter binding

Snowflake supports two placeholder formats for parameter binding:

  • Positional — with a "?" placeholders
  • Named — parameter name prefixed with a ":"

Both formats are supported. You can use positional placeholders to bind values of "simple" types (like Int, String or DateTime). To bind named parameters you can use classes, structs, anonymous types or dictionary. See examples below.

// Positional placeholder, any "simple" type
var result1 = await snowflakeClient.QueryAsync<Employee>
              ("SELECT * FROM EMPLOYEES WHERE TITLE = ?", "Programmer");

// Positional placeholders, any IEnumerable<T>
var result2 = await snowflakeClient.QueryAsync<Employee>
              ("SELECT * FROM EMPLOYEES WHERE ID IN (?, ?, ?)", new int[] { 1, 2, 3 });

// Named placeholders, any custom class or struct
var result3 = await snowflakeClient.QueryAsync<Employee>  
              ("SELECT * FROM EMPLOYEES WHERE TITLE = :Title", new Employee() { Title = "Programmer" });

// Named placeholders, any anonymous class
var result4 = await snowflakeClient.QueryAsync<Employee>     
              ("SELECT * FROM EMPLOYEES WHERE TITLE = :Title", new { Title = "Junior" });

// Named placeholders, any IDictionary<T>
var result5 = await snowflakeClient.QueryAsync<Employee>
              ("SELECT * FROM EMPLOYEES WHERE TITLE = :Title", new Dictionary<string, string> {{ "Title", "Programmer" }});

Mapping basics

Use QueryAsync<T> method to get response data automatically mapped to your model (T):

// Executes query and maps response data to "Employee" class
IEnumerable<Empolyee> employees = await snowflakeClient.QueryAsync<Employee>("SELECT * FROM MASTER.PUBLIC.EMPLOYEES;");

// Your model
public class Employee
{ 
    public int Id { get; set; }
    public float? Rating { get; set; }
    public bool? IsFired { get; set; }
    public string FirstName { get; set; }
    public string[] ContactLinks { get; set; } // supports arrays and lists
    public EmplyeeInfo Info { get; set; } // supports custom json ojects ("object" and "variant")
    public DateTimeOffset HiredAt { get; set; } // DateTimeOffset for "timestamp_ltz" and "timestamp_tz"
    public DateTime FiredAt { get; set; } // DateTime for "date", "time" and "timestamp_ntz"
    public byte[] Image { get; set; } // bytes array/list for "binary"
}

Internally it uses System.Text.Json to deserialize Snowflake data to your model. It uses default deserialize behavior, except PropertyNameCaseInsensitive is set to true, so your properties names don't have to be in the exact same case as column names in your tables.
You can override this behavior by providing custom JsonSerializerOptions. You can pass it in SnowflakeClient constructor or you can set it directly via SnowflakeDataMapper.SetJsonMapperOptions(jsonSerializerOptions).

Advanced usage

You may want to get raw response from Snowflake, for example, to get QueryID or some other information.
In this case you can use mapper explicitly:

// Executes query and returns raw response from Snowflake (rows, columns and query information)
var queryDataResponse = await snowflakeClient.QueryRawResponseAsync("SELECT * FROM MASTER.PUBLIC.EMPLOYEES;");

// Maps Snowflake rows and columns to your model (internally uses System.Text.Json)
var employees = SnowflakeDataMapper.MapTo<Employee>(queryDataResponse.Columns, queryDataResponse.Rows);

You can override internal http client. Fr example, this can be used to bypass SSL check:

var handler = new HttpClientHandler
{
    SslProtocols = SslProtocols.Tls12,
    CheckCertificateRevocationList = false, 
    ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true // i.e. bypass cert validation
};

var httpClient = new HttpClient(handler);
var snowflakeClient = new SnowflakeClient("user", "password", "account", "region");
snowflakeClient.SetHttpClient(httpClient);

Release notes

0.4.0

  • Increased http client tiemout to 1 hour for a long running queries
  • Added missing cancellation token for a few methods

0.3.9

  • Now can handle long-running queries (> 45 seconds)
  • Now returns date-time values as DateTimeKind.Unspecified

0.3.8

0.3.7

  • Added cancellation token for public async methods
  • Improved mapping tests

0.3.6

  • Set Expect100Continue and UseNagleAlgorithm to false for better HTTP performance
  • Allow streaming for http responses with ResponseHeadersRead option
  • Improved bool mapping
  • Adding IDictionary<> support for binding parameters

0.3.5

  • Added response auto-decompression
  • Added cloud tag auto-detection to finally fix SSL cert issue
  • Fix: explicit URL host now actually have higher priority than auto-constructed
  • Now it automatically replaces underscore in account name
  • More info on this release

0.3.4

  • Forced TLS 1.2 and revocation check as in official connector

0.3.3

0.3.2

  • Added support for binding from class fields and structs
  • Added a lot of unit tests
  • Started working on integration tests
  • Now uses it's own driver name .NET_Snowflake.Client

0.3.1

  • Implemented query cancellation with CancelQueryAsync()
  • Fixed issue with mapping for semi-structured SF types (object, variant, array)
  • Implemented auto-renewing SF session, if its expired
  • Initializes SF session automatically with first query
  • QueryRawAsync() now returns response with all metadata
  • Extracted interfaces for public classes

0.3.0

  • Changed all API methods to be async
  • Added a lot of documentation in readme file
  • Implemented first unit tests
  • Target frameworks changed to NET Standard 2.0 and .NET Core 3.1

0.2.4

  • Fix: now actually uses passed JsonMapperOptions
  • New Execute() method which returns affected rows count

0.2.3

  • Changed return type of ExecuteScalar() to string
  • Added comments and documentation for public methods

0.2.2

  • First public release
  • Implemented all basic features
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 is compatible. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.4.6 53,298 6/4/2023
0.4.5 32,174 4/9/2023
0.4.4 24,409 1/1/2023
0.4.3 12,958 12/10/2022
0.4.2 884 12/6/2022
0.4.1 8,311 10/10/2022
0.4.0 23,254 6/29/2022
0.3.9 39,497 1/29/2022
0.3.8 958 1/22/2022
0.3.6 37,792 8/22/2021
0.3.5 31,786 4/2/2021
0.3.4 1,375 3/27/2021
0.3.3 1,547 3/20/2021
0.3.2 928 2/26/2021
0.3.1 38,264 1/29/2021
0.3.0 809 1/25/2021
0.2.4 1,067 10/10/2020
0.2.3 918 9/27/2020
0.2.2 964 9/24/2020