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 Amazon QLDB para .NET: referencia de un libro de cocina
Esta guía de referencia muestra los casos de uso más comunes del controlador Amazon QLDB para .NET. En este ejemplo de código C# se muestra cómo usar 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 mejores prácticas 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
- Crear una instancia del controlador
- Operaciones CRUD
- Trabajo con Amazon Ion
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;
Crear una instancia del controlador
El siguiente ejemplo de código crea una instancia del controlador que se conecta a un nombre de registro especificado mediante la configuración predeterminada.
Operaciones CRUD
QLDB ejecuta operaciones de creación, lectura, actualización y eliminación (CRUD, por sus siglas en inglés) como parte de una transacción.
aviso
Como práctica recomendada, haga que sus transacciones de escritura sean estrictamente idempotentes.
Hacer transacciones idempotentes
Le recomendamos que haga que las transacciones de escritura sean idempotentes para evitar efectos secundarios inesperados en caso de reintentos. Una transacción es idempotente si puede ejecutarse varias veces y producir resultados idénticos cada vez.
Por ejemplo, considere una transacción que inserta un documento en una tabla denominadaPerson
. 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 lado del servidor, pero el cliente agota el tiempo de espera mientras espera una respuesta. Si la transacción no es idempotente, podría insertarse el mismo documento más de una vez en caso de reintentarlo.
Uso de índices para evitar escaneos completos de tablas
También se recomienda ejecutar sentencias con una cláusula deWHERE
predicado mediante un operador de igualdad en un campo indexado o un identificador de documento; por ejemplo,WHERE indexedField = 123
oWHERE indexedField IN (456, 789)
. Sin esta búsqueda indexada, QLDB necesita realizar un escaneo de tablas, lo que puede provocar tiempos de espera de las transacciones o conflictos optimistas en el control de concurrencia (OCC).
Para obtener más información sobre la OCC, consulteModelo de simultaneidad de Amazon QLDB.
Transacciones creadas implícitamente
El método Amazon.QLDB.Driver.iQldbDriver .ExecuteTransactionExecutor
envuelve una transacción creada implícitamente.
Puede ejecutar sentencias dentro de la función lambda mediante elExecute
método del ejecutor de transacciones. El controlador confirma implícitamente la transacción cuando la función lambda retorna.
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 se ejecuta una consulta sin una búsqueda indexada, se invoca un análisis completo de la tabla. En este ejemplo, se recomienda tener un índice en elGovId
campo para optimizar el rendimiento. Si no hay un índice activadoGovId
, las consultas pueden tener más latencia y también provocar excepciones de conflictos en la OCC o tiempos de espera en las transacciones.
Uso de parámetros de consulta
El siguiente ejemplo de código usa 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. }
El siguiente ejemplo de código utiliza 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. }
El siguiente ejemplo de código usa 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. }
El siguiente ejemplo de código usa 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 se ejecuta una consulta sin una búsqueda indexada, se invoca un análisis completo de la tabla. En este ejemplo, se recomienda tener un índice en elGovId
campo para optimizar el rendimiento. Si no hay un índice activadoGovId
, las consultas pueden tener más latencia y también provocar excepciones de conflictos en la OCC o tiempos de espera en las transacciones.
El siguiente ejemplo de código usa un parámetro de consulta de tipo Ion.
El siguiente ejemplo de código utiliza varios parámetros de consulta.
El siguiente ejemplo de código usa una lista de parámetros de consulta.
El siguiente ejemplo de código usa una lista de iones como valor. Para obtener más información sobre el uso de diferentes tipos de Ion, consulteTrabajando con tipos de datos de Amazon Ion en Amazon QLDB.
Inserción de documentos
El siguiente ejemplo de código inserta tipos de datos de iones.
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 laPerson
tabla. Antes de insertarlo, primero comprueba si el documento ya existe en la tabla. Esta comprobación hace que la transacción sea de naturaleza idempotente. Incluso si realizas esta transacción varias veces, no causará ningún efecto secundario no deseado.
nota
En este ejemplo, se recomienda tener un índice en elGovId
campo para optimizar el rendimiento. Sin un índice activadoGovId
, las declaraciones pueden tener más latencia y también provocar excepciones de conflictos en la OCC o tiempos de espera en las transacciones.
Insertar varios documentos en una declaración
Para insertar varios documentos mediante una solaINSERT sentencia, puede pasar unList
parámetro de C# a la sentencia 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 solaINSERT sentencia, puede pasar un parámetro de tipo Ion list a la sentencia de la siguiente manera.
No se incluye el marcador de posición de la variable (?
) entre corchetes de doble ángulo (<<...>>
) al pasar una lista de iones. En las instrucciones manuales de PartiQL, 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 elGovId
campo para optimizar el rendimiento. Sin un índice activadoGovId
, las declaraciones pueden tener más latencia y también provocar excepciones de conflictos en la OCC o tiempos de espera en las transacciones.
Eliminar 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 elGovId
campo para optimizar el rendimiento. Sin un índice activadoGovId
, las declaraciones pueden tener más latencia y también provocar excepciones de conflictos en la OCC o tiempos de espera en las transacciones.
Ejecución de varios estados de cuenta 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; }); }
Reintentar la lógica
Para obtener información sobre la lógica de reintento integrada en el controlador, consulteDescripción de la política de reintentos con el controlador en Amazon QLDB.
Implementación de restricciones de unicidad
QLDB no admite índices únicos, pero puede implementar este comportamiento en su aplicación.
Supongamos que desea implementar una restricción de exclusividad en elGovId
campo de laPerson
tabla. Para ello, puede escribir una transacción que haga lo siguiente:
-
Afirme que la tabla no tiene ningún documento existente con un valor especificado
GovId
. -
Inserte el documento si se aprueba la afirmación.
Si una transacción competidora aprueba simultáneamente la afirmación, solo una de las transacciones se confirmará correctamente. La otra transacción fallará si se produce una excepción de conflicto de OCC.
En el ejemplo de código siguiente se muestra cómo implementar esta lógica de restricción de unicidad.
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 elGovId
campo para optimizar el rendimiento. Sin un índice activadoGovId
, las declaraciones pueden tener más latencia y también provocar excepciones de conflictos en la OCC o tiempos de espera en las transacciones.
Trabajo con Amazon Ion
Hay varias formas de procesar datos de Amazon Ion en QLDB. Puede utilizar la biblioteca de iones
Las siguientes secciones proporcionan ejemplos de código del procesamiento de datos de iones 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 ejemplo de código siguiente 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 de iones mediante la biblioteca de iones.
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 iones
// 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