Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.
Amazon QLDB Driver pour. NET— Référence à un livre de recettes
Important
Avis de fin de support : les clients existants pourront utiliser Amazon QLDB jusqu'à la fin du support le 31 juillet 2025. Pour plus de détails, consultez Migrer un Amazon QLDB Ledger vers Amazon Aurora SQL Postgre
Ce guide de référence présente les cas d'utilisation courants du QLDB pilote Amazon pour. NET. Il fournit des exemples de code C# qui montrent comment utiliser le pilote pour exécuter des opérations de base de création, de lecture, de mise à jour et de suppression (CRUD). Il inclut également des exemples de code pour le traitement des données Amazon Ion. En outre, ce guide met en évidence les meilleures pratiques pour rendre les transactions idempotentes et mettre en œuvre des contraintes d'unicité.
Note
Cette rubrique fournit des exemples de code relatifs au traitement des données Amazon Ion à l'aide du mappeur d'objets Ion
Table des matières
- Importation du pilote
- Instanciation du pilote
- CRUDopérations
- Travailler avec Amazon Ion
Importation du pilote
L'exemple de code suivant importe le pilote.
using Amazon.QLDB.Driver; using Amazon.QLDB.Driver.Generic; using Amazon.QLDB.Driver.Serialization;
using Amazon.QLDB.Driver; using Amazon.IonDotnet.Builders;
Instanciation du pilote
L'exemple de code suivant crée une instance du pilote qui se connecte à un nom de registre spécifié à l'aide des paramètres par défaut.
CRUDopérations
QLDBexécute les opérations de création, de lecture, de mise à jour et de suppression (CRUD) dans le cadre d'une transaction.
Avertissement
La meilleure pratique consiste à rendre vos transactions d'écriture strictement idempotentes.
Rendre les transactions idempotentes
Nous vous recommandons de rendre les transactions d'écriture idempotentes afin d'éviter tout effet secondaire inattendu en cas de nouvelle tentative. Une transaction est idempotente si elle peut être exécutée plusieurs fois et produire des résultats identiques à chaque fois.
Prenons l'exemple d'une transaction qui insère un document dans une table nomméePerson
. La transaction doit d'abord vérifier si le document existe déjà dans le tableau. Sans cette vérification, le tableau risque de se retrouver avec des documents dupliqués.
Supposons que la transaction soit validée QLDB avec succès côté serveur, mais que le client expire le délai d'attente d'une réponse. Si la transaction n'est pas idempotente, le même document peut être inséré plusieurs fois en cas de nouvelle tentative.
Utilisation d'index pour éviter l'analyse complète des tables
Nous vous recommandons également d'exécuter des instructions contenant une clause de WHERE
prédicat à l'aide d'un opérateur d'égalité sur un champ indexé ou un identifiant de document ; par exemple, WHERE indexedField = 123
ou. WHERE indexedField IN (456, 789)
Sans cette recherche indexée, il est QLDB nécessaire d'effectuer une analyse des tables, ce qui peut entraîner des délais d'expiration des transactions ou des conflits optimistes de contrôle simultané ()OCC.
Pour plus d'informations sur OCC, consultez Modèle de QLDB simultanéité Amazon.
Transactions créées implicitement
L'Amazon. QLDB.Chauffeur. TransactionExecutor
enveloppe une transaction créée implicitement.
Vous pouvez exécuter des instructions dans la fonction lambda en utilisant la Execute
méthode de l'exécuteur de transactions. Le pilote valide implicitement la transaction lorsque la fonction lambda revient.
Les sections suivantes montrent comment exécuter des CRUD opérations de base, spécifier une logique de nouvelle tentative personnalisée et implémenter des contraintes d'unicité.
Table des matières
Création de tables
Création d'index
Lecture de documents
// 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. }
Note
Lorsque vous exécutez une requête sans recherche indexée, elle appelle une analyse complète de la table. Dans cet exemple, nous recommandons d'avoir un index sur le GovId
terrain pour optimiser les performances. Sans index activéGovId
, les requêtes peuvent avoir une latence plus importante et peuvent également entraîner des OCC conflits, des exceptions ou des délais d'attente pour les transactions.
Utilisation des paramètres de requête
L'exemple de code suivant utilise un paramètre de requête de type 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. }
L'exemple de code suivant utilise plusieurs paramètres de requête de type 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. }
L'exemple de code suivant utilise un tableau de paramètres de requête de type 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. }
L'exemple de code suivant utilise une liste C# comme valeur.
// 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 // }, // ] // } }
Note
Lorsque vous exécutez une requête sans recherche indexée, elle appelle une analyse complète de la table. Dans cet exemple, nous recommandons d'avoir un index sur le GovId
terrain pour optimiser les performances. Sans index activéGovId
, les requêtes peuvent avoir une latence plus importante et peuvent également entraîner des OCC conflits, des exceptions ou des délais d'attente pour les transactions.
L'exemple de code suivant utilise un paramètre de requête de type Ion.
L'exemple de code suivant utilise plusieurs paramètres de requête.
L'exemple de code suivant utilise une liste de paramètres de requête.
L'exemple de code suivant utilise une liste d'ions comme valeur. Pour en savoir plus sur l'utilisation des différents types d'ions, consultezUtilisation des types de données Amazon Ion dans Amazon QLDB.
Insertion de documents
L'exemple de code suivant insère les types de données 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)); });
Cette transaction insère un document dans le Person
tableau. Avant l'insertion, il vérifie d'abord si le document existe déjà dans le tableau. Cette vérification rend la transaction idempotente par nature. Même si vous exécutez cette transaction plusieurs fois, elle ne provoquera aucun effet secondaire involontaire.
Note
Dans cet exemple, nous recommandons d'avoir un index sur le GovId
terrain pour optimiser les performances. Sans index activéGovId
, les instructions peuvent avoir une latence plus importante et peuvent également entraîner des OCC conflits, des exceptions ou des délais d'attente pour les transactions.
Insertion de plusieurs documents dans une seule déclaration
Pour insérer plusieurs documents à l'aide d'une seule INSERT instruction, vous pouvez transmettre un List
paramètre C# à l'instruction comme suit.
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 } }
Pour insérer plusieurs documents à l'aide d'une seule INSERT instruction, vous pouvez transmettre un paramètre de type Liste d'ions à l'instruction comme suit.
Vous ne placez pas la variable placeholder (?
) entre crochets (<<...>>
) lorsque vous transmettez une liste d'ions. Dans les instructions partiQL manuelles, les crochets à double angle indiquent une collection non ordonnée appelée sac.
Mettre à jour des documents
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 } }
Note
Dans cet exemple, nous recommandons d'avoir un index sur le GovId
terrain pour optimiser les performances. Sans index activéGovId
, les instructions peuvent avoir une latence plus importante et peuvent également entraîner des OCC conflits, des exceptions ou des délais d'attente pour les transactions.
Supprimer des documents
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 } }
Note
Dans cet exemple, nous recommandons d'avoir un index sur le GovId
terrain pour optimiser les performances. Sans index activéGovId
, les instructions peuvent avoir une latence plus importante et peuvent également entraîner des OCC conflits, des exceptions ou des délais d'attente pour les transactions.
Exécution de plusieurs instructions dans une transaction
// 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; }); }
Logique des nouvelles tentatives
Pour plus d'informations sur la logique de nouvelle tentative intégrée au pilote, consultezComprendre la politique de nouvelle tentative avec le chauffeur sur Amazon QLDB.
Mettre en œuvre des contraintes d'unicité
QLDBne prend pas en charge les index uniques, mais vous pouvez implémenter ce comportement dans votre application.
Supposons que vous souhaitiez implémenter une contrainte d'unicité sur le GovId
champ de la Person
table. Pour ce faire, vous pouvez écrire une transaction qui effectue les opérations suivantes :
-
Affirme que le tableau ne contient aucun document existant avec une valeur spécifiée
GovId
. -
Insérez le document si l'assertion est acceptée.
Si une transaction concurrente passe simultanément l'assertion, une seule des transactions sera validée avec succès. L'autre transaction échouera avec une exception de OCC conflit.
L'exemple de code suivant montre comment implémenter cette logique de contrainte d'unicité.
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)); });
Note
Dans cet exemple, nous recommandons d'avoir un index sur le GovId
terrain pour optimiser les performances. Sans index activéGovId
, les instructions peuvent avoir une latence plus importante et peuvent également entraîner des OCC conflits, des exceptions ou des délais d'attente pour les transactions.
Travailler avec Amazon Ion
Il existe plusieurs méthodes pour traiter les données Amazon Ion dansQLDB. Vous pouvez utiliser la bibliothèque Ion
Les sections suivantes fournissent des exemples de code de traitement de données ioniques à l'aide des deux techniques.
Table des matières
Importation du module Ion
using Amazon.IonObjectMapper;
using Amazon.IonDotnet.Builders;
Création de types d'ions
L'exemple de code suivant montre comment créer des valeurs Ion à partir d'objets C# à l'aide du mappeur d'objets 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);
Les exemples de code suivants montrent les deux manières de créer des valeurs d'ions à l'aide de la bibliothèque d'ions.
Utilisation de 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);
Utilisation de 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);
Obtenir un dump binaire d'ions
// 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()));
Obtenir un vidage de texte 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());
Pour plus d'informations sur l'utilisation d'Ion, consultez la documentation Amazon Ion