Amazon QLDB 驅動程序. NET— 食譜參考 - Amazon Quantum Ledger 資料庫 (Amazon QLDB)

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

Amazon QLDB 驅動程序. NET— 食譜參考

重要

支援結束通知:現有客戶將能夠使用 Amazon,QLDB直到 2025 年 7 月 31 日終止支援為止。有關更多詳細信息,請參閱將 Amazon QLDB 分類帳遷移到 Amazon Aurora 郵政. SQL

本參考指南顯示 Amazon QLDB 驅動程式的常見使用案例。 NET。它提供 C# 程式碼範例,示範如何使用驅動程式來執行基本的建立、讀取、更新和 delete (CRUD) 作業。它還包括用於處理 Amazon Ion 資料的程式碼範例。此外,本指南還重點介紹了使交易冪等和實施唯一性約束的最佳實踐。

注意

本主題提供預設使用 Ion 物件對應程式處理 Amazon Ion 資料的程式碼範例。QLDB在版本 1.3.0 中引入了離子對象映射器。 NET驅動程序。在適用的情況下,本主題也會提供使用標準 Ion 程式庫做為替代方案的程式碼範例。如需進一步了解,請參閱與 Amazon 離子工作

匯入驅動程式

下列程式碼範例會匯入驅動程式。

using Amazon.QLDB.Driver; using Amazon.QLDB.Driver.Generic; using Amazon.QLDB.Driver.Serialization;
using Amazon.QLDB.Driver; using Amazon.IonDotnet.Builders;

實例化驅動程序

下列程式碼範例會建立使用預設設定連線至指定分類帳名稱的驅動程式執行環境。

Async
IAsyncQldbDriver driver = AsyncQldbDriver.Builder() .WithLedger("vehicle-registration") // Add Serialization library .WithSerializer(new ObjectSerializer()) .Build();
Sync
IQldbDriver driver = QldbDriver.Builder() .WithLedger("vehicle-registration") // Add Serialization library .WithSerializer(new ObjectSerializer()) .Build();
Async
IAsyncQldbDriver driver = AsyncQldbDriver.Builder().WithLedger("vehicle-registration").Build();
Sync
IQldbDriver driver = QldbDriver.Builder().WithLedger("vehicle-registration").Build();

CRUD操作

QLDB在交易中執行建立、讀取、更新和刪除 (CRUD) 作業。

警告

作為最佳實踐,使您的寫入事務嚴格為冪等。

使交易冪等

我們建議您將寫入事務設為冪等,以避免在重試的情況下出現任何意外的副作用。如果事務可以運行多次並每次產生相同的結果,則事務是冪等的。

例如,假設將文件插入名為的資料表中的交易Person。事務應該首先檢查文檔是否已經存在於表中。如果沒有此檢查,表格最終可能會出現重複的文件。

假設成QLDB功地在服務器端提交事務,但客戶端在等待響應時超時。如果事務不是冪等的,則在重試的情況下,可以多次插入相同的文檔。

使用索引來避免全表掃描

我們也建議您在索引欄位或文件 ID 上使用相等運算子來執行具有述WHERE詞子句的陳述式;例如,WHERE indexedField = 123WHERE indexedField IN (456, 789)。如果沒有此索引查找,則QLDB需要進行表掃描,這可能會導致事務超時或樂觀的並發控制(OCC)衝突。

如需有關 OCC 的詳細資訊,請參閱 Amazon QLDB 并发模型

隱含建立的交易

Amazon。 QLDB. 驅動程式。 IQldbDriver.Execute 方法接受接收 Amazon 實例的 lambda 函數。 QLDB. 驅動程式。 TransactionExecutor,您可以使用它來執行陳述式。TransactionExecutor封裝隱含建立之交易的執行個體。

您可以使用交易執行程Execute式的方法,在 lambda 函數中執行陳述式。當 lambda 函數返回時,驅動程序隱式地提交交易。

下列各節說明如何執行基本CRUD作業、指定自訂重試邏輯,以及實作唯一性條件約束。

建立資料表

