本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。
適用於 .NET 的 Amazon QLDB 驅動程式 – Cookbook 參考
此參考指南顯示適用於 .NET 的 Amazon QLDB 驅動程式的常見使用案例。它提供 C# 程式碼範例,示範如何使用驅動程式執行基本的建立、讀取、更新和刪除 (CRUD) 操作。它也包含處理 Amazon Ion 資料的程式碼範例。此外,本指南重點介紹了建立交易等冪和實作唯一性限制的最佳實務。
匯入驅動程式
下列程式碼範例會匯入驅動程式。
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 = 123或 WHERE indexedField IN (456, 789)。如果沒有此索引查詢,QLDB 需要執行資料表掃描,這可能會導致交易逾時或樂觀並行控制 (OCC) 衝突。
如需 OCC 的詳細資訊,請參閱 Amazon QLDB 並行模型。
隱含建立的交易
Amazon.QLDB.Driver.IQldbDriver.Execute 方法接受 lambda 函數,該函數會接收 Amazon.QLDB.Driver.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 清單做為值。若要進一步了解如何使用不同的 Ion 類型,請參閱 在 Amazon QLDB 中使用 Amazon Ion 資料類型。
- 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陳述式插入多個文件,您可以將 Ion 類型清單的參數傳遞至陳述式,如下所示。
- 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"
// }
}
傳遞 Ion 清單時,您不會將變數預留位置 (?) 括在雙角度括號 ( ) <<...>> 中。在手動 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 欄位實作唯一性限制條件。若要執行此操作,您可以撰寫執行下列動作的交易:
-
宣告資料表沒有具有指定 的現有文件GovId。
-
如果聲明通過,請插入文件。
如果競爭交易同時通過聲明,則只有一個交易會成功遞交。另一個交易將失敗,但 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 Ion
有數種方式可以處理 QLDB 中的 Amazon Ion 資料。您可以使用 Ion 程式庫來建立和修改 Ion 值。或者,您可以使用 Ion 物件映射器,將 C# 純舊 CLR 物件 (POCO) 對應至 Ion 值。適用於 .NET 的 QLDB 驅動程式 1.3.0 版推出對 Ion 物件映射器的支援。
以下各節提供使用兩種技術處理 Ion 資料的程式碼範例。
匯入 Ion 模組
using Amazon.IonObjectMapper;
using Amazon.IonDotnet.Builders;
建立 Ion 類型
下列程式碼範例示範如何使用 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);
取得 Ion 二進位傾印
// 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()));
取得 Ion 文字傾印
// 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 的詳細資訊,請參閱 GitHub 上的 Amazon Ion 文件。如需在 QLDB 中使用 Ion 的更多程式碼範例,請參閱 在 Amazon QLDB 中使用 Amazon Ion 資料類型。