Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.
Controlador de Amazon QLDB para .NET: libro de recetas de referencia
importante
Aviso de fin del soporte: los clientes actuales podrán utilizar Amazon QLDB hasta que finalice el soporte, el 31 de julio de 2025. Para obtener más información, consulte Migración de un registro de Amazon QLDB a Amazon Aurora
Esta guía de referencia muestra los casos de uso más comunes del controlador de Amazon QLDB para .NET. En él se proporcionan ejemplos de código C# que muestran cómo utilizar el controlador para ejecutar operaciones básicas de creación, lectura, actualización y eliminación (CRUD). También incluye ejemplos de código para procesar datos de Amazon Ion. Además, esta guía destaca las prácticas recomendadas para hacer que las transacciones sean idempotentes e implementar restricciones de exclusividad.
nota
En este tema, se proporcionan ejemplos de código sobre el procesamiento de datos de Amazon Ion mediante el mapeador de objetos Ion
Contenido
Importación del controlador
El siguiente ejemplo de código importa el controlador.
using Amazon.QLDB.Driver; using Amazon.QLDB.Driver.Generic; using Amazon.QLDB.Driver.Serialization;
using Amazon.QLDB.Driver; using Amazon.IonDotnet.Builders;
Instanciación del controlador
En el siguiente ejemplo de código, se crea una instancia del controlador que se conecta a un nombre de libro mayor especificado mediante la configuración predeterminada.
Operaciones CRUD
La QLDB ejecuta operaciones de creación, lectura, actualización y eliminación (CRUD) como parte de una transacción.
aviso
Como práctica recomendada, haga que sus transacciones de escritura sean estrictamente idempotentes.
Hacer que las transacciones sean idempotentes
Le recomendamos que haga que las transacciones de escritura sean idempotentes para evitar cualquier efecto secundario inesperado en caso de reintentos. Una transacción es idempotente si puede ejecutarse varias veces y producir resultados idénticos cada vez.
Por ejemplo, pensemos en una transacción que inserta un documento en una tabla llamada Person
. La transacción debe comprobar primero si el documento ya existe o no en la tabla. Sin esta comprobación, la tabla podría terminar con documentos duplicados.
Supongamos que QLDB confirma correctamente la transacción en el servidor pero el cliente agota el tiempo de espera mientras espera una respuesta. Si la transacción no es idempotente, se podría insertar el mismo documento más de una vez en caso de volver a intentarlo.
Uso de índices para evitar escanear tablas completas
También le recomendamos que ejecute instrucciones con una frase de predicado WHERE
utilizando un operador de igualdad en un campo indexado o un ID de documento; por ejemplo, WHERE indexedField = 123
o WHERE indexedField IN (456, 789)
. Sin esta búsqueda indexada, la QLDB necesita escanear las tablas, lo que puede provocar tiempos de espera de las transacciones o conflictos de control de concurrencia optimista (OCC).
Para obtener más información acerca de OCC, consulte Modelo de concurrencia de Amazon QLDB.
Transacciones creadas de forma implícita
El controlador Amazon.QLDB. TransactionExecutor
envuelve una transacción creada de forma implícita.
Puede ejecutar sentencias dentro de la función de Lambda mediante el método Execute
del ejecutor de transacciones. El controlador confirma implícitamente la transacción cuando vuelve la función de Lambda.
En las siguientes secciones se muestra cómo ejecutar operaciones CRUD básicas, especificar una lógica de reintento personalizada e implementar restricciones de exclusividad.
Contenido
Creación de tablas
Creación de índices
Lectura de documentos
// 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. }
nota
Cuando ejecuta una consulta sin una búsqueda indexada, se invoca un escaneo completo de la tabla. En este ejemplo, se recomienda tener un índice en el campo GovId
para optimizar el rendimiento. Sin un índice en GovId
, las consultas pueden tener más latencia y, además, provocar excepciones de conflictos de OCC o tiempos de espera de las transacciones.
Uso de parámetros de consulta
En el siguiente ejemplo de código, se utiliza un parámetro de consulta de tipo 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. }
En el siguiente ejemplo de código, se utilizan varios parámetros de consulta de tipo 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. }
En el siguiente ejemplo de código, se utiliza una matriz de parámetros de consulta de tipo 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. }
En el siguiente ejemplo de código, se utiliza una lista de C# como valor.
// 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 // }, // ] // } }
nota
Cuando ejecuta una consulta sin una búsqueda indexada, se invoca un escaneo completo de la tabla. En este ejemplo, se recomienda tener un índice en el campo GovId
para optimizar el rendimiento. Sin un índice en GovId
, las consultas pueden tener más latencia y, además, provocar excepciones de conflictos de OCC o tiempos de espera de las transacciones.
En el siguiente ejemplo de código, se utiliza un parámetro de consulta de tipo Ion.
En el siguiente ejemplo de código se utilizan varios parámetros de consulta.
En el siguiente ejemplo de código, se utiliza una lista de parámetros de consulta.
En el siguiente ejemplo de código, se utiliza una lista de Ion como valor. Para obtener más información sobre los distintos tipos Ion, consulte Uso de tipos de datos de Amazon Ion en Amazon QLDB.
Inserción de documentos
El siguiente ejemplo de código inserta los tipos de datos de 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)); });
Esta transacción inserta un documento en la tabla Person
. Antes de insertar, compruebe primero si el documento ya existe en la tabla. Esta comprobación hace que la transacción sea de naturaleza idempotente. Incluso si realiza esta transacción varias veces, no provocará ningún efecto secundario no deseado.
nota
En este ejemplo, se recomienda tener un índice en el campo GovId
para optimizar el rendimiento. Sin un índice en GovId
, las instrucciones pueden tener más latencia y, además, provocar excepciones de conflictos de OCC o tiempos de espera de las transacciones.
Insertar varios documentos en una instrucción
Para insertar varios documentos mediante una sola instrucción INSERT, puede pasar un parámetro del tipo List
C# a la instrucción de la siguiente manera.
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 } }
Para insertar varios documentos mediante una sola instrucción INSERT, puede pasar un parámetro del tipo Ion list a la instrucción de la siguiente manera.
No coloque el marcador de posición variable (?
) entre corchetes de doble ángulo (<<...>>
) al pasar una lista Ion. En las instrucciones PartiQL manuales, los corchetes de doble ángulo indican una colección desordenada conocida como bolsa.
Actualización de documentos
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 } }
nota
En este ejemplo, se recomienda tener un índice en el campo GovId
para optimizar el rendimiento. Sin un índice en GovId
, las instrucciones pueden tener más latencia y, además, provocar excepciones de conflictos de OCC o tiempos de espera de las transacciones.
Eliminación de documentos
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 } }
nota
En este ejemplo, se recomienda tener un índice en el campo GovId
para optimizar el rendimiento. Sin un índice en GovId
, las instrucciones pueden tener más latencia y, además, provocar excepciones de conflictos de OCC o tiempos de espera de las transacciones.
Ejecutar varias instrucciones en una transacción
// 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; }); }
Lógica de reintentos
Para obtener información sobre la lógica de reintentos integrada en el controlador, consulte Descripción de la política de reintentos del controlador en Amazon QLDB.
Implementación de restricciones de exclusividad
QLDB no admite índices únicos, pero puede implementar este comportamiento en su aplicación.
Suponga que desea implementar una restricción de exclusividad en el campo GovId
de la tabla Person
. Para ello, puede escribir una transacción que haga lo siguiente:
-
Afirme que en la tabla no hay documentos existentes con un
GovId
especificado. -
Insertar el documento si se aprueba la afirmación.
Si una transacción competidora supera la afirmación simultáneamente, solo una de las transacciones se confirmará correctamente. La otra transacción fallará y se producirá una excepción de conflicto de OCC.
En el siguiente ejemplo de código, se muestra cómo implementar esta lógica de restricción de exclusividad.
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)); });
nota
En este ejemplo, se recomienda tener un índice en el campo GovId
para optimizar el rendimiento. Sin un índice en GovId
, las instrucciones pueden tener más latencia y, además, provocar excepciones de conflictos de OCC o tiempos de espera de las transacciones.
Trabajar con Amazon Ion
En QLDB, los datos de Amazon Ion se pueden procesar de varias formas. Puede utilizar la biblioteca de Ion
En las siguientes secciones se proporcionan ejemplos de código del procesamiento de datos de Ion mediante ambas técnicas.
Contenido
Importación del módulo Ion
using Amazon.IonObjectMapper;
using Amazon.IonDotnet.Builders;
Creación de tipos de Ion
En el siguiente ejemplo de código, se muestra cómo crear valores de Ion a partir de objetos de C# mediante el mapeador de objetos de 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);
Los siguientes ejemplos de código muestran las dos formas de crear valores Ion mediante la biblioteca de Ion.
Uso 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);
Uso 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);
Obtener un volcado binario de 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()));
Obtener un volcado de texto de 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());
Para obtener más información sobre cómo trabajar con Ion, consulte la documentación de Amazon Ion