Async
IAsyncResult<Table> createResult = await driver.Execute(async txn => { IQuery<Table> query = txn.Query<Table>("CREATE TABLE Person"); return await txn.Execute(query); }); await foreach (var result in createResult) { Console.WriteLine("{ tableId: " + result.TableId + " }"); // The statement returns the created table ID: // { tableId: 4o5Uk09OcjC6PpJpLahceE } }
Sync
IResult<Table> createResult = driver.Execute( txn => { IQuery<Table> query = txn.Query<Table>("CREATE TABLE Person"); return txn.Execute(query); }); foreach (var result in createResult) { Console.WriteLine("{ tableId: " + result.TableId + " }"); // The statement returns the created table ID: // { tableId: 4o5Uk09OcjC6PpJpLahceE } }
Async
// The result from driver.Execute() is buffered into memory because once the // transaction is committed, streaming the result is no longer possible. IAsyncResult result = await driver.Execute(async txn => { return await txn.Execute("CREATE TABLE Person"); }); await foreach (IIonValue row in result) { Console.WriteLine(row.ToPrettyString()); // The statement returns the created table ID: // { // tableId: "4o5Uk09OcjC6PpJpLahceE" // } }
Sync
// The result from driver.Execute() is buffered into memory because once the // transaction is committed, streaming the result is no longer possible. IResult result = driver.Execute(txn => { return txn.Execute("CREATE TABLE Person"); }); foreach (IIonValue row in result) { Console.WriteLine(row.ToPrettyString()); // The statement returns the created table ID: // { // tableId: "4o5Uk09OcjC6PpJpLahceE" // } }

建立索引

Async
IAsyncResult<Table> createResult = await driver.Execute(async txn => { IQuery<Table> query = txn.Query<Table>("CREATE INDEX ON Person(firstName)"); return await txn.Execute(query); }); await foreach (var result in createResult) { Console.WriteLine("{ tableId: " + result.TableId + " }"); // The statement returns the updated table ID: // { tableId: 4o5Uk09OcjC6PpJpLahceE } }
Sync
IResult<Table> createResult = driver.Execute(txn => { IQuery<Table> query = txn.Query<Table>("CREATE INDEX ON Person(firstName)"); return txn.Execute(query); }); foreach (var result in createResult) { Console.WriteLine("{ tableId: " + result.TableId + " }"); // The statement returns the updated table ID: // { tableId: 4o5Uk09OcjC6PpJpLahceE } }
Async
IAsyncResult result = await driver.Execute(async txn => { return await txn.Execute("CREATE INDEX ON Person(GovId)"); }); await foreach (IIonValue row in result) { Console.WriteLine(row.ToPrettyString()); // The statement returns the updated table ID: // { // tableId: "4o5Uk09OcjC6PpJpLahceE" // } }
Sync
IResult result = driver.Execute(txn => { return txn.Execute("CREATE INDEX ON Person(GovId)"); }); foreach (IIonValue row in result) { Console.WriteLine(row.ToPrettyString()); // The statement returns the updated table ID: // { // tableId: "4o5Uk09OcjC6PpJpLahceE" // } }

閱讀文件

// Assumes that Person table has documents as follows: // { "GovId": "TOYENC486FH", "FirstName" : "Brent" } // Person class is defined as follows: // public class Person // { // public string GovId { get; set; } // public string FirstName { get; set; } // } IAsyncResult<Person> result = await driver.Execute(async txn => { return await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'")); }); await foreach (Person person in result) { Console.WriteLine(person.GovId); // Prints TOYENC486FH. Console.WriteLine(person.FirstName); // Prints Brent. }
注意

當您在沒有索引查閱的情況下執行查詢時,會叫用完整資料表掃描。在此範例中,我們建議在GovId欄位上建立索引以最佳化效能。如果沒有開啟索引GovId,查詢可能會有更多延遲,也可能導致OCC衝突例外狀況或交易逾時。

使用查詢參數

下列程式碼範例會使用 C# 型別查詢參數。

IAsyncResult<Person> result = await driver.Execute(async txn => { return await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE FirstName = ?", "Brent")); }); await foreach (Person person in result) { Console.WriteLine(person.GovId); // Prints TOYENC486FH. Console.WriteLine(person.FirstName); // Prints Brent. }

下列程式碼範例會使用多個 C# 型別查詢參數。

IAsyncResult<Person> result = await driver.Execute(async txn => { return await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE GovId = ? AND FirstName = ?", "TOYENC486FH", "Brent")); }); await foreach (Person person in result) { Console.WriteLine(person.GovId); // Prints TOYENC486FH. Console.WriteLine(person.FirstName); // Prints Brent. }

下列程式碼範例會使用 C# 型別查詢參數的陣列。

// Assumes that Person table has documents as follows: // { "GovId": "TOYENC486FH", "FirstName" : "Brent" } // { "GovId": "ROEE1C1AABH", "FirstName" : "Jim" } // { "GovId": "YH844DA7LDB", "FirstName" : "Mary" } string[] ids = { "TOYENC486FH", "ROEE1C1AABH", "YH844DA7LDB" }; IAsyncResult<Person> result = await driver.Execute(async txn => { return await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE GovId IN (?,?,?)", ids)); }); await foreach (Person person in result) { Console.WriteLine(person.FirstName); // Prints Brent on first iteration. // Prints Jim on second iteration. // Prints Mary on third iteration. }

下列程式碼範例會使用 C# 清單做為值。

// Assumes that Person table has document as follows: // { "GovId": "TOYENC486FH", // "FirstName" : "Brent", // "Vehicles": [ // { "Make": "Volkswagen", // "Model": "Golf"}, // { "Make": "Honda", // "Model": "Civic"} // ] // } // Person class is defined as follows: // public class Person // { // public string GovId { get; set; } // public string FirstName { get; set; } // public List<Vehicle> Vehicles { get; set; } // } // Vehicle class is defined as follows: // public class Vehicle // { // public string Make { get; set; } // public string Model { get; set; } // } List<Vehicle> vehicles = new List<Vehicle> { new Vehicle { Make = "Volkswagen", Model = "Golf" }, new Vehicle { Make = "Honda", Model = "Civic" } }; IAsyncResult<Person> result = await driver.Execute(async txn => { return await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE Vehicles = ?", vehicles)); }); await foreach (Person person in result) { Console.WriteLine("{"); Console.WriteLine($" GovId: {person.GovId},"); Console.WriteLine($" FirstName: {person.FirstName},"); Console.WriteLine(" Vehicles: ["); foreach (Vehicle vehicle in person.Vehicles) { Console.WriteLine(" {"); Console.WriteLine($" Make: {vehicle.Make},"); Console.WriteLine($" Model: {vehicle.Model},"); Console.WriteLine(" },"); } Console.WriteLine(" ]"); Console.WriteLine("}"); // Prints: // { // GovId: TOYENC486FH, // FirstName: Brent, // Vehicles: [ // { // Make: Volkswagen, // Model: Golf // }, // { // Make: Honda, // Model: Civic // }, // ] // } }
Async
// Assumes that Person table has documents as follows: // { "GovId": "TOYENC486FH", "FirstName" : "Brent" } IAsyncResult result = await driver.Execute(async txn => { return await txn.Execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'"); }); await foreach (IIonValue row in result) { Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH. Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent. }
Sync
// Assumes that Person table has documents as follows: // { "GovId": "TOYENC486FH", "FirstName" : "Brent" } IResult result = driver.Execute(txn => { return txn.Execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'"); }); foreach (IIonValue row in result) { Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH. Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent. }
注意

當您在沒有索引查閱的情況下執行查詢時,會叫用完整資料表掃描。在此範例中,我們建議在GovId欄位上建立索引以最佳化效能。如果沒有開啟索引GovId,查詢可能會有更多延遲,也可能導致OCC衝突例外狀況或交易逾時。

下列程式碼範例會使用 Ion 型別查詢參數。

Async
IValueFactory valueFactory = new ValueFactory(); IIonValue ionFirstName = valueFactory.NewString("Brent"); IAsyncResult result = await driver.Execute(async txn => { return await txn.Execute("SELECT * FROM Person WHERE FirstName = ?", ionFirstName); }); await foreach (IIonValue row in result) { Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH. Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent. }
Sync
IValueFactory valueFactory = new ValueFactory(); IIonValue ionFirstName = valueFactory.NewString("Brent"); IResult result = driver.Execute(txn => { return txn.Execute("SELECT * FROM Person WHERE FirstName = ?", ionFirstName); }); foreach (IIonValue row in result) { Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH. Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent. }

下列程式碼範例會使用多個查詢參數。

Async
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH"); IIonValue ionFirstName = valueFactory.NewString("Brent"); IAsyncResult result = await driver.Execute(async txn => { return await txn.Execute("SELECT * FROM Person WHERE GovId = ? AND FirstName = ?", ionGovId, ionFirstName); }); await foreach (IIonValue row in result) { Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH. Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent. }
Sync
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH"); IIonValue ionFirstName = valueFactory.NewString("Brent"); IResult result = driver.Execute(txn => { return txn.Execute("SELECT * FROM Person WHERE GovId = ? AND FirstName = ?", ionGovId, ionFirstName); }); foreach (IIonValue row in result) { Console.WriteLine(row.GetField("GovId").StringValue); // Prints TOYENC486FH. Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent. }

下列程式碼範例使用查詢參數清單。

Async
// Assumes that Person table has documents as follows: // { "GovId": "TOYENC486FH", "FirstName" : "Brent" } // { "GovId": "ROEE1C1AABH", "FirstName" : "Jim" } // { "GovId": "YH844DA7LDB", "FirstName" : "Mary" } IIonValue[] ionIds = { valueFactory.NewString("TOYENC486FH"), valueFactory.NewString("ROEE1C1AABH"), valueFactory.NewString("YH844DA7LDB") }; IAsyncResult result = await driver.Execute(async txn => { return await txn.Execute("SELECT * FROM Person WHERE GovId IN (?,?,?)", ionIds); }); await foreach (IIonValue row in result) { Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent on first iteration. // Prints Jim on second iteration. // Prints Mary on third iteration. }
Sync
// Assumes that Person table has documents as follows: // { "GovId": "TOYENC486FH", "FirstName" : "Brent" } // { "GovId": "ROEE1C1AABH", "FirstName" : "Jim" } // { "GovId": "YH844DA7LDB", "FirstName" : "Mary" } IIonValue[] ionIds = { valueFactory.NewString("TOYENC486FH"), valueFactory.NewString("ROEE1C1AABH"), valueFactory.NewString("YH844DA7LDB") }; IResult result = driver.Execute(txn => { return txn.Execute("SELECT * FROM Person WHERE GovId IN (?,?,?)", ionIds); }); foreach (IIonValue row in result) { Console.WriteLine(row.GetField("FirstName").StringValue); // Prints Brent on first iteration. // Prints Jim on second iteration. // Prints Mary on third iteration. }

下列程式碼範例會使用 Ion 清單做為值。若要進一步瞭解如何使用不同離子類型,請參閱使用 Amazon 離子數據類型在 Amazon QLDB

Async
// Assumes that Person table has document as follows: // { "GovId": "TOYENC486FH", // "FirstName" : "Brent", // "Vehicles": [ // { "Make": "Volkswagen", // "Model": "Golf"}, // { "Make": "Honda", // "Model": "Civic"} // ] // } IIonValue ionVehicle1 = valueFactory.NewEmptyStruct(); ionVehicle1.SetField("Make", valueFactory.NewString("Volkswagen")); ionVehicle1.SetField("Model", valueFactory.NewString("Golf")); IIonValue ionVehicle2 = valueFactory.NewEmptyStruct(); ionVehicle2.SetField("Make", valueFactory.NewString("Honda")); ionVehicle2.SetField("Model", valueFactory.NewString("Civic")); IIonValue ionVehicles = valueFactory.NewEmptyList(); ionVehicles.Add(ionVehicle1); ionVehicles.Add(ionVehicle2); IAsyncResult result = await driver.Execute(async txn => { return await txn.Execute("SELECT * FROM Person WHERE Vehicles = ?", ionVehicles); }); await foreach (IIonValue row in result) { Console.WriteLine(row.ToPrettyString()); // Prints: // { // GovId: "TOYENC486FN", // FirstName: "Brent", // Vehicles: [ // { // Make: "Volkswagen", // Model: "Golf" // }, // { // Make: "Honda", // Model: "Civic" // } // ] // } }
Sync
// Assumes that Person table has document as follows: // { "GovId": "TOYENC486FH", // "FirstName" : "Brent", // "Vehicles": [ // { "Make": "Volkswagen", // "Model": "Golf"}, // { "Make": "Honda", // "Model": "Civic"} // ] // } IIonValue ionVehicle1 = valueFactory.NewEmptyStruct(); ionVehicle1.SetField("Make", valueFactory.NewString("Volkswagen")); ionVehicle1.SetField("Model", valueFactory.NewString("Golf")); IIonValue ionVehicle2 = valueFactory.NewEmptyStruct(); ionVehicle2.SetField("Make", valueFactory.NewString("Honda")); ionVehicle2.SetField("Model", valueFactory.NewString("Civic")); IIonValue ionVehicles = valueFactory.NewEmptyList(); ionVehicles.Add(ionVehicle1); ionVehicles.Add(ionVehicle2); IResult result = driver.Execute(txn => { return txn.Execute("SELECT * FROM Person WHERE Vehicles = ?", ionVehicles); }); foreach (IIonValue row in result) { Console.WriteLine(row.ToPrettyString()); // Prints: // { // GovId: "TOYENC486FN", // FirstName: "Brent", // Vehicles: [ // { // Make: "Volkswagen", // Model: "Golf" // }, // { // Make: "Honda", // Model: "Civic" // } // ] // } }

插入文件

下列程式碼範例會插入 Ion 資料型別。

string govId = "TOYENC486FH"; Person person = new Person { GovId = "TOYENC486FH", FirstName = "Brent" }; await driver.Execute(async txn => { // Check if a document with GovId:TOYENC486FH exists // This is critical to make this transaction idempotent IAsyncResult<Person> result = await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE GovId = ?", govId)); // Check if there is a record in the cursor. int count = await result.CountAsync(); if (count > 0) { // Document already exists, no need to insert return; } // Insert the document. await txn.Execute(txn.Query<Document>("INSERT INTO Person ?", person)); });
Async
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH"); IIonValue ionPerson = valueFactory.NewEmptyStruct(); ionPerson.SetField("GovId", valueFactory.NewString("TOYENC486FH")); ionPerson.SetField("FirstName", valueFactory.NewString("Brent")); await driver.Execute(async txn => { // Check if a document with GovId:TOYENC486FH exists // This is critical to make this transaction idempotent IAsyncResult result = await txn.Execute("SELECT * FROM Person WHERE GovId = ?", ionGovId); // Check if there is a record in the cursor. int count = await result.CountAsync(); if (count > 0) { // Document already exists, no need to insert return; } // Insert the document. await txn.Execute("INSERT INTO Person ?", ionPerson); });
Sync
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH"); IIonValue ionPerson = valueFactory.NewEmptyStruct(); ionPerson.SetField("GovId", valueFactory.NewString("TOYENC486FH")); ionPerson.SetField("FirstName", valueFactory.NewString("Brent")); driver.Execute(txn => { // Check if a document with GovId:TOYENC486FH exists // This is critical to make this transaction idempotent IResult result = txn.Execute("SELECT * FROM Person WHERE GovId = ?", ionGovId); // Check if there is a record in the cursor. int count = result.Count(); if (count > 0) { // Document already exists, no need to insert return; } // Insert the document. txn.Execute("INSERT INTO Person ?", ionPerson); });

此事務將一個文檔插入到Person表中。在插入之前,它會先檢查文件是否已存在於表格中。這個檢查使事務本質上是冪等的。即使您多次運行此事務,也不會造成任何意外的副作用。

注意

在此範例中,我們建議在GovId欄位上建立索引以最佳化效能。如果沒有開啟索引GovId,陳述式可能會有更多延遲,也可能導致OCC衝突例外狀況或交易逾時。

在一個語句中插入多個文檔

若要使用單一INSERT陳述式插入多個文件,您可以將 C# List 參數傳遞至陳述式,如下所示。

Person person1 = new Person { FirstName = "Brent", GovId = "TOYENC486FH" }; Person person2 = new Person { FirstName = "Jim", GovId = "ROEE1C1AABH" }; List<Person> people = new List<Person>(); people.Add(person1); people.Add(person2); IAsyncResult<Document> result = await driver.Execute(async txn => { return await txn.Execute(txn.Query<Document>("INSERT INTO Person ?", people)); }); await foreach (Document row in result) { Console.WriteLine("{ documentId: " + row.DocumentId + " }"); // The statement returns the created documents' ID: // { documentId: 6BFt5eJQDFLBW2aR8LPw42 } // { documentId: K5Zrcb6N3gmIEHgGhwoyKF } }

要通過使用單個INSERT語句插入多個文檔,可以傳遞離子列表類型的參數到語句如下。

Async
IIonValue ionPerson1 = valueFactory.NewEmptyStruct(); ionPerson1.SetField("FirstName", valueFactory.NewString("Brent")); ionPerson1.SetField("GovId", valueFactory.NewString("TOYENC486FH")); IIonValue ionPerson2 = valueFactory.NewEmptyStruct(); ionPerson2.SetField("FirstName", valueFactory.NewString("Jim")); ionPerson2.SetField("GovId", valueFactory.NewString("ROEE1C1AABH")); IIonValue ionPeople = valueFactory.NewEmptyList(); ionPeople.Add(ionPerson1); ionPeople.Add(ionPerson2); IAsyncResult result = await driver.Execute(async txn => { return await txn.Execute("INSERT INTO Person ?", ionPeople); }); await foreach (IIonValue row in result) { Console.WriteLine(row.ToPrettyString()); // The statement returns the created documents' ID: // { // documentId: "6BFt5eJQDFLBW2aR8LPw42" // } // // { // documentId: "K5Zrcb6N3gmIEHgGhwoyKF" // } }
Sync
IIonValue ionPerson1 = valueFactory.NewEmptyStruct(); ionPerson1.SetField("FirstName", valueFactory.NewString("Brent")); ionPerson1.SetField("GovId", valueFactory.NewString("TOYENC486FH")); IIonValue ionPerson2 = valueFactory.NewEmptyStruct(); ionPerson2.SetField("FirstName", valueFactory.NewString("Jim")); ionPerson2.SetField("GovId", valueFactory.NewString("ROEE1C1AABH")); IIonValue ionPeople = valueFactory.NewEmptyList(); ionPeople.Add(ionPerson1); ionPeople.Add(ionPerson2); IResult result = driver.Execute(txn => { return txn.Execute("INSERT INTO Person ?", ionPeople); }); foreach (IIonValue row in result) { Console.WriteLine(row.ToPrettyString()); // The statement returns the created documents' ID: // { // documentId: "6BFt5eJQDFLBW2aR8LPw42" // } // // { // documentId: "K5Zrcb6N3gmIEHgGhwoyKF" // } }

傳遞離子清單時,您不會將變數預留位置 (?<<...>>) 括在雙尖括號 () 中。在手動 PartiQL 陳述式中,雙角括號表示稱為袋子的無序集合。

更新文件

string govId = "TOYENC486FH"; string firstName = "John"; IAsyncResult<Document> result = await driver.Execute(async txn => { return await txn.Execute(txn.Query<Document>("UPDATE Person SET FirstName = ? WHERE GovId = ?", firstName , govId)); }); await foreach (Document row in result) { Console.WriteLine("{ documentId: " + row.DocumentId + " }"); // The statement returns the updated document ID: // { documentId: Djg30Zoltqy5M4BFsA2jSJ } }
Async
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH"); IIonValue ionFirstName = valueFactory.NewString("John"); IAsyncResult result = await driver.Execute(async txn => { return await txn.Execute("UPDATE Person SET FirstName = ? WHERE GovId = ?", ionFirstName , ionGovId); }); await foreach (IIonValue row in result) { Console.WriteLine(row.ToPrettyString()); // The statement returns the updated document ID: // { // documentId: "Djg30Zoltqy5M4BFsA2jSJ" // } }
Sync
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH"); IIonValue ionFirstName = valueFactory.NewString("John"); IResult result = driver.Execute(txn => { return txn.Execute("UPDATE Person SET FirstName = ? WHERE GovId = ?", ionFirstName , ionGovId); }); foreach (IIonValue row in result) { Console.WriteLine(row.ToPrettyString()); // The statement returns the updated document ID: // { // documentId: "Djg30Zoltqy5M4BFsA2jSJ" // } }
注意

在此範例中,我們建議在GovId欄位上建立索引以最佳化效能。如果沒有開啟索引GovId,陳述式可能會有更多延遲,也可能導致OCC衝突例外狀況或交易逾時。

刪除文件

string govId = "TOYENC486FH"; IAsyncResult<Document> result = await driver.Execute(async txn => { return await txn.Execute(txn.Query<Document>("DELETE FROM Person WHERE GovId = ?", govId)); }); await foreach (Document row in result) { Console.WriteLine("{ documentId: " + row.DocumentId + " }"); // The statement returns the updated document ID: // { documentId: Djg30Zoltqy5M4BFsA2jSJ } }
Async
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH"); IAsyncResult result = await driver.Execute(async txn => { return await txn.Execute("DELETE FROM Person WHERE GovId = ?", ionGovId); }); await foreach (IIonValue row in result) { Console.WriteLine(row.ToPrettyString()); // The statement returns the deleted document ID: // { // documentId: "Djg30Zoltqy5M4BFsA2jSJ" // } }
Sync
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH"); IResult result = driver.Execute(txn => { return txn.Execute("DELETE FROM Person WHERE GovId = ?", ionGovId); }); foreach (IIonValue row in result) { Console.WriteLine(row.ToPrettyString()); // The statement returns the deleted document ID: // { // documentId: "Djg30Zoltqy5M4BFsA2jSJ" // } }
注意

在此範例中,我們建議在GovId欄位上建立索引以最佳化效能。如果沒有開啟索引GovId,陳述式可能會有更多延遲,也可能導致OCC衝突例外狀況或交易逾時。

在交易中執行多個陳述式

// This code snippet is intentionally trivial. In reality you wouldn't do this because you'd // set your UPDATE to filter on vin and insured, and check if you updated something or not. public static async Task<bool> InsureVehicle(IAsyncQldbDriver driver, string vin) { return await driver.Execute(async txn => { // Check if the vehicle is insured. Amazon.QLDB.Driver.Generic.IAsyncResult<Vehicle> result = await txn.Execute( txn.Query<Vehicle>("SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", vin)); if (await result.CountAsync() > 0) { // If the vehicle is not insured, insure it. await txn.Execute( txn.Query<Document>("UPDATE Vehicles SET insured = TRUE WHERE vin = ?", vin)); return true; } return false; }); }
Async
// This code snippet is intentionally trivial. In reality you wouldn't do this because you'd // set your UPDATE to filter on vin and insured, and check if you updated something or not. public static async Task<bool> InsureVehicle(IAsyncQldbDriver driver, string vin) { ValueFactory valueFactory = new ValueFactory(); IIonValue ionVin = valueFactory.NewString(vin); return await driver.Execute(async txn => { // Check if the vehicle is insured. Amazon.QLDB.Driver.IAsyncResult result = await txn.Execute( "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", ionVin); if (await result.CountAsync() > 0) { // If the vehicle is not insured, insure it. await txn.Execute( "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", ionVin); return true; } return false; }); }

重試邏輯

如需驅動程式內建重試邏輯的詳細資訊,請參閱了解 Amazon 中的驅動程序的重試政策 QLDB

實施唯一性約束

QLDB不支持唯一索引,但您可以在應用程序中實現此行為。

假設您要在Person表中的GovId字段上實現唯一性約束。要做到這一點,你可以寫一個事務,執行以下操作:

  1. 斷言表沒有具有指定GovId的現有文檔。

  2. 插入文檔,如果斷言通過。

如果競爭交易同時通過斷言,則只有其中一個交易將成功提交。另一個交易會失敗,並出現OCC衝突例外狀況。

下列程式碼範例會示範如何實作此唯一性條件約束邏輯。

string govId = "TOYENC486FH"; Person person = new Person { GovId = "TOYENC486FH", FirstName = "Brent" }; await driver.Execute(async txn => { // Check if a document with GovId:TOYENC486FH exists // This is critical to make this transaction idempotent IAsyncResult<Person> result = await txn.Execute(txn.Query<Person>("SELECT * FROM Person WHERE GovId = ?", govId)); // Check if there is a record in the cursor. int count = await result.CountAsync(); if (count > 0) { // Document already exists, no need to insert return; } // Insert the document. await txn.Execute(txn.Query<Document>("INSERT INTO Person ?", person)); });
Async
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH"); IIonValue ionPerson = valueFactory.NewEmptyStruct(); ionPerson.SetField("GovId", valueFactory.NewString("TOYENC486FH")); ionPerson.SetField("FirstName", valueFactory.NewString("Brent")); await driver.Execute(async txn => { // Check if a document with GovId:TOYENC486FH exists // This is critical to make this transaction idempotent IAsyncResult result = await txn.Execute("SELECT * FROM Person WHERE GovId = ?", ionGovId); // Check if there is a record in the cursor. int count = await result.CountAsync(); if (count > 0) { // Document already exists, no need to insert return; } // Insert the document. await txn.Execute("INSERT INTO Person ?", ionPerson); });
Sync
IIonValue ionGovId = valueFactory.NewString("TOYENC486FH"); IIonValue ionPerson = valueFactory.NewEmptyStruct(); ionPerson.SetField("GovId", valueFactory.NewString("TOYENC486FH")); ionPerson.SetField("FirstName", valueFactory.NewString("Brent")); driver.Execute(txn => { // Check if a document with GovId:TOYENC486FH exists // This is critical to make this transaction idempotent IResult result = txn.Execute("SELECT * FROM Person WHERE GovId = ?", ionGovId); // Check if there is a record in the cursor. int count = result.Count(); if (count > 0) { // Document already exists, no need to insert return; } // Insert the document. txn.Execute("INSERT INTO Person ?", ionPerson); });
注意

在此範例中,我們建議在GovId欄位上建立索引以最佳化效能。如果沒有開啟索引GovId,陳述式可能會有更多延遲,也可能導致OCC衝突例外狀況或交易逾時。

與 Amazon 離子工作

有多種方法可以在中處理 Amazon 離子數據QLDB。您可以使用 I on 元件庫來建立和修改 Ion 值。或者,您可以使用 Ion 對象映射器將 C# 普通舊對CLR象(POCO)映射到 Ion 值。QLDB驅動程式的 1.3.0 版本。 NET引入了對 Ion 對象映射器的支持。

以下各節提供使用這兩種技術處理離子資料的程式碼範例。

匯入離子模組

using Amazon.IonObjectMapper;
using Amazon.IonDotnet.Builders;

建立離子類型

下列程式碼範例會示範如何使用 Ion 物件對應程式,從 C# 物件建立 Ion 值。

// Assumes that Person class is defined as follows: // public class Person // { // public string FirstName { get; set; } // public int Age { get; set; } // } // Initialize the Ion Object Mapper IonSerializer ionSerializer = new IonSerializer(); // The C# object to be serialized Person person = new Person { FirstName = "John", Age = 13 }; // Serialize the C# object into stream using the Ion Object Mapper Stream stream = ionSerializer.Serialize(person); // Load will take in stream and return a datagram; a top level container of Ion values. IIonValue ionDatagram = IonLoader.Default.Load(stream); // To get the Ion value within the datagram, we call GetElementAt(0). IIonValue ionPerson = ionDatagram.GetElementAt(0); Console.WriteLine(ionPerson.GetField("firstName").StringValue); Console.WriteLine(ionPerson.GetField("age").IntValue);

下列程式碼範例會示範使用 Ion 程式庫建立 Ion 值的兩種方式。

使用 ValueFactory

using Amazon.IonDotnet.Tree; using Amazon.IonDotnet.Tree.Impl; IValueFactory valueFactory = new ValueFactory(); IIonValue ionPerson = valueFactory.NewEmptyStruct(); ionPerson.SetField("firstName", valueFactory.NewString("John")); ionPerson.SetField("age", valueFactory.NewInt(13)); Console.WriteLine(ionPerson.GetField("firstName").StringValue); Console.WriteLine(ionPerson.GetField("age").IntValue);

使用 IonLoader

using Amazon.IonDotnet.Builders; using Amazon.IonDotnet.Tree; // Load will take in Ion text and return a datagram; a top level container of Ion values. IIonValue ionDatagram = IonLoader.Default.Load("{firstName: \"John\", age: 13}"); // To get the Ion value within the datagram, we call GetElementAt(0). IIonValue ionPerson = ionDatagram.GetElementAt(0); Console.WriteLine(ionPerson.GetField("firstName").StringValue); Console.WriteLine(ionPerson.GetField("age").IntValue);

獲取離子二進制轉儲

// Initialize the Ion Object Mapper with Ion binary serialization format IonSerializer ionSerializer = new IonSerializer(new IonSerializationOptions { Format = IonSerializationFormat.BINARY }); // The C# object to be serialized Person person = new Person { FirstName = "John", Age = 13 }; MemoryStream stream = (MemoryStream) ionSerializer.Serialize(person); Console.WriteLine(BitConverter.ToString(stream.ToArray()));
// ionObject is an Ion struct MemoryStream stream = new MemoryStream(); using (var writer = IonBinaryWriterBuilder.Build(stream)) { ionObject.WriteTo(writer); writer.Finish(); } Console.WriteLine(BitConverter.ToString(stream.ToArray()));

獲取離子文本轉儲

// Initialize the Ion Object Mapper IonSerializer ionSerializer = new IonSerializer(new IonSerializationOptions { Format = IonSerializationFormat.TEXT }); // The C# object to be serialized Person person = new Person { FirstName = "John", Age = 13 }; MemoryStream stream = (MemoryStream) ionSerializer.Serialize(person); Console.WriteLine(System.Text.Encoding.UTF8.GetString(stream.ToArray()));
// ionObject is an Ion struct StringWriter sw = new StringWriter(); using (var writer = IonTextWriterBuilder.Build(sw)) { ionObject.WriteTo(writer); writer.Finish(); } Console.WriteLine(sw.ToString());

如需使用 Ion 的詳細資訊,請參閱上的 Amazon Ion 文件 GitHub。如需中使用 Ion 的更多程式碼範例QLDB,請參閱使用 Amazon 離子數據類型在 Amazon QLDB