C# client usage
This page provides language-specific guidance for using the Data API C# client.
For information about installing and getting started with the C# client, see Get started with the Data API.
Client hierarchy
When you create apps using the Data API clients, you must instantiate a DataAPIClient object.
The DataAPIClient object serves as the entry point to the client hierarchy. It includes the following concepts:
-
-
Databases-
CollectionsandTables
-
-
Adjacent to these concepts are the administration classes for database administration. The specific administration classes you use, and how you instantiate them, depends on your client language and database type (Astra DB, HCD, or DSE).
You directly instantiate the DataAPIClient object only.
Then, through the DataAPIClient object, you can instantiate and access other classes and concepts.
Where necessary, instructions for instantiating other classes are provided in the command reference relevant to each class.
For instructions for instantiating the DataAPIClient object, see Instantiate a client object.
Custom typing for collections
By default, the Collection object is typed as Collection<Document>, where Document is Dictionary<string, object>.
You can enable stronger typing by specifying a type when you create or get the collection.
Similarly, you can either use Document or a custom type for other classes that accept a type, such as FindOptions<T> and Builders<T>.
Your custom type can use the following attributes:
-
[CollectionName("COLLECTION_NAME")]on your custom type to specify the collection name. -
[CollectionVector(PARAMETERS)]on your custom type to configure the vector configuration for the collection. -
[CollectionVectorize(PARAMETERS)]on your custom type to configure the vector configuration for the collection and an embedding provider to automatically generate embeddings. -
[LexicalOptions(PARAMETERS)]on your custom type to configure the lexical search configuration for the collection. -
[DocumentId]or[DocumentId(DefaultIdType.ID_TYPE)]on a property to map that field to the_idfield in the database. For more information about document IDs, see Document IDs. -
[DocumentMapping(DocumentMappingField.Vector)]on a property to map that field to the$vectorfield in the database. -
[DocumentMapping(DocumentMappingField.Vectorize)]on a property to map that field to the$vectorizefield in the database. -
[DocumentMapping(DocumentMappingField.Lexical)]on a property to map that field to the$lexicalfield in the database. -
[DocumentMapping(DocumentMappingField.Hybrid)]on a property to map that field to the$vectorizeand$lexicalfields in the database via the$hybridshorthand. -
[DocumentMapping(DocumentMappingField.Similarity)]on a nullable floating-point property to receive the similarity score from a vector search. This field is populated by the database during vector similarity searches when requested. It should not be set by your application during insertions or updates. Use a nullable type such asdouble?to prevent serialization when the value is not set.
Typed collection example
using System.Text.Json;
using DataStax.AstraDB.DataApi;
using DataStax.AstraDB.DataApi.Collections;
using DataStax.AstraDB.DataApi.Core;
using DataStax.AstraDB.DataApi.Core.Query;
using DataStax.AstraDB.DataApi.SerDes;
namespace Examples;
[CollectionName("COLLECTION_NAME")]
[CollectionVectorize(
"nvidia",
"nvidia/nv-embedqa-e5-v5",
SimilarityMetric.Cosine
)]
[LexicalOptions(
TokenizerName = "standard",
Filters = new[] { "lowercase", "stop", "porterstem", "asciifolding" },
CharacterFilters = new string[] { }
)]
public class ExampleDocument
{
[DocumentId]
public Guid? Id { get; set; }
[DocumentMapping(DocumentMappingField.Vector)]
public float[]? VectorEmbeddings { get; set; }
[DocumentMapping(DocumentMappingField.Vectorize)]
public string? StringToVectorize { get; set; }
[DocumentMapping(DocumentMappingField.Lexical)]
public string? StringForLexical { get; set; }
[DocumentMapping(DocumentMappingField.Similarity)]
public double? Similarity { get; set; }
public string? Title { get; set; }
public int? NumberOfPages { get; set; }
public bool? IsCheckedOut { get; set; }
}
public class Program
{
static async Task Main()
{
// Get an existing collection
var client = new DataAPIClient();
var database = client.GetDatabase(
"API_ENDPOINT",
"APPLICATION_TOKEN"
);
var collection = database.GetCollection<ExampleDocument>();
// Insert documents
var insertionResult = await collection.InsertManyAsync(
[
new ExampleDocument()
{
VectorEmbeddings = [0.08f, -0.62f, 0.39f],
Title = "Ocean Depths",
NumberOfPages = 237,
IsCheckedOut = false,
},
new ExampleDocument()
{
StringToVectorize =
"A thrilling novel about the future of flight.",
Title = "Sky Limits",
NumberOfPages = 298,
},
new ExampleDocument()
{
Id = Guid.CreateVersion7(),
Title = "Open Plains",
IsCheckedOut = true,
},
]
);
// Find documents
var filterBuilder = Builders<ExampleDocument>.CollectionFilter;
var filter = filterBuilder.And(
filterBuilder.Eq(x => x.IsCheckedOut, false),
filterBuilder.Lt(x => x.NumberOfPages, 300)
);
var result = collection.Find(
filter,
new CollectionFindOptions<ExampleDocument>()
{
Projection = Builders<ExampleDocument>
.Projection.Include(x => x.Title)
.Include(x => x.IsCheckedOut),
}
);
await foreach (var document in result)
{
Console.WriteLine(JsonSerializer.Serialize(document));
}
}
}
Custom typing for tables
By default, the Table object is typed as Table<Row>, where Row is Dictionary<string, object>.
You can enable stronger typing by specifying a type when you create or get the table.
Similarly, you can either use Row or a custom type for other classes that accept a type, such as FindOptions<T> and Builders<T>.
Your custom type can use the following attributes:
-
[TableName("TABLE_NAME")]on your custom type to specify the table name. -
[ColumnPrimaryKey(ORDER_INTEGER)]and[ColumnPrimaryKeySort(ORDER_INTEGER, SortDirection.SORT_DIRECTION)]to specify the primary keys. -
[ColumnName("COLUMN_NAME")]to specify column names. -
[ColumnVector(DIMENSION)]to configure the vector configuration for a vector column. -
[ColumnVectorize(PARAMETERS)]to configure the vector configuration for a vector column and an embedding provider to automatically generate embeddings. -
[ColumnJsonString]to serialize and deserialize a column as a JSON string. -
[ColumnMapping(DocumentMappingField.Similarity)]on a nullable floating-point property to receive the similarity score from a vector search. -
[ColumnIgnore]to exclude a property from table column mapping.
If your table uses user-defined types (UDTs), use the [UserDefinedType("TYPE_NAME")] attribute on your custom user-defined type classes.
Typed table example
For more examples, see Create a table.
using System.Text.Json;
using DataStax.AstraDB.DataApi;
using DataStax.AstraDB.DataApi.Core;
using DataStax.AstraDB.DataApi.Core.Query;
using DataStax.AstraDB.DataApi.SerDes;
using DataStax.AstraDB.DataApi.Tables;
namespace Examples;
[TableName("TABLE_NAME")]
public class ExampleRow
{
[ColumnPrimaryKey(1)]
[ColumnName("title")]
public string Title { get; set; } = null!;
[ColumnPrimaryKey(2)]
[ColumnName("rating")]
public double Rating { get; set; }
[ColumnVectorize(
provider: "nvidia",
modelName: "nvidia/nv-embedqa-e5-v5",
dimension: 1024
)]
[ColumnName("example_vectorize")]
public object? ExampleVectorize { get; set; }
[ColumnVector(1024)]
[ColumnName("example_vector")]
public double[]? ExampleVector { get; set; }
[ColumnPrimaryKeySort(1, SortDirection.Ascending)]
[ColumnName("number_of_pages")]
public int? NumberOfPages { get; set; }
[ColumnPrimaryKeySort(2, SortDirection.Descending)]
[ColumnName("is_checked_out")]
public bool? IsCheckedOut { get; set; }
[ColumnName("editor")]
public Person? Editor { get; set; }
[ColumnJsonString]
[ColumnName("review")]
public List<Review>? Reviews { get; set; }
[ColumnIgnore]
public TimeUuid TimeUuid { get; set; }
[ColumnIgnore]
[ColumnMapping(ColumnMappingField.Similarity)]
public double? Similarity { get; set; }
}
[UserDefinedType("person")]
public class Person
{
[ColumnName("name")]
public string? Name { get; set; }
[ColumnName("level")]
public int? Level { get; set; }
};
public class Review
{
public string? CustomerName { get; set; }
public int Rating { get; set; }
public string? Comment { get; set; }
public DateTime ReviewDate { get; set; }
}
public class Program
{
static async Task Main()
{
// Get an existing table
var client = new DataAPIClient();
var database = client.GetDatabase(
"API_ENDPOINT",
"APPLICATION_TOKEN"
);
var table = database.GetTable<ExampleRow>();
// Insert rows into the table
var rows = new List<ExampleRow>()
{
new ExampleRow()
{
ExampleVector = [0.08f, -0.62f, 0.39f],
Title = "Ocean Depths",
Rating = 4.5,
NumberOfPages = 237,
IsCheckedOut = false,
},
new ExampleRow()
{
ExampleVectorize =
"A thrilling novel about the future of flight.",
Title = "Sky Limits",
NumberOfPages = 298,
IsCheckedOut = true,
Rating = 3.9,
},
new ExampleRow()
{
Title = "Open Plains",
Rating = 4.2,
IsCheckedOut = true,
NumberOfPages = 499,
},
};
await table.InsertManyAsync(rows);
// Find rows
var filterBuilder = Builders<ExampleRow>.TableFilter;
var filter = filterBuilder.And(
filterBuilder.Eq(b => b.IsCheckedOut, false),
filterBuilder.Lt(b => b.NumberOfPages, 300)
);
var results = table.Find(
filter,
new TableFindOptions<ExampleRow>()
{
Sort = Builders<ExampleRow>
.TableSort.Ascending(b => b.NumberOfPages)
.Descending(b => b.Title),
Projection = Builders<ExampleRow>
.Projection.Include(b => b.IsCheckedOut)
.Include(b => b.Title),
}
);
await foreach (var row in results)
{
Console.WriteLine(JsonSerializer.Serialize(row));
}
}
}
Serialization/deserialization
The client uses System.Text.Json for serialization/deserialization.
You can use the [JsonPropertyName] attribute to specify the name of a property in the JSON representation.
For more information, see the Microsoft documentation.
Customize API interaction
You can customize things like timeouts, serialization/deserialization, authentication keys, and the working keyspace.
To do so, provide the options parameter at any level of the client hierarchy, including the DataAPIClient, Database, and Collection objects.
You can also provide options directly to methods of these objects.
The options specified at the most granular level will take precedence.
using DataStax.AstraDB.DataApi;
using DataStax.AstraDB.DataApi.Admin;
using DataStax.AstraDB.DataApi.Collections;
using DataStax.AstraDB.DataApi.Core;
using DataStax.AstraDB.DataApi.Core.Query;
namespace Examples;
public class Program
{
static async Task Main()
{
// Specifies the token and timeouts
var clientOptions = new CommandOptions()
{
Token = "APPLICATION_TOKEN",
TimeoutOptions = new TimeoutOptions()
{
ConnectionTimeout = TimeSpan.FromMilliseconds(3000),
RequestTimeout = TimeSpan.FromMilliseconds(9000),
},
};
var client = new DataAPIClient(clientOptions);
// Overrides the token that was passed to the client
// and specifies the keyspace
var databaseOptions = new GetDatabaseOptions()
{
Token = "APPLICATION_TOKEN",
Keyspace = "KEYSPACE_NAME",
};
var database = client.GetDatabase(
"API_ENDPOINT",
databaseOptions
);
// Overrides the keyspace that was passed to the database
var collectionOptions = new GetCollectionOptions()
{
Keyspace = "KEYSPACE_NAME",
};
var collection = database.GetCollection(
"COLLECTION_NAME",
collectionOptions
);
// Overrides a timeout that was passed to the client
var methodOptions = new CollectionFindOneOptions<Document>()
{
TimeoutOptions = new TimeoutOptions()
{
RequestTimeout = TimeSpan.FromMilliseconds(4000),
},
};
var result = await collection.FindOneAsync(methodOptions);
}
}
Asynchronous and synchronous methods
Most methods have both a synchronous and asynchronous version.
For example, the InsertMany and InsertManyAsync methods of the Collection class.
Aside from the asynchronous behavior, the methods are identical.
Lists and cursors
For operations that return a list of documents or rows, the Data API returns results in pages.
You can use IAsyncEnumerable<T> or IEnumerable<T> to iterate over the results.
The client automatically handles batch iteration.
The client supports standard IEnumerable extensions, like Skip, Take, and Where.
Alternatively, you can use the InitialPageState option to fetch a specific page of results.
This is useful for cases where an external action triggers fetching the next page of results.
For example, you might use this feature if you implement a "Load More" button or an infinite scroll interface.
For examples, see Iterate over found documents.
If you need a list of all results, call ToList().
However, the time and memory required for this operation depend on the number of results.
This is not recommended when you expect a large number of documents.
DataAPIVector
Unlike the other clients, the C# client doesn’t have a DataAPIVector class.
The C# client automatically binary-encodes your vector embeddings when you insert into a collection.
Other usage of vector embeddings, such as in vector search, is not binary encoded.
See also
For examples and details about each command, see the command references About collections with the Data API and About tables with the Data API.
For major changes between versions, see Data API client upgrade guide.
For the complete client reference, see C# client reference.