diff --git a/_data/navigation.yml b/_data/navigation.yml index 63bd9d2c..25b20b4c 100644 --- a/_data/navigation.yml +++ b/_data/navigation.yml @@ -81,10 +81,12 @@ versions: url: /docs/latest/getting-started-with-scalardb/ - title: "Getting Started with ScalarDB by Using Kotlin" url: /docs/latest/getting-started-with-scalardb-by-using-kotlin/ - - title: "Getting Started with ScalarDB Cluster" - url: /docs/latest/scalardb-cluster/getting-started-with-scalardb-cluster-overview/ - title: "Getting Started with ScalarDB Analytics with PostgreSQL" url: /docs/latest/scalardb-analytics-postgresql/getting-started/ + - title: "Getting Started with ScalarDB Cluster" + url: /docs/latest/scalardb-cluster/getting-started-with-scalardb-cluster-overview/ + - title: "Getting Started with the ScalarDB Cluster .NET Client SDK" + url: /docs/latest/scalardb-cluster-dotnet-client-sdk/ # Samples docs - title: "Samples" children: @@ -229,10 +231,10 @@ versions: url: /docs/3.10/getting-started-with-scalardb/ - title: "Getting Started with ScalarDB by Using Kotlin" url: /docs/3.10/getting-started-with-scalardb-by-using-kotlin/ - - title: "Getting Started with ScalarDB Cluster" - url: /docs/3.10/scalardb-cluster/getting-started-with-scalardb-cluster-overview/ - title: "Getting Started with ScalarDB Analytics with PostgreSQL" url: /docs/3.10/scalardb-analytics-postgresql/getting-started/ + - title: "Getting Started with ScalarDB Cluster" + url: /docs/3.10/scalardb-cluster/getting-started-with-scalardb-cluster-overview/ # Samples docs - title: "Samples" children: @@ -371,10 +373,10 @@ versions: url: /docs/3.9/getting-started-with-scalardb/ - title: "Getting Started with ScalarDB by Using Kotlin" url: /docs/3.9/getting-started-with-scalardb-by-using-kotlin/ - - title: "Getting Started with ScalarDB Cluster" - url: /docs/3.9/scalardb-cluster/getting-started-with-scalardb-cluster-overview/ - title: "Getting Started with ScalarDB Analytics with PostgreSQL" url: /docs/3.9/scalardb-analytics-postgresql/getting-started/ + - title: "Getting Started with ScalarDB Cluster" + url: /docs/3.9/scalardb-cluster/getting-started-with-scalardb-cluster-overview/ # Samples docs - title: "Samples" children: diff --git a/docs/3.10/scalardb-cluster/getting-started-with-scalardb-cluster-overview.md b/docs/3.10/scalardb-cluster/getting-started-with-scalardb-cluster-overview.md index 4ce7dda4..3245b21e 100644 --- a/docs/3.10/scalardb-cluster/getting-started-with-scalardb-cluster-overview.md +++ b/docs/3.10/scalardb-cluster/getting-started-with-scalardb-cluster-overview.md @@ -1,6 +1,6 @@ # Getting Started with ScalarDB Cluster -The following is tutorials for getting started with using ScalarDB Cluster: +The following are tutorials for getting started with using ScalarDB Cluster: - [Getting Started with ScalarDB Cluster](getting-started-with-scalardb-cluster.md) - [Getting Started with ScalarDB Cluster GraphQL](getting-started-with-scalardb-cluster-graphql.md) diff --git a/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-admin-api.md b/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-admin-api.md new file mode 100644 index 00000000..72cc2cf0 --- /dev/null +++ b/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-admin-api.md @@ -0,0 +1,104 @@ +# Getting Started with the Administrative API in the ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK supports the Administrative API of ScalarDB Cluster. By using this API, you can manage ScalarDB Cluster from .NET applications. + +{% capture notice--info %} +**Note** + +Although we recommend using asynchronous methods as in the following examples, you can use synchronous methods instead. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +## Get a transaction manager + +First, you need to get an object for interacting with the Administrative API. To get the object, you can use `TransactionFactory` as follows, replacing `` with the URL of your cluster: + +```c# +var scalarDbOptions = new ScalarDbOptions + { + Address = "http://:60053" + HopLimit = 10 + }; +var factory = TransactionFactory.Create(scalarDbOptions); + +using var admin = factory.GetTransactionAdmin(); +``` + +## Manage ScalarDB Cluster + +The following operations can be performed by using the ScalarDB Cluster .NET Client SDK. + +### Create a new namespace + +```c# +await admin.CreateNamespaceAsync("ns", ifNotExists: true); +``` + +### Drop a namespace + +```c# +await admin.DropNamespaceAsync("ns", ifExists: true); +``` + +### Check if a namespace exists + +```c# +var namespaceExists = await admin.IsNamespacePresentAsync("ns"); +``` + +### Create a new table + +```c# +using Scalar.Db.Cluster.Rpc.V1; +// ... +using ScalarDB.Net.Client.Builders; + +// ... + +var tableMetadata = + new TableMetadataBuilder() + .AddPartitionKey("pk", DataType.Int) + .AddClusteringKey("ck", DataType.Double) + .AddSecondaryIndex("index", DataType.Float) + .AddColumn("ordinary", DataType.Text) + .Build(); + +await admin.CreateTableAsync("ns", "table_name", tableMetadata, ifNotExists: true); +``` + +### Drop a table + +```c# +await admin.DropTableAsync("ns", "table_name", ifExists: true); +``` + +### Checking if a table exists + +```c# +var tableExists = await admin.IsTablePresentAsync("ns", "table_name"); +``` + +### Get the names of existing tables + +```c# +var tablesList = await admin.GetTableNamesAsync("ns"); +``` + +### Create the Coordinator table + +```c# +await admin.CreateCoordinatorTablesAsync(); +``` + +### Drop the Coordinator table + +```c# +await admin.DropCoordinatorTablesAsync(); +``` + +### Check if the Coordinator table exist + +```c# +var exists = await admin.AreCoordinatorTablesPresentAsync(); +``` diff --git a/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-aspnet-and-di.md b/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-aspnet-and-di.md new file mode 100644 index 00000000..5b3b9711 --- /dev/null +++ b/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-aspnet-and-di.md @@ -0,0 +1,49 @@ +# Getting Started with ASP.NET Core and Dependency Injection in the ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK supports Dependency Injection in frameworks like ASP.NET Core. + +You can register the ScalarDB Cluster transaction managers as services in `IServiceCollection` as follows, replacing `` with the URL of your cluster: + +```c# +using ScalarDB.Net.Client.Extensions; + +//... + +var builder = WebApplication.CreateBuilder(args); + +//... + +builder.Services.AddScalarDbCluster(options => +{ + options.Address = "http:\\:60053"; + options.HopLimit = 10; + options.EnableCrud = true; // true by default + options.EnableSql = true; // false by default + options.EnableAdmin = true; // false by default +}); +``` + +After registering the transaction managers, they can be injected into the controller's constructor as follows: + +```c# +[ApiController] +public class OrderController: ControllerBase +{ + private readonly IDistributedTransactionManager _manager; + + public OrderController(IDistributedTransactionManager manager) + { + _manager = manager; + } +} +``` + +Although these examples are for WebApi projects, the examples will work in a similar way in GrpcService projects. + +### `AddScalarDbCluster`-specific options + +This section describes some specific options for `AddScalarDbCluster`: + +- **EnableCrud:** Enables the transaction managers for executing CRUD operations. `IDistributedTransactionManager` and `ITwoPhaseCommitTransactionManager` will become available for injection. +- **EnableSql:** Enables the transaction managers for executing SQL statements. `ISqlTransactionManager` and `ISqlTwoPhaseCommitTransactionManager` will become available for injection. +- **EnableAdmin:** Enables the administrative interface. `IDistributedTransactionAdmin` will become available for injection. diff --git a/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-auth.md b/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-auth.md new file mode 100644 index 00000000..6dd03ce0 --- /dev/null +++ b/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-auth.md @@ -0,0 +1,27 @@ +# Getting Started with ScalarDB Auth by Using ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK supports [ScalarDB Auth](https://github.com/scalar-labs/scalardb-cluster/blob/main/docs/scalardb-auth-with-sql.md), which allows you to authenticate and authorize your requests to ScalarDB Cluster. + +## Set credentials in `ScalarDbOptions` + +First, you need to get a transaction manager or transaction admin object with credentials by using `TransactionFactory` as follows, replacing the contents in the angle brackets as described. Also, be sure to replace `` with `GetTransactionManager()`, `GetTwoPhaseCommitTransactionManager()`, `GetSqlTransactionManager()`, or `GetSqlTwoPhaseCommitTransactionManager()`. + +```c# +var scalarDbOptions = new ScalarDbOptions + { + Address = "http://:60053" + HopLimit = 10, + AuthEnabled = true, + Username = "", + Password = "" + }; +var factory = TransactionFactory.Create(scalarDbOptions); + +// To get a transaction manager +using var manager = factory.(); + +// To get a transaction admin +using var admin = factory.GetTransactionAdmin(); +``` + +A transaction manager or transaction admin object created from `TransactionFactory` with the provided credentials will automatically log in to ScalarDB Cluster and can communicate with it. diff --git a/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-distributed-sql-transactions.md b/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-distributed-sql-transactions.md new file mode 100644 index 00000000..34434935 --- /dev/null +++ b/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-distributed-sql-transactions.md @@ -0,0 +1,112 @@ +# Getting Started with Distributed SQL Transactions in the ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK supports the distributed SQL transaction functionality of ScalarDB Cluster. The SDK includes transaction and manager abstractions for easier communication within a cluster. + +{% capture notice--info %} +**Note** + +Although we recommend using asynchronous methods, as in the following examples, you can use synchronous methods instead. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +For details about distributed non-SQL transactions, see [Getting Started with Distributed Transactions in the ScalarDB Cluster .NET Client SDK](getting-started-with-distributed-transactions.md). + +## Get a transaction manager + +First, you need to get a transaction manager object for distributed SQL transactions. To get the transaction manager object, you can use `TransactionFactory` as follows, replacing `` with the URL of your cluster: + +```c# +var scalarDbOptions = new ScalarDbOptions + { + Address = "http://:60053" + HopLimit = 10 + }; +var factory = TransactionFactory.Create(scalarDbOptions); + +using var manager = factory.GetSqlTransactionManager(); +``` + +## Execute SQL queries + +To execute a SQL statement, you need a `SqlStatement` object, which can be created by using a builder as follows: + +```c# +using ScalarDB.Net.Client.Builders; + +// ... + +var sqlStatement = + new SqlStatementBuilder() + .SetSql("SELECT * FROM order_service.statements WHERE item_id = :item_id") + .AddParam("item_id", 2) + .Build(); +``` + +A single SQL statement can be executed directly by using the transaction manager as follows: + +```c# +var resultSet = await manager.ExecuteAsync(sqlStatement); +``` + +The result from the `ExecuteAsync` method will contain records received from the cluster. The SDK has `GetValue`, `TryGetValue`, and `IsNull` extension methods to simplify using the records: + +```c# +using ScalarDB.Net.Client.Extensions; + +// ... + +foreach (var record in resultSet.Records) +{ + // Getting an integer value from the "item_id" column. + // If it fails, an exception will be thrown. + var itemId = record.GetValue("item_id"); + + // Trying to get a string value from the "order_id" column. + // If it fails, no exception will be thrown. + if (record.TryGetValue("order_id", out var orderId)) + Console.WriteLine($"order_id: {orderId}"); + + // Checking if the "count" column is null. + if (record.IsNull("count")) + Console.WriteLine("'count' is null"); +} +``` + +## Execute SQL queries in a transaction + +To execute multiple SQL statements as part of a single transaction, you need a transaction object. + +You can create a transaction object by using the transaction manager as follows: + +```c# +var transaction = await manager.BeginAsync(); +``` + +You can also resume a transaction that has already been started as follows: + +```c# +var transaction = manager.Resume(transactionIdString); +``` + +{% capture notice--info %} +**Note** + +The `Resume` method doesn't have an asynchronous version because it only creates a transaction object. Because of this, resuming a transaction by using the wrong ID is possible. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +The transaction has the same `ExecuteAsync` method as the transaction manager. That method can be used to execute SQL statements. + +When a transaction is ready to be committed, you can call the `CommitAsync` method of the transaction as follows: + +```c# +await transaction.CommitAsync(); +``` + +To roll back the transaction, you can use the `RollbackAsync` method: + +```c# +await transaction.RollbackAsync(); +``` diff --git a/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-distributed-transactions.md b/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-distributed-transactions.md new file mode 100644 index 00000000..148aa2bf --- /dev/null +++ b/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-distributed-transactions.md @@ -0,0 +1,181 @@ +# Getting Started with Distributed Transactions in the ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK supports the distributed transaction functionality of ScalarDB Cluster. The SDK includes transaction and manager abstractions for easier communication within a cluster. + +{% capture notice--info %} +**Note** + +Although we recommend using asynchronous methods as in the following examples, you can use synchronous versions instead. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +For details about distributed SQL transactions, see [Getting Started with Distributed SQL Transactions in the ScalarDB Cluster .NET Client SDK](getting-started-with-distributed-sql-transactions.md). + +## Get a transaction manager + +First, you need to get a transaction manager for distributed transactions. To get the transaction manager, you can use `TransactionFactory` as follows, replacing `` with the URL of your cluster: + +```c# +var scalarDbOptions = new ScalarDbOptions + { + Address = "http://:60053" + HopLimit = 10 + }; +var factory = TransactionFactory.Create(scalarDbOptions); + +using var manager = factory.GetTransactionManager(); +``` + +## Manage transactions + +To execute CRUD operations, a transaction is needed. You can begin a transaction by using the transaction manager as follows: + +```c# +var transaction = await manager.BeginAsync(); +``` + +You can also resume a transaction that is already being executed as follows: + +```c# +var transaction = manager.Resume(transactionIdString); +``` + +{% capture notice--info %} +**Note** + +The `Resume` method doesn't have an asynchronous version because it only creates a transaction object. Because of this, resuming a transaction by using the wrong ID is possible. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +When a transaction is ready to be committed, you can call the `CommitAsync` method of the transaction as follows: + +```c# +await transaction.CommitAsync(); +``` + +To roll back the transaction, you can use the `RollbackAsync` method: + +```c# +await transaction.RollbackAsync(); +``` + +## Execute CRUD operations + +A transaction has `GetAsync`, `ScanAsync`, `PutAsync`, `DeleteAsync`, and `MutateAsync` methods to execute CRUD commands against the cluster. As a parameter, these methods have a command object. A command object can be created by using the builders listed in this section. + +To use these builders add the following namespace to the `using` section: + +```c# +using ScalarDB.Net.Client.Builders; +``` + +{% capture notice--info %} +**Note** + +The cluster does not support parallel execution of commands inside one transaction, so make sure to use `await` for asynchronous methods. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +### `GetAsync` method example + +```c# +using GetTypeEnum = Scalar.Db.Cluster.Rpc.V1.Get.Types.GetType; + +// ... + +var get = + new GetBuilder() + .SetNamespaceName("ns") + .SetTableName("statements") + .SetGetType(GetTypeEnum.Get) + .AddPartitionKey("order_id", "1") + .AddClusteringKey("item_id", 2) + .SetProjections("item_id", "count") + .Build(); + +var getResult = await transaction.GetAsync(get); +``` + +### `ScanAsync` method example + +```c# +using static Scalar.Db.Cluster.Rpc.V1.Scan.Types; + +// .. + +var scan = + new ScanBuilder() + .SetNamespaceName("ns") + .SetTableName("statements") + .SetScanType(ScanType.Scan) + .AddPartitionKey("order_id", "1") + .AddStartClusteringKey("item_id", 2) + .SetStartInclusive(true) + .AddEndClusteringKey("item_id", 8) + .SetEndInclusive(true) + .SetProjections("item_id", "count") + .Build(); + +var scanResult = await transaction.ScanAsync(get); +``` + +### `PutAsync` method example + +```c# +var put = + new PutBuilder() + .SetNamespaceName("ns") + .SetTableName("statements") + .AddPartitionKey("order_id", "1") + .AddClusteringKey("item_id", 2) + .AddColumn("count", 11) + .Build(); + +await client.PutAsync(put); +``` + +### `DeleteAsync` method example + +```c# +var delete = + new DeleteBuilder() + .SetNamespaceName("ns") + .SetTableName("statements") + .AddPartitionKey("order_id", "1") + .AddClusteringKey("item_id", 2) + .Build(); + +await client.DeleteAsync(delete); +``` + +### `MutateAsync` method example: + +```c# +using Scalar.Db.Cluster.Rpc.V1; + +// ... + +var put = + new PutBuilder() + .SetNamespaceName("ns") + .SetTableName("statements") + .AddPartitionKey("order_id", "1") + .AddClusteringKey("item_id", 2) + .AddColumn("count", 11) + .Build(); + +var mutate = new Mutation { Put = put }; + +await client.MutateAsync(new[] { mutate }); +``` + +{% capture notice--info %} +**Note** + +To modify data by using the `PutAsync`, `DeleteAsync`, or `MutateAsync` method, the data must be retrieved first by using the `GetAsync` or `ScanAsync` method. +{% endcapture %} + +
{{ notice--info | markdownify }}
diff --git a/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-linq.md b/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-linq.md new file mode 100644 index 00000000..8003a449 --- /dev/null +++ b/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-linq.md @@ -0,0 +1,346 @@ +# Getting Started with LINQ in the ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK supports querying the cluster with LINQ and some EntityFramework-like functionality. However, this SDK doesn't support EntityFramework. + +{% capture notice--info %} +**Note** + +SQL support must be enabled on the cluster to use LINQ. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +## Set up classes + +After confirming that SQL support is enabled, create a C# class for each ScalarDB table that you want to use. For example: + +```c# +using System.ComponentModel.DataAnnotations.Schema; +using ScalarDB.Net.Client.DataAnnotations; + +// ... + +[Table("ns.statements")] +public class Statement +{ + [Column("statement_id", Order = 0), PartitionKey] + public int Id { get; set; } + + [Column("order_id", Order = 1), SecondaryIndex] + public string OrderId { get; set; } = String.Empty; + + [Column("item_id", Order = 2), SecondaryIndex] + public int ItemId { get; set; } + + [Column("count", Order = 3)] + public int Count { get; set; } +} + +[Table("order_service.items")] +public class Item +{ + [Column("item_id", Order = 0), PartitionKey] + public int Id { get; set; } + + [Column("name", Order = 1)] + public string Name { get; set; } = String.Empty; + + [Column("price", Order = 2)] + public int Price { get; set; } +} +``` + +If a partition key, clustering key, or secondary index consists of more than one column, the `Order` property of `ColumnAttribute` will decide the order inside the key or index. + +Create a context class that has properties for all the tables you want to use. For example: + +```c# + public class MyDbContext: ScalarDbContext + { + public ScalarDbSet Statements { get; set; } + public ScalarDbSet Items { get; set; } + } +``` + +After all the classes are created, you need to add the created context to the Dependency Injection. For example: + +```c# +using ScalarDB.Net.Client.Extensions; + +//... + +var builder = WebApplication.CreateBuilder(args); + +//... + +builder.Services.AddScalarDbContext(options => +{ + options.Address = "http:\\:60053"; + options.HopLimit = 10; +}); +``` + +The context can be injected into the controller's constructor as follows: + +```c# +[ApiController] +public class OrderController: ControllerBase +{ + private readonly MyDbContext _myDbContext; + + public OrderController(MyDbContext myDbContext) + { + _myDbContext = myDbContext; + } +} +``` + +## Use LINQ to query properties + +After receiving `MyDbContext` in your controller, you can query its properties by using LINQ. For example: + +### Use query syntax + +```c# +from stat in _myDbContext.Statements +join item in _myDbContext.Items on stat.ItemId equals item.Id +where stat.Count > 2 && item.Name.Contains("apple") +orderby stat.Count descending, stat.ItemId +select new { item.Name, stat.Count }; +``` + +### Use method syntax + +```c# +_myDbContext.Statements + .Where(stat => stat.OrderId == "1") + .Skip(1) + .Take(2); +``` + +### Use the `First` method to get one `Statement` by its partition key + +```c# +_myDbContext.Statements.First(stat => stat.OrderId == "1"); +``` + +### Use the `DefaultIfEmpty` method to perform left outer join + +```c# +from stat in _myDbContext.Statements +join item in _myDbContext.Items on stat.ItemId equals item.Id into items +from i in items.DefaultIfEmpty() +select new { ItemName = i != null ? i.Name : "" } +``` + +The following methods are supported: + +- `Select` +- `Where` +- `Join` +- `GroupJoin` +- `First`/`FirstOrDefault` +- `Skip` +- `Take` +- `OrderBy`/`OrderByDescending` +- `ThenBy`/`ThenByDescending` + +The following `String` methods are supported inside the predicates of `Where` and `First`/`FirstOrDefault` methods: + +- `Contains` +- `StartsWith` +- `EndsWith` + +Unsupported LINQ methods can be used after the supported methods. For example: + +```c# +_myDbContext.Statements + .Where(stat => stat.OrderId == "1") // Will be executed remotely on the cluster. + .Distinct() // Will be executed locally in the app. + .Where(stat => stat.ItemId < 5); // Will be executed locally. +``` + +{% capture notice--info %} +**Note** + +If `Skip` is specified before `Take` or `First`/`FirstOrDefault`, the number that is passed to `Skip` will be added to the `LIMIT` number in the SQL query. By itself, `Skip` won't change the resulting SQL query. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +## Limitations when using LINQ against `ScalarDbSet{T}` objects + +- All method calls are supported inside `Select`. For example: + +```c# +.Select(stat => convertToSomething(stat.ItemId)) +//... +.Select(stat => stat.ItemId * getSomeNumber()) +``` + +- Method calls, except for calls against the querying object, are also supported inside `Where` and `First`/`FirstOrDefault`. For example: + +```c# +.Where(stat => stat.ItemId == getItemId()) // is OK +//... +.Where(stat => stat.ItemId.ToString() == "1") // is not supported +``` + +- All method calls are supported inside the result-selecting lambda of `Join` and `GroupJoin`. For example: + +```c# +.Join(_myDbContext.Items, + stat => stat.ItemId, + item => item.Id, + (stat, item) => new { ItemName = convertToSomething(item.Name), + ItemCount = stat.Count.ToString() }) +``` + +- Method calls are not supported inside the key-selecting lambdas of `Join` and `GroupJoin`. +- Custom equality comparers are not supported. The `comparer` argument in `Join` and `GroupJoin` methods will be ignored if the argument has been passed. +- More than one `from` directly in one query is not supported, except when the `DefaultIfEmpty` method is used to perform left outer join. Each subsequent `from` is considered to be a separate query. + +```c# +var firstQuery = from stat in _myDbContext.Statements + where stat.Count > 2 + select new { stat.Count }; + +var secondQuery = from item in _myDbContext.Items + where item.Price > 6 + select new { item.Name }; + +var finalQuery = from first in firstQuery + from second in secondQuery + select new { first.Count, second.Name }; + +// 1. firstQuery will be executed against the cluster. +// 2. secondQuery will be executed against the cluster for each object (row) from 1. +// 3. finalQuery will be executed locally with the results from 1 and 2. +var result = finalQuery.ToArray(); +``` + +- Method calls are not supported inside `OrderBy`/`OrderByDescending` or `ThenBy`/`ThenByDescending`. +- Only overloads of `Contains`, `StartsWith`, and `EndsWith` methods that have a single string argument are supported inside `Where` and `First`/`FirstOrDefault`. + +## Modify data in a cluster by using `ScalarDbContext` + +The properties of the class inherited from `ScalarDbContext` can be used to modify data. + +### Add a new object by using the `AddAsync` method + +```c# +var statement = new Statement + { + OrderId = "2", + ItemId = 4, + Count = 8 + }; +await _myDbContext.Statements.AddAsync(statement); +``` + +### Update an object by using the `UpdateAsync` method + +```c# +var statement = _myDbContext.Statements.First(stat => stat.Id == 1); + +// ... + +statement.Count = 10; +await _myDbContext.Statements.UpdateAsync(statement); +``` + +### Remove an object by using the `RemoveAsync` method + +```c# +var statement = _myDbContext.Statements.First(stat => stat.Id == 1); + +// ... + +await _myDbContext.Statements.RemoveAsync(statement); +``` + +## Manage transactions + +LINQ queries and `AddAsync`, `UpdateAsync`, and `RemoveAsync` methods can be executed without an explicitly started transaction. However, to execute multiple queries and methods as part of a single transaction, the transaction must be explicitly started and committed. `ScalarDbContext` supports both ordinary transactions and transactions with the two-phase commit interface in ScalarDB. + +### Begin a new transaction + +```c# +await _myDbContext.BeginTransactionAsync(); +``` + +### Begin a new transaction with the two-phase commit interface + +```c# +using ScalarDB.Net.Client.Core; + +// ... + +await _myDbContext.BeginTransactionAsync(TransactionType.TwoPhaseCommit); +``` + +### Get the ID of a currently active transaction + +```c# +var transactionId = _myDbContext.CurrentTransactionId; +``` + +### Get the type of a currently active transaction + +```c# +var transactionType = _myDbContext.CurrentTransactionType; +``` + +### Join an existing transaction with the two-phase commit interface + +```c# +using ScalarDB.Net.Client.Core; + +// ... + +await _myDbContext.JoinTransactionAsync(transactionId, TransactionType.TwoPhaseCommit); +``` + +### Resume an existing transaction + +```c# +await _myDbContext.ResumeTransaction(transactionId); +``` + +### Resume an existing transaction with the two-phase commit interface + +```c# +await _myDbContext.ResumeTransaction(transactionId, TransactionType.TwoPhaseCommit); +``` + +{% capture notice--info %} +**Note** + +The `ResumeTransaction` method doesn't have an asynchronous version because it only initializes the transaction data in the `ScalarDbContext` inheriting object without querying the cluster. Because of this, resuming a transaction by using the wrong ID is possible. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +### Commit a transaction (ordinary or two-phase commit) + +```c# +await _myDbContext.CommitTransactionAsync(); +``` + +### Roll back a transaction (ordinary or two-phase commit) + +```c# +await _myDbContext.RollbackTransactionAsync(); +``` + +### Prepare a transaction with the two-phase commit interface for the commit + +```c# +await _myDbContext.PrepareTransactionAsync(); +``` + +### Validate a transaction with the two-phase commit interface before the commit + +```c# +await _myDbContext.ValidateTransactionAsync(); +``` diff --git a/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-scalardb-tables-as-csharp-classes.md b/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-scalardb-tables-as-csharp-classes.md new file mode 100644 index 00000000..62c6c376 --- /dev/null +++ b/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-scalardb-tables-as-csharp-classes.md @@ -0,0 +1,184 @@ +# Getting Started with Tables as C# Classes in the ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK helps you write code to access a cluster by abstracting ScalarDB tables as C# objects. After defining a class that represents a table in the cluster, you can ensure that a column name or its type won't be mixed up when querying the cluster. In addition, if a table's structure changes, you can apply the changes to the code by using the refactoring feature in your IDE. + +{% capture notice--info %} +**Note** + +Although we recommend using asynchronous methods, as in the following examples, you can use synchronous methods instead. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +## Create classes for all ScalarDB tables + +To work with ScalarDB tables as C# objects, you must create a class for each table that you want to use. For example: + +```c# +using System.ComponentModel.DataAnnotations.Schema; +using ScalarDB.Net.Client.DataAnnotations; + +// ... + +[Table("ns.statements")] +public class Statement +{ + [Column("order_id", Order = 0), PartitionKey] + public string OrderId { get; set; } = String.Empty; + + [Column("item_id", Order = 1), ClusteringKey] + public int ItemId { get; set; } + + [Column("count", Order = 2)] + public int Count { get; set; } +} +``` + +## Execute CRUD operations + +After creating a class for each table, you can use the classes as objects by using the generic `GetAsync`, `ScanAsync`, `InsertAsync`, `UpdateAsync`, `DeleteAsync`, `PutAsync`, or `MutateAsync` method of `IDistributedTransaction` (or more specifically, of `ITransactionCrudOperable`). + +To use these generic methods, add the following namespace to the `using` section: + +```c# +using ScalarDB.Net.Client.Extensions; +``` + +### Get one object by using the `GetAsync` method + +```c# +var keys = new Dictionary + { + { nameof(Statement.OrderId), "1" } + }; +var statement = await transaction.GetAsync(keys); + +Console.WriteLine($"ItemId: {statement.ItemId}, Count: {statement.Count}"); +``` + +### Get multiple objects by using the `ScanAsync` method + +```c# +var startKeys = new Dictionary + { + { nameof(Statement.OrderId), "1" }, + { nameof(Statement.ItemId), 3 } + }; +var endKeys = new Dictionary + { + { nameof(Statement.ItemId), 6} + }; +var statements = await transaction.ScanAsync(startKeys, endKeys); + +foreach (var s in statements) + Console.WriteLine($"ItemId: {s.ItemId}, Count: {s.Count}"); +``` + +### Insert a new object by using the `InsertAsync` method + +```c# +var statement = new Statement + { + OrderId = "2", + ItemId = 4, + Count = 8 + }; +await transaction.InsertAsync(statement); +``` + +### Update an object by using the `UpdateAsync` method + +```c# +// ... +statement.ItemId = 4; +statement.Count = 8; + +await transaction.UpdateAsync(statement); +``` + +### Delete an object by using the `DeleteAsync` method + +```c# +// ... +await transaction.DeleteAsync(statement); +``` + +### Upsert an object by using the `PutAsync` method + +```c# +var statement = new Statement + { + OrderId = "2", + ItemId = 4, + Count = 8 + }; +await transaction.PutAsync(statement); +``` + +### Put and delete multiple objects at once by using the `MutateAsync` method + +```c# +var statement = new Statement + { + OrderId = "2", + ItemId = 4, + Count = 16 + }; + +// ... + +await client.MutateAsync(objectsToPut: new[] { statement }, + objectsToDelete: new[] { statement2 }); +``` + +{% capture notice--info %} +**Note** + +To modify objects by using the `UpdateAsync`, `DeleteAsync`, `PutAsync`, or `MutateAsync` method, the objects must be retrieved first by using the `GetAsync` or `ScanAsync` method. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +## Use the Administrative API + +C# objects also can be used with the Administrative API. To use generic Administrative API methods, add the following namespace to the `using` section: + +```c# +using ScalarDB.Net.Client.Extensions; +``` + +### Create a new namespace + +```c# +await admin.CreateNamespaceAsync(); +``` + +### Drop an existing namespace + +```c# +await admin.DropNamespaceAsync(); +``` + +### Check if a namespace exists + +```c# +var namespaceExists = await admin.IsNamespacePresentAsync(); +``` + +### Create a new table + +```c# +await admin.CreateTableAsync(); +``` + +### Drop an existing table + +```c# +await admin.DropTableAsync(); +``` + +### Check if a table exists + +```c# +var tableExists = await admin.IsTablePresentAsync(); +``` diff --git a/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-two-phase-commit-transactions.md b/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-two-phase-commit-transactions.md new file mode 100644 index 00000000..1ab0e22d --- /dev/null +++ b/docs/3.11/scalardb-cluster-dotnet-client-sdk/getting-started-with-two-phase-commit-transactions.md @@ -0,0 +1,120 @@ +# Getting Started with Distributed Transactions with a Two-Phase Commit Interface in the ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK supports transactions with the two-phase commit interface in ScalarDB. The SDK includes transaction and manager abstractions for enhanced communication within a cluster. + +{% capture notice--info %} +**Note** + +Although we recommend using asynchronous methods as in the following examples, you can use synchronous methods instead. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +## About transactions with the two-phase commit interface + +By using the SDK, you can execute transactions with the two-phase commit interface that span multiple applications. For example, if you have multiple microservices, you can create a transaction manager in each of them and execute a transaction that spans those microservices. + +In transactions with the two-phase commit interface, there are two roles—coordinator and a participant—that collaboratively execute a single transaction. + +The coordinator process first begins a transaction and sends the ID of the transaction to all the participants, and the participant processes join the transaction. After executing CRUD or SQL operations, the coordinator process and the participant processes commit the transaction by using the two-phase interface. + +## Get a transaction manager (for coordinator and participants) + +First, you need to get a transaction manager for distributed transactions with the two-phase commit interface. To get the transaction manager, you can use `TransactionFactory` as follows, replacing `` with the URL of your cluster: + +```c# +var scalarDbOptions = new ScalarDbOptions + { + Address = "http://:60053" + HopLimit = 10 + }; +var factory = TransactionFactory.Create(scalarDbOptions); + +using var manager = factory.GetTwoPhaseCommitTransactionManager(); +``` + +Alternatively, you can use SQL instead of CRUD operations for transactions with the two-phase commit interface by specifying the following transaction manager: + +```c# +using var manager = factory.GetSqlTwoPhaseCommitTransactionManager(); +``` + +## Begin a transaction (for coordinator) + +You can begin a transaction with the two-phase commit interface in the coordinator as follows: + +```c# +var transaction = await manager.BeginAsync(); +``` + +The ID of the started transaction can be obtained with the following code: + +```c# +var transactionId = transaction.Id; +``` + +## Join a transaction (for participants) + +You can join a transaction with the two-phase commit interface in a participant as follows: + +```c# +var transaction = await manager.JoinAsync(transactionId); +``` + +## Resume a transaction (for coordinator and participants) + +Usually, a transaction with the two-phase commit interface involves multiple request and response exchanges. In scenarios where you need to work with a transaction that has been begun or joined in the previous request, you can resume such transaction as follows: + +```c# +var transaction = manager.Resume(transactionId); +``` + +{% capture notice--info %} +**Note** + +The `Resume` method doesn't have an asynchronous version because it only creates a transaction object. Because of this, resuming a transaction by using the wrong ID is possible. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +## Roll back a transaction + +If a transaction fails to commit, you can roll back the transaction as follows: + +```c# +await transaction.RollbackAsync(); +``` + +## Commit a transaction (for coordinator and participants) + +After completing CRUD or SQL operations, you must commit the transaction. However, for transactions with the two-phase commit interface, you must prepare the transaction in the coordinator and all the participants first. + +```c# +await transaction.PrepareAsync(); +``` + +Next, depending on the concurrency control protocol, you may need to validate the transaction in the coordinator and all the participants as follows: + +```c# +await transaction.ValidateAsync(); +``` + +Finally, you can commit the transaction in the coordinator and all the participants as follows: + +```c# +await transaction.CommitAsync(); +``` + +If the coordinator or any of the participants failed to prepare or validate the transaction, you will need to call `RollbackAsync` in the coordinator and all the participants. + +In addition, if the coordinator and all the participants failed to commit the transaction, you will need to call `RollbackAsync` in the coordinator and all the participants. + +However, if the coordinator or only some of the participants failed to commit the transaction, the transaction will be regarded as committed as long as the coordinator or any one of the participants has succeeded in committing the transaction. + +## Execute CRUD operations + +The two-phase commit interface of the transaction has the same methods for CRUD operations as ordinary transactions. For details, see [Execute CRUD operations](getting-started-with-distributed-transactions.md#execute-crud-operations). + +## Execute SQL statements + +The two-phase commit interface of the SQL transaction has the same methods for executing SQL queries as ordinary SQL transactions. For details, see [Execute SQL queries](getting-started-with-distributed-sql-transactions.md#execute-sql-queries). diff --git a/docs/3.11/scalardb-cluster-dotnet-client-sdk/index.md b/docs/3.11/scalardb-cluster-dotnet-client-sdk/index.md new file mode 100644 index 00000000..c6959684 --- /dev/null +++ b/docs/3.11/scalardb-cluster-dotnet-client-sdk/index.md @@ -0,0 +1,12 @@ +# Getting Started with the ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK is a .NET Standard 2.0 library that can be used with various .NET versions. For details about .NET Standard and its versions, see [.NET Standard](https://dotnet.microsoft.com/en-us/platform/dotnet-standard). + +* [Getting Started with Distributed Transactions](getting-started-with-distributed-transactions.md) +* [Getting Started with Distributed SQL Transactions](getting-started-with-distributed-sql-transactions.md) +* [Getting Started with the Administrative API](getting-started-with-admin-api.md) +* [Getting Started with ScalarDB Tables as C# Classes](getting-started-with-scalardb-tables-as-csharp-classes.md) +* [Getting Started with ASP.NET Core and Dependency Injection](getting-started-with-aspnet-and-di.md) +* [Getting Started with LINQ](getting-started-with-linq.md) +* [Getting Started with Distributed Transactions with a Two-Phase Commit Interface](getting-started-with-two-phase-commit-transactions.md) +* [Getting Started with ScalarDB Auth](getting-started-with-auth.md) diff --git a/docs/3.11/scalardb-cluster/getting-started-with-scalardb-cluster-overview.md b/docs/3.11/scalardb-cluster/getting-started-with-scalardb-cluster-overview.md index 4ce7dda4..3245b21e 100644 --- a/docs/3.11/scalardb-cluster/getting-started-with-scalardb-cluster-overview.md +++ b/docs/3.11/scalardb-cluster/getting-started-with-scalardb-cluster-overview.md @@ -1,6 +1,6 @@ # Getting Started with ScalarDB Cluster -The following is tutorials for getting started with using ScalarDB Cluster: +The following are tutorials for getting started with using ScalarDB Cluster: - [Getting Started with ScalarDB Cluster](getting-started-with-scalardb-cluster.md) - [Getting Started with ScalarDB Cluster GraphQL](getting-started-with-scalardb-cluster-graphql.md) diff --git a/docs/3.9/scalardb-cluster/getting-started-with-scalardb-cluster-overview.md b/docs/3.9/scalardb-cluster/getting-started-with-scalardb-cluster-overview.md index 4ce7dda4..3245b21e 100644 --- a/docs/3.9/scalardb-cluster/getting-started-with-scalardb-cluster-overview.md +++ b/docs/3.9/scalardb-cluster/getting-started-with-scalardb-cluster-overview.md @@ -1,6 +1,6 @@ # Getting Started with ScalarDB Cluster -The following is tutorials for getting started with using ScalarDB Cluster: +The following are tutorials for getting started with using ScalarDB Cluster: - [Getting Started with ScalarDB Cluster](getting-started-with-scalardb-cluster.md) - [Getting Started with ScalarDB Cluster GraphQL](getting-started-with-scalardb-cluster-graphql.md) diff --git a/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-admin-api.md b/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-admin-api.md new file mode 100644 index 00000000..72cc2cf0 --- /dev/null +++ b/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-admin-api.md @@ -0,0 +1,104 @@ +# Getting Started with the Administrative API in the ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK supports the Administrative API of ScalarDB Cluster. By using this API, you can manage ScalarDB Cluster from .NET applications. + +{% capture notice--info %} +**Note** + +Although we recommend using asynchronous methods as in the following examples, you can use synchronous methods instead. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +## Get a transaction manager + +First, you need to get an object for interacting with the Administrative API. To get the object, you can use `TransactionFactory` as follows, replacing `` with the URL of your cluster: + +```c# +var scalarDbOptions = new ScalarDbOptions + { + Address = "http://:60053" + HopLimit = 10 + }; +var factory = TransactionFactory.Create(scalarDbOptions); + +using var admin = factory.GetTransactionAdmin(); +``` + +## Manage ScalarDB Cluster + +The following operations can be performed by using the ScalarDB Cluster .NET Client SDK. + +### Create a new namespace + +```c# +await admin.CreateNamespaceAsync("ns", ifNotExists: true); +``` + +### Drop a namespace + +```c# +await admin.DropNamespaceAsync("ns", ifExists: true); +``` + +### Check if a namespace exists + +```c# +var namespaceExists = await admin.IsNamespacePresentAsync("ns"); +``` + +### Create a new table + +```c# +using Scalar.Db.Cluster.Rpc.V1; +// ... +using ScalarDB.Net.Client.Builders; + +// ... + +var tableMetadata = + new TableMetadataBuilder() + .AddPartitionKey("pk", DataType.Int) + .AddClusteringKey("ck", DataType.Double) + .AddSecondaryIndex("index", DataType.Float) + .AddColumn("ordinary", DataType.Text) + .Build(); + +await admin.CreateTableAsync("ns", "table_name", tableMetadata, ifNotExists: true); +``` + +### Drop a table + +```c# +await admin.DropTableAsync("ns", "table_name", ifExists: true); +``` + +### Checking if a table exists + +```c# +var tableExists = await admin.IsTablePresentAsync("ns", "table_name"); +``` + +### Get the names of existing tables + +```c# +var tablesList = await admin.GetTableNamesAsync("ns"); +``` + +### Create the Coordinator table + +```c# +await admin.CreateCoordinatorTablesAsync(); +``` + +### Drop the Coordinator table + +```c# +await admin.DropCoordinatorTablesAsync(); +``` + +### Check if the Coordinator table exist + +```c# +var exists = await admin.AreCoordinatorTablesPresentAsync(); +``` diff --git a/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-aspnet-and-di.md b/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-aspnet-and-di.md new file mode 100644 index 00000000..5b3b9711 --- /dev/null +++ b/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-aspnet-and-di.md @@ -0,0 +1,49 @@ +# Getting Started with ASP.NET Core and Dependency Injection in the ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK supports Dependency Injection in frameworks like ASP.NET Core. + +You can register the ScalarDB Cluster transaction managers as services in `IServiceCollection` as follows, replacing `` with the URL of your cluster: + +```c# +using ScalarDB.Net.Client.Extensions; + +//... + +var builder = WebApplication.CreateBuilder(args); + +//... + +builder.Services.AddScalarDbCluster(options => +{ + options.Address = "http:\\:60053"; + options.HopLimit = 10; + options.EnableCrud = true; // true by default + options.EnableSql = true; // false by default + options.EnableAdmin = true; // false by default +}); +``` + +After registering the transaction managers, they can be injected into the controller's constructor as follows: + +```c# +[ApiController] +public class OrderController: ControllerBase +{ + private readonly IDistributedTransactionManager _manager; + + public OrderController(IDistributedTransactionManager manager) + { + _manager = manager; + } +} +``` + +Although these examples are for WebApi projects, the examples will work in a similar way in GrpcService projects. + +### `AddScalarDbCluster`-specific options + +This section describes some specific options for `AddScalarDbCluster`: + +- **EnableCrud:** Enables the transaction managers for executing CRUD operations. `IDistributedTransactionManager` and `ITwoPhaseCommitTransactionManager` will become available for injection. +- **EnableSql:** Enables the transaction managers for executing SQL statements. `ISqlTransactionManager` and `ISqlTwoPhaseCommitTransactionManager` will become available for injection. +- **EnableAdmin:** Enables the administrative interface. `IDistributedTransactionAdmin` will become available for injection. diff --git a/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-auth.md b/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-auth.md new file mode 100644 index 00000000..6dd03ce0 --- /dev/null +++ b/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-auth.md @@ -0,0 +1,27 @@ +# Getting Started with ScalarDB Auth by Using ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK supports [ScalarDB Auth](https://github.com/scalar-labs/scalardb-cluster/blob/main/docs/scalardb-auth-with-sql.md), which allows you to authenticate and authorize your requests to ScalarDB Cluster. + +## Set credentials in `ScalarDbOptions` + +First, you need to get a transaction manager or transaction admin object with credentials by using `TransactionFactory` as follows, replacing the contents in the angle brackets as described. Also, be sure to replace `` with `GetTransactionManager()`, `GetTwoPhaseCommitTransactionManager()`, `GetSqlTransactionManager()`, or `GetSqlTwoPhaseCommitTransactionManager()`. + +```c# +var scalarDbOptions = new ScalarDbOptions + { + Address = "http://:60053" + HopLimit = 10, + AuthEnabled = true, + Username = "", + Password = "" + }; +var factory = TransactionFactory.Create(scalarDbOptions); + +// To get a transaction manager +using var manager = factory.(); + +// To get a transaction admin +using var admin = factory.GetTransactionAdmin(); +``` + +A transaction manager or transaction admin object created from `TransactionFactory` with the provided credentials will automatically log in to ScalarDB Cluster and can communicate with it. diff --git a/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-distributed-sql-transactions.md b/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-distributed-sql-transactions.md new file mode 100644 index 00000000..34434935 --- /dev/null +++ b/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-distributed-sql-transactions.md @@ -0,0 +1,112 @@ +# Getting Started with Distributed SQL Transactions in the ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK supports the distributed SQL transaction functionality of ScalarDB Cluster. The SDK includes transaction and manager abstractions for easier communication within a cluster. + +{% capture notice--info %} +**Note** + +Although we recommend using asynchronous methods, as in the following examples, you can use synchronous methods instead. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +For details about distributed non-SQL transactions, see [Getting Started with Distributed Transactions in the ScalarDB Cluster .NET Client SDK](getting-started-with-distributed-transactions.md). + +## Get a transaction manager + +First, you need to get a transaction manager object for distributed SQL transactions. To get the transaction manager object, you can use `TransactionFactory` as follows, replacing `` with the URL of your cluster: + +```c# +var scalarDbOptions = new ScalarDbOptions + { + Address = "http://:60053" + HopLimit = 10 + }; +var factory = TransactionFactory.Create(scalarDbOptions); + +using var manager = factory.GetSqlTransactionManager(); +``` + +## Execute SQL queries + +To execute a SQL statement, you need a `SqlStatement` object, which can be created by using a builder as follows: + +```c# +using ScalarDB.Net.Client.Builders; + +// ... + +var sqlStatement = + new SqlStatementBuilder() + .SetSql("SELECT * FROM order_service.statements WHERE item_id = :item_id") + .AddParam("item_id", 2) + .Build(); +``` + +A single SQL statement can be executed directly by using the transaction manager as follows: + +```c# +var resultSet = await manager.ExecuteAsync(sqlStatement); +``` + +The result from the `ExecuteAsync` method will contain records received from the cluster. The SDK has `GetValue`, `TryGetValue`, and `IsNull` extension methods to simplify using the records: + +```c# +using ScalarDB.Net.Client.Extensions; + +// ... + +foreach (var record in resultSet.Records) +{ + // Getting an integer value from the "item_id" column. + // If it fails, an exception will be thrown. + var itemId = record.GetValue("item_id"); + + // Trying to get a string value from the "order_id" column. + // If it fails, no exception will be thrown. + if (record.TryGetValue("order_id", out var orderId)) + Console.WriteLine($"order_id: {orderId}"); + + // Checking if the "count" column is null. + if (record.IsNull("count")) + Console.WriteLine("'count' is null"); +} +``` + +## Execute SQL queries in a transaction + +To execute multiple SQL statements as part of a single transaction, you need a transaction object. + +You can create a transaction object by using the transaction manager as follows: + +```c# +var transaction = await manager.BeginAsync(); +``` + +You can also resume a transaction that has already been started as follows: + +```c# +var transaction = manager.Resume(transactionIdString); +``` + +{% capture notice--info %} +**Note** + +The `Resume` method doesn't have an asynchronous version because it only creates a transaction object. Because of this, resuming a transaction by using the wrong ID is possible. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +The transaction has the same `ExecuteAsync` method as the transaction manager. That method can be used to execute SQL statements. + +When a transaction is ready to be committed, you can call the `CommitAsync` method of the transaction as follows: + +```c# +await transaction.CommitAsync(); +``` + +To roll back the transaction, you can use the `RollbackAsync` method: + +```c# +await transaction.RollbackAsync(); +``` diff --git a/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-distributed-transactions.md b/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-distributed-transactions.md new file mode 100644 index 00000000..148aa2bf --- /dev/null +++ b/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-distributed-transactions.md @@ -0,0 +1,181 @@ +# Getting Started with Distributed Transactions in the ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK supports the distributed transaction functionality of ScalarDB Cluster. The SDK includes transaction and manager abstractions for easier communication within a cluster. + +{% capture notice--info %} +**Note** + +Although we recommend using asynchronous methods as in the following examples, you can use synchronous versions instead. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +For details about distributed SQL transactions, see [Getting Started with Distributed SQL Transactions in the ScalarDB Cluster .NET Client SDK](getting-started-with-distributed-sql-transactions.md). + +## Get a transaction manager + +First, you need to get a transaction manager for distributed transactions. To get the transaction manager, you can use `TransactionFactory` as follows, replacing `` with the URL of your cluster: + +```c# +var scalarDbOptions = new ScalarDbOptions + { + Address = "http://:60053" + HopLimit = 10 + }; +var factory = TransactionFactory.Create(scalarDbOptions); + +using var manager = factory.GetTransactionManager(); +``` + +## Manage transactions + +To execute CRUD operations, a transaction is needed. You can begin a transaction by using the transaction manager as follows: + +```c# +var transaction = await manager.BeginAsync(); +``` + +You can also resume a transaction that is already being executed as follows: + +```c# +var transaction = manager.Resume(transactionIdString); +``` + +{% capture notice--info %} +**Note** + +The `Resume` method doesn't have an asynchronous version because it only creates a transaction object. Because of this, resuming a transaction by using the wrong ID is possible. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +When a transaction is ready to be committed, you can call the `CommitAsync` method of the transaction as follows: + +```c# +await transaction.CommitAsync(); +``` + +To roll back the transaction, you can use the `RollbackAsync` method: + +```c# +await transaction.RollbackAsync(); +``` + +## Execute CRUD operations + +A transaction has `GetAsync`, `ScanAsync`, `PutAsync`, `DeleteAsync`, and `MutateAsync` methods to execute CRUD commands against the cluster. As a parameter, these methods have a command object. A command object can be created by using the builders listed in this section. + +To use these builders add the following namespace to the `using` section: + +```c# +using ScalarDB.Net.Client.Builders; +``` + +{% capture notice--info %} +**Note** + +The cluster does not support parallel execution of commands inside one transaction, so make sure to use `await` for asynchronous methods. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +### `GetAsync` method example + +```c# +using GetTypeEnum = Scalar.Db.Cluster.Rpc.V1.Get.Types.GetType; + +// ... + +var get = + new GetBuilder() + .SetNamespaceName("ns") + .SetTableName("statements") + .SetGetType(GetTypeEnum.Get) + .AddPartitionKey("order_id", "1") + .AddClusteringKey("item_id", 2) + .SetProjections("item_id", "count") + .Build(); + +var getResult = await transaction.GetAsync(get); +``` + +### `ScanAsync` method example + +```c# +using static Scalar.Db.Cluster.Rpc.V1.Scan.Types; + +// .. + +var scan = + new ScanBuilder() + .SetNamespaceName("ns") + .SetTableName("statements") + .SetScanType(ScanType.Scan) + .AddPartitionKey("order_id", "1") + .AddStartClusteringKey("item_id", 2) + .SetStartInclusive(true) + .AddEndClusteringKey("item_id", 8) + .SetEndInclusive(true) + .SetProjections("item_id", "count") + .Build(); + +var scanResult = await transaction.ScanAsync(get); +``` + +### `PutAsync` method example + +```c# +var put = + new PutBuilder() + .SetNamespaceName("ns") + .SetTableName("statements") + .AddPartitionKey("order_id", "1") + .AddClusteringKey("item_id", 2) + .AddColumn("count", 11) + .Build(); + +await client.PutAsync(put); +``` + +### `DeleteAsync` method example + +```c# +var delete = + new DeleteBuilder() + .SetNamespaceName("ns") + .SetTableName("statements") + .AddPartitionKey("order_id", "1") + .AddClusteringKey("item_id", 2) + .Build(); + +await client.DeleteAsync(delete); +``` + +### `MutateAsync` method example: + +```c# +using Scalar.Db.Cluster.Rpc.V1; + +// ... + +var put = + new PutBuilder() + .SetNamespaceName("ns") + .SetTableName("statements") + .AddPartitionKey("order_id", "1") + .AddClusteringKey("item_id", 2) + .AddColumn("count", 11) + .Build(); + +var mutate = new Mutation { Put = put }; + +await client.MutateAsync(new[] { mutate }); +``` + +{% capture notice--info %} +**Note** + +To modify data by using the `PutAsync`, `DeleteAsync`, or `MutateAsync` method, the data must be retrieved first by using the `GetAsync` or `ScanAsync` method. +{% endcapture %} + +
{{ notice--info | markdownify }}
diff --git a/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-linq.md b/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-linq.md new file mode 100644 index 00000000..8003a449 --- /dev/null +++ b/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-linq.md @@ -0,0 +1,346 @@ +# Getting Started with LINQ in the ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK supports querying the cluster with LINQ and some EntityFramework-like functionality. However, this SDK doesn't support EntityFramework. + +{% capture notice--info %} +**Note** + +SQL support must be enabled on the cluster to use LINQ. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +## Set up classes + +After confirming that SQL support is enabled, create a C# class for each ScalarDB table that you want to use. For example: + +```c# +using System.ComponentModel.DataAnnotations.Schema; +using ScalarDB.Net.Client.DataAnnotations; + +// ... + +[Table("ns.statements")] +public class Statement +{ + [Column("statement_id", Order = 0), PartitionKey] + public int Id { get; set; } + + [Column("order_id", Order = 1), SecondaryIndex] + public string OrderId { get; set; } = String.Empty; + + [Column("item_id", Order = 2), SecondaryIndex] + public int ItemId { get; set; } + + [Column("count", Order = 3)] + public int Count { get; set; } +} + +[Table("order_service.items")] +public class Item +{ + [Column("item_id", Order = 0), PartitionKey] + public int Id { get; set; } + + [Column("name", Order = 1)] + public string Name { get; set; } = String.Empty; + + [Column("price", Order = 2)] + public int Price { get; set; } +} +``` + +If a partition key, clustering key, or secondary index consists of more than one column, the `Order` property of `ColumnAttribute` will decide the order inside the key or index. + +Create a context class that has properties for all the tables you want to use. For example: + +```c# + public class MyDbContext: ScalarDbContext + { + public ScalarDbSet Statements { get; set; } + public ScalarDbSet Items { get; set; } + } +``` + +After all the classes are created, you need to add the created context to the Dependency Injection. For example: + +```c# +using ScalarDB.Net.Client.Extensions; + +//... + +var builder = WebApplication.CreateBuilder(args); + +//... + +builder.Services.AddScalarDbContext(options => +{ + options.Address = "http:\\:60053"; + options.HopLimit = 10; +}); +``` + +The context can be injected into the controller's constructor as follows: + +```c# +[ApiController] +public class OrderController: ControllerBase +{ + private readonly MyDbContext _myDbContext; + + public OrderController(MyDbContext myDbContext) + { + _myDbContext = myDbContext; + } +} +``` + +## Use LINQ to query properties + +After receiving `MyDbContext` in your controller, you can query its properties by using LINQ. For example: + +### Use query syntax + +```c# +from stat in _myDbContext.Statements +join item in _myDbContext.Items on stat.ItemId equals item.Id +where stat.Count > 2 && item.Name.Contains("apple") +orderby stat.Count descending, stat.ItemId +select new { item.Name, stat.Count }; +``` + +### Use method syntax + +```c# +_myDbContext.Statements + .Where(stat => stat.OrderId == "1") + .Skip(1) + .Take(2); +``` + +### Use the `First` method to get one `Statement` by its partition key + +```c# +_myDbContext.Statements.First(stat => stat.OrderId == "1"); +``` + +### Use the `DefaultIfEmpty` method to perform left outer join + +```c# +from stat in _myDbContext.Statements +join item in _myDbContext.Items on stat.ItemId equals item.Id into items +from i in items.DefaultIfEmpty() +select new { ItemName = i != null ? i.Name : "" } +``` + +The following methods are supported: + +- `Select` +- `Where` +- `Join` +- `GroupJoin` +- `First`/`FirstOrDefault` +- `Skip` +- `Take` +- `OrderBy`/`OrderByDescending` +- `ThenBy`/`ThenByDescending` + +The following `String` methods are supported inside the predicates of `Where` and `First`/`FirstOrDefault` methods: + +- `Contains` +- `StartsWith` +- `EndsWith` + +Unsupported LINQ methods can be used after the supported methods. For example: + +```c# +_myDbContext.Statements + .Where(stat => stat.OrderId == "1") // Will be executed remotely on the cluster. + .Distinct() // Will be executed locally in the app. + .Where(stat => stat.ItemId < 5); // Will be executed locally. +``` + +{% capture notice--info %} +**Note** + +If `Skip` is specified before `Take` or `First`/`FirstOrDefault`, the number that is passed to `Skip` will be added to the `LIMIT` number in the SQL query. By itself, `Skip` won't change the resulting SQL query. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +## Limitations when using LINQ against `ScalarDbSet{T}` objects + +- All method calls are supported inside `Select`. For example: + +```c# +.Select(stat => convertToSomething(stat.ItemId)) +//... +.Select(stat => stat.ItemId * getSomeNumber()) +``` + +- Method calls, except for calls against the querying object, are also supported inside `Where` and `First`/`FirstOrDefault`. For example: + +```c# +.Where(stat => stat.ItemId == getItemId()) // is OK +//... +.Where(stat => stat.ItemId.ToString() == "1") // is not supported +``` + +- All method calls are supported inside the result-selecting lambda of `Join` and `GroupJoin`. For example: + +```c# +.Join(_myDbContext.Items, + stat => stat.ItemId, + item => item.Id, + (stat, item) => new { ItemName = convertToSomething(item.Name), + ItemCount = stat.Count.ToString() }) +``` + +- Method calls are not supported inside the key-selecting lambdas of `Join` and `GroupJoin`. +- Custom equality comparers are not supported. The `comparer` argument in `Join` and `GroupJoin` methods will be ignored if the argument has been passed. +- More than one `from` directly in one query is not supported, except when the `DefaultIfEmpty` method is used to perform left outer join. Each subsequent `from` is considered to be a separate query. + +```c# +var firstQuery = from stat in _myDbContext.Statements + where stat.Count > 2 + select new { stat.Count }; + +var secondQuery = from item in _myDbContext.Items + where item.Price > 6 + select new { item.Name }; + +var finalQuery = from first in firstQuery + from second in secondQuery + select new { first.Count, second.Name }; + +// 1. firstQuery will be executed against the cluster. +// 2. secondQuery will be executed against the cluster for each object (row) from 1. +// 3. finalQuery will be executed locally with the results from 1 and 2. +var result = finalQuery.ToArray(); +``` + +- Method calls are not supported inside `OrderBy`/`OrderByDescending` or `ThenBy`/`ThenByDescending`. +- Only overloads of `Contains`, `StartsWith`, and `EndsWith` methods that have a single string argument are supported inside `Where` and `First`/`FirstOrDefault`. + +## Modify data in a cluster by using `ScalarDbContext` + +The properties of the class inherited from `ScalarDbContext` can be used to modify data. + +### Add a new object by using the `AddAsync` method + +```c# +var statement = new Statement + { + OrderId = "2", + ItemId = 4, + Count = 8 + }; +await _myDbContext.Statements.AddAsync(statement); +``` + +### Update an object by using the `UpdateAsync` method + +```c# +var statement = _myDbContext.Statements.First(stat => stat.Id == 1); + +// ... + +statement.Count = 10; +await _myDbContext.Statements.UpdateAsync(statement); +``` + +### Remove an object by using the `RemoveAsync` method + +```c# +var statement = _myDbContext.Statements.First(stat => stat.Id == 1); + +// ... + +await _myDbContext.Statements.RemoveAsync(statement); +``` + +## Manage transactions + +LINQ queries and `AddAsync`, `UpdateAsync`, and `RemoveAsync` methods can be executed without an explicitly started transaction. However, to execute multiple queries and methods as part of a single transaction, the transaction must be explicitly started and committed. `ScalarDbContext` supports both ordinary transactions and transactions with the two-phase commit interface in ScalarDB. + +### Begin a new transaction + +```c# +await _myDbContext.BeginTransactionAsync(); +``` + +### Begin a new transaction with the two-phase commit interface + +```c# +using ScalarDB.Net.Client.Core; + +// ... + +await _myDbContext.BeginTransactionAsync(TransactionType.TwoPhaseCommit); +``` + +### Get the ID of a currently active transaction + +```c# +var transactionId = _myDbContext.CurrentTransactionId; +``` + +### Get the type of a currently active transaction + +```c# +var transactionType = _myDbContext.CurrentTransactionType; +``` + +### Join an existing transaction with the two-phase commit interface + +```c# +using ScalarDB.Net.Client.Core; + +// ... + +await _myDbContext.JoinTransactionAsync(transactionId, TransactionType.TwoPhaseCommit); +``` + +### Resume an existing transaction + +```c# +await _myDbContext.ResumeTransaction(transactionId); +``` + +### Resume an existing transaction with the two-phase commit interface + +```c# +await _myDbContext.ResumeTransaction(transactionId, TransactionType.TwoPhaseCommit); +``` + +{% capture notice--info %} +**Note** + +The `ResumeTransaction` method doesn't have an asynchronous version because it only initializes the transaction data in the `ScalarDbContext` inheriting object without querying the cluster. Because of this, resuming a transaction by using the wrong ID is possible. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +### Commit a transaction (ordinary or two-phase commit) + +```c# +await _myDbContext.CommitTransactionAsync(); +``` + +### Roll back a transaction (ordinary or two-phase commit) + +```c# +await _myDbContext.RollbackTransactionAsync(); +``` + +### Prepare a transaction with the two-phase commit interface for the commit + +```c# +await _myDbContext.PrepareTransactionAsync(); +``` + +### Validate a transaction with the two-phase commit interface before the commit + +```c# +await _myDbContext.ValidateTransactionAsync(); +``` diff --git a/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-scalardb-tables-as-csharp-classes.md b/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-scalardb-tables-as-csharp-classes.md new file mode 100644 index 00000000..62c6c376 --- /dev/null +++ b/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-scalardb-tables-as-csharp-classes.md @@ -0,0 +1,184 @@ +# Getting Started with Tables as C# Classes in the ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK helps you write code to access a cluster by abstracting ScalarDB tables as C# objects. After defining a class that represents a table in the cluster, you can ensure that a column name or its type won't be mixed up when querying the cluster. In addition, if a table's structure changes, you can apply the changes to the code by using the refactoring feature in your IDE. + +{% capture notice--info %} +**Note** + +Although we recommend using asynchronous methods, as in the following examples, you can use synchronous methods instead. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +## Create classes for all ScalarDB tables + +To work with ScalarDB tables as C# objects, you must create a class for each table that you want to use. For example: + +```c# +using System.ComponentModel.DataAnnotations.Schema; +using ScalarDB.Net.Client.DataAnnotations; + +// ... + +[Table("ns.statements")] +public class Statement +{ + [Column("order_id", Order = 0), PartitionKey] + public string OrderId { get; set; } = String.Empty; + + [Column("item_id", Order = 1), ClusteringKey] + public int ItemId { get; set; } + + [Column("count", Order = 2)] + public int Count { get; set; } +} +``` + +## Execute CRUD operations + +After creating a class for each table, you can use the classes as objects by using the generic `GetAsync`, `ScanAsync`, `InsertAsync`, `UpdateAsync`, `DeleteAsync`, `PutAsync`, or `MutateAsync` method of `IDistributedTransaction` (or more specifically, of `ITransactionCrudOperable`). + +To use these generic methods, add the following namespace to the `using` section: + +```c# +using ScalarDB.Net.Client.Extensions; +``` + +### Get one object by using the `GetAsync` method + +```c# +var keys = new Dictionary + { + { nameof(Statement.OrderId), "1" } + }; +var statement = await transaction.GetAsync(keys); + +Console.WriteLine($"ItemId: {statement.ItemId}, Count: {statement.Count}"); +``` + +### Get multiple objects by using the `ScanAsync` method + +```c# +var startKeys = new Dictionary + { + { nameof(Statement.OrderId), "1" }, + { nameof(Statement.ItemId), 3 } + }; +var endKeys = new Dictionary + { + { nameof(Statement.ItemId), 6} + }; +var statements = await transaction.ScanAsync(startKeys, endKeys); + +foreach (var s in statements) + Console.WriteLine($"ItemId: {s.ItemId}, Count: {s.Count}"); +``` + +### Insert a new object by using the `InsertAsync` method + +```c# +var statement = new Statement + { + OrderId = "2", + ItemId = 4, + Count = 8 + }; +await transaction.InsertAsync(statement); +``` + +### Update an object by using the `UpdateAsync` method + +```c# +// ... +statement.ItemId = 4; +statement.Count = 8; + +await transaction.UpdateAsync(statement); +``` + +### Delete an object by using the `DeleteAsync` method + +```c# +// ... +await transaction.DeleteAsync(statement); +``` + +### Upsert an object by using the `PutAsync` method + +```c# +var statement = new Statement + { + OrderId = "2", + ItemId = 4, + Count = 8 + }; +await transaction.PutAsync(statement); +``` + +### Put and delete multiple objects at once by using the `MutateAsync` method + +```c# +var statement = new Statement + { + OrderId = "2", + ItemId = 4, + Count = 16 + }; + +// ... + +await client.MutateAsync(objectsToPut: new[] { statement }, + objectsToDelete: new[] { statement2 }); +``` + +{% capture notice--info %} +**Note** + +To modify objects by using the `UpdateAsync`, `DeleteAsync`, `PutAsync`, or `MutateAsync` method, the objects must be retrieved first by using the `GetAsync` or `ScanAsync` method. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +## Use the Administrative API + +C# objects also can be used with the Administrative API. To use generic Administrative API methods, add the following namespace to the `using` section: + +```c# +using ScalarDB.Net.Client.Extensions; +``` + +### Create a new namespace + +```c# +await admin.CreateNamespaceAsync(); +``` + +### Drop an existing namespace + +```c# +await admin.DropNamespaceAsync(); +``` + +### Check if a namespace exists + +```c# +var namespaceExists = await admin.IsNamespacePresentAsync(); +``` + +### Create a new table + +```c# +await admin.CreateTableAsync(); +``` + +### Drop an existing table + +```c# +await admin.DropTableAsync(); +``` + +### Check if a table exists + +```c# +var tableExists = await admin.IsTablePresentAsync(); +``` diff --git a/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-two-phase-commit-transactions.md b/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-two-phase-commit-transactions.md new file mode 100644 index 00000000..1ab0e22d --- /dev/null +++ b/docs/latest/scalardb-cluster-dotnet-client-sdk/getting-started-with-two-phase-commit-transactions.md @@ -0,0 +1,120 @@ +# Getting Started with Distributed Transactions with a Two-Phase Commit Interface in the ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK supports transactions with the two-phase commit interface in ScalarDB. The SDK includes transaction and manager abstractions for enhanced communication within a cluster. + +{% capture notice--info %} +**Note** + +Although we recommend using asynchronous methods as in the following examples, you can use synchronous methods instead. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +## About transactions with the two-phase commit interface + +By using the SDK, you can execute transactions with the two-phase commit interface that span multiple applications. For example, if you have multiple microservices, you can create a transaction manager in each of them and execute a transaction that spans those microservices. + +In transactions with the two-phase commit interface, there are two roles—coordinator and a participant—that collaboratively execute a single transaction. + +The coordinator process first begins a transaction and sends the ID of the transaction to all the participants, and the participant processes join the transaction. After executing CRUD or SQL operations, the coordinator process and the participant processes commit the transaction by using the two-phase interface. + +## Get a transaction manager (for coordinator and participants) + +First, you need to get a transaction manager for distributed transactions with the two-phase commit interface. To get the transaction manager, you can use `TransactionFactory` as follows, replacing `` with the URL of your cluster: + +```c# +var scalarDbOptions = new ScalarDbOptions + { + Address = "http://:60053" + HopLimit = 10 + }; +var factory = TransactionFactory.Create(scalarDbOptions); + +using var manager = factory.GetTwoPhaseCommitTransactionManager(); +``` + +Alternatively, you can use SQL instead of CRUD operations for transactions with the two-phase commit interface by specifying the following transaction manager: + +```c# +using var manager = factory.GetSqlTwoPhaseCommitTransactionManager(); +``` + +## Begin a transaction (for coordinator) + +You can begin a transaction with the two-phase commit interface in the coordinator as follows: + +```c# +var transaction = await manager.BeginAsync(); +``` + +The ID of the started transaction can be obtained with the following code: + +```c# +var transactionId = transaction.Id; +``` + +## Join a transaction (for participants) + +You can join a transaction with the two-phase commit interface in a participant as follows: + +```c# +var transaction = await manager.JoinAsync(transactionId); +``` + +## Resume a transaction (for coordinator and participants) + +Usually, a transaction with the two-phase commit interface involves multiple request and response exchanges. In scenarios where you need to work with a transaction that has been begun or joined in the previous request, you can resume such transaction as follows: + +```c# +var transaction = manager.Resume(transactionId); +``` + +{% capture notice--info %} +**Note** + +The `Resume` method doesn't have an asynchronous version because it only creates a transaction object. Because of this, resuming a transaction by using the wrong ID is possible. +{% endcapture %} + +
{{ notice--info | markdownify }}
+ +## Roll back a transaction + +If a transaction fails to commit, you can roll back the transaction as follows: + +```c# +await transaction.RollbackAsync(); +``` + +## Commit a transaction (for coordinator and participants) + +After completing CRUD or SQL operations, you must commit the transaction. However, for transactions with the two-phase commit interface, you must prepare the transaction in the coordinator and all the participants first. + +```c# +await transaction.PrepareAsync(); +``` + +Next, depending on the concurrency control protocol, you may need to validate the transaction in the coordinator and all the participants as follows: + +```c# +await transaction.ValidateAsync(); +``` + +Finally, you can commit the transaction in the coordinator and all the participants as follows: + +```c# +await transaction.CommitAsync(); +``` + +If the coordinator or any of the participants failed to prepare or validate the transaction, you will need to call `RollbackAsync` in the coordinator and all the participants. + +In addition, if the coordinator and all the participants failed to commit the transaction, you will need to call `RollbackAsync` in the coordinator and all the participants. + +However, if the coordinator or only some of the participants failed to commit the transaction, the transaction will be regarded as committed as long as the coordinator or any one of the participants has succeeded in committing the transaction. + +## Execute CRUD operations + +The two-phase commit interface of the transaction has the same methods for CRUD operations as ordinary transactions. For details, see [Execute CRUD operations](getting-started-with-distributed-transactions.md#execute-crud-operations). + +## Execute SQL statements + +The two-phase commit interface of the SQL transaction has the same methods for executing SQL queries as ordinary SQL transactions. For details, see [Execute SQL queries](getting-started-with-distributed-sql-transactions.md#execute-sql-queries). diff --git a/docs/latest/scalardb-cluster-dotnet-client-sdk/index.md b/docs/latest/scalardb-cluster-dotnet-client-sdk/index.md new file mode 100644 index 00000000..c6959684 --- /dev/null +++ b/docs/latest/scalardb-cluster-dotnet-client-sdk/index.md @@ -0,0 +1,12 @@ +# Getting Started with the ScalarDB Cluster .NET Client SDK + +The ScalarDB Cluster .NET Client SDK is a .NET Standard 2.0 library that can be used with various .NET versions. For details about .NET Standard and its versions, see [.NET Standard](https://dotnet.microsoft.com/en-us/platform/dotnet-standard). + +* [Getting Started with Distributed Transactions](getting-started-with-distributed-transactions.md) +* [Getting Started with Distributed SQL Transactions](getting-started-with-distributed-sql-transactions.md) +* [Getting Started with the Administrative API](getting-started-with-admin-api.md) +* [Getting Started with ScalarDB Tables as C# Classes](getting-started-with-scalardb-tables-as-csharp-classes.md) +* [Getting Started with ASP.NET Core and Dependency Injection](getting-started-with-aspnet-and-di.md) +* [Getting Started with LINQ](getting-started-with-linq.md) +* [Getting Started with Distributed Transactions with a Two-Phase Commit Interface](getting-started-with-two-phase-commit-transactions.md) +* [Getting Started with ScalarDB Auth](getting-started-with-auth.md) diff --git a/docs/latest/scalardb-cluster/getting-started-with-scalardb-cluster-overview.md b/docs/latest/scalardb-cluster/getting-started-with-scalardb-cluster-overview.md index 4ce7dda4..3245b21e 100644 --- a/docs/latest/scalardb-cluster/getting-started-with-scalardb-cluster-overview.md +++ b/docs/latest/scalardb-cluster/getting-started-with-scalardb-cluster-overview.md @@ -1,6 +1,6 @@ # Getting Started with ScalarDB Cluster -The following is tutorials for getting started with using ScalarDB Cluster: +The following are tutorials for getting started with using ScalarDB Cluster: - [Getting Started with ScalarDB Cluster](getting-started-with-scalardb-cluster.md) - [Getting Started with ScalarDB Cluster GraphQL](getting-started-with-scalardb-cluster-graphql.md)