As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.
QLDBDriver da Amazon para. NET— Referência do livro de receitas
Importante
Aviso de fim do suporte: os clientes existentes poderão usar a Amazon QLDB até o final do suporte em 31/07/2025. Para obter mais detalhes, consulte Migrar um Amazon QLDB Ledger para o Amazon Aurora Postgre
Este guia de referência mostra casos de uso comuns do QLDB driver da Amazon para. NET. Ele fornece exemplos de código C# que demonstram como usar o driver para executar operações básicas de criação, leitura, atualização e exclusão (CRUD). Também inclui exemplos de código para processamento de dados do Amazon Ion. Além disso, este guia destaca as práticas recomendadas para tornar as transações idempotentes e implantar restrições de exclusividade.
nota
Este tópico fornece exemplos de código de processamento de dados do Amazon Ion usando o mapeador de objetos Ion
Importação do driver
O exemplo de código a seguir importa o driver.
using Amazon.QLDB.Driver; using Amazon.QLDB.Driver.Generic; using Amazon.QLDB.Driver.Serialization;
using Amazon.QLDB.Driver; using Amazon.IonDotnet.Builders;
Instanciação do driver
O exemplo de código a seguir cria uma instância do driver que se conecta a um nome de ledger especificado usando as configurações padrão.
CRUDoperações
QLDBexecuta operações de criação, leitura, atualização e exclusão (CRUD) como parte de uma transação.
Atenção
Como prática recomendada, torne suas transações de gravação estritamente idempotentes.
Tornar as transações idempotentes
Recomendamos que você torne as transações de gravação idempotentes para evitar efeitos colaterais inesperados no caso de novas tentativas. Uma transação é idempotente se puder ser executada várias vezes e produzir resultados idênticos a cada vez.
Por exemplo, considere uma transação que insere um documento em uma tabela chamada Person
. A transação deve primeiro verificar se o documento já existe ou não na tabela. Sem essa verificação, a tabela pode acabar com documentos duplicados.
Suponha que a transação seja confirmada QLDB com sucesso no lado do servidor, mas o cliente expire enquanto espera por uma resposta. Se a transação não for idempotente, o mesmo documento poderá ser inserido mais de uma vez no caso de uma nova tentativa.
Usar índices para evitar varreduras completas da tabela
Também recomendamos executar instruções com uma cláusula de predicado WHERE
usando um operador de igualdade em um campo indexado ou em um ID de documento, por exemplo, WHERE indexedField = 123
ou WHERE indexedField IN (456, 789)
. Sem essa pesquisa indexada, é QLDB necessário fazer uma varredura da tabela, o que pode levar a tempos limite de transação ou conflitos otimistas de controle de simultaneidade (). OCC
Para obter mais informações sobre o OCC, consulte Modelo de QLDB concorrência da Amazon.
Transações criadas implicitamente
A Amazônia. QLDB.Motorista. TransactionExecutor
envolve uma transação criada implicitamente.
Você pode executar instruções na função do Lambda usando o método Execute
do executor da transação. O driver confirma implicitamente a transação quando a função do Lambda retorna.
As seções a seguir mostram como executar CRUD operações básicas, especificar a lógica de repetição personalizada e implementar restrições de exclusividade.
Sumário
Criar tabelas
Criar índices
Ler 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
Quando você executa uma consulta sem uma pesquisa indexada, ela invoca uma verificação completa da tabela. Neste exemplo, recomendamos ter um índice no campo GovId
para otimizar o desempenho. Sem um índice ativadoGovId
, as consultas podem ter mais latência e também podem levar a exceções de OCC conflito ou tempos limite de transação.
Usar parâmetros de consulta
O exemplo de código a seguir usa um parâmetro de consulta do 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. }
O exemplo de código a seguir usa múltiplos parâmetros de consulta do 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. }
O exemplo de código a seguir usa uma matriz de consulta do 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. }
O exemplo de código a seguir usa uma lista 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
Quando você executa uma consulta sem uma pesquisa indexada, ela invoca uma verificação completa da tabela. Neste exemplo, recomendamos ter um índice no campo GovId
para otimizar o desempenho. Sem um índice ativadoGovId
, as consultas podem ter mais latência e também podem levar a exceções de OCC conflito ou tempos limite de transação.
O exemplo de código a seguir usa um parâmetro de consulta do tipo Ion.
O exemplo de código a seguir usa múltiplos parâmetros de consulta.
O exemplo de código a seguir usa uma lista de parâmetros de consulta.
O exemplo de código a seguir usa uma lista Ion como valor. Para saber mais sobre os diferentes tipos de Ion, consulte Trabalhando com os tipos de dados Amazon Ion na Amazon QLDB.
Inserir documentos
Os exemplos de código a seguir inserem os tipos de dados 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)); });
Essa transação insere um documento na tabela Person
. Antes de inserir, ele primeiro verifica se o documento já existe na tabela. Essa verificação torna a transação idempotente por natureza. Mesmo que você execute essa transação várias vezes, ela não causará efeitos colaterais indesejados.
nota
Neste exemplo, recomendamos ter um índice no campo GovId
para otimizar o desempenho. Sem um índice ativadoGovId
, as declarações podem ter mais latência e também podem levar a exceções de OCC conflito ou tempos limite de transação.
Como inserir vários documentos em uma instrução
Para inserir vários documentos usando uma única instrução INSERT, você pode passar um parâmetro C# List
para a instrução da seguinte maneira.
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 inserir vários documentos usando uma única instrução INSERT, você pode passar um parâmetro do tipo lista Ion para a instrução da seguinte maneira.
Você não coloca o marcador variável (?
) entre colchetes angulares duplos (<<...>>
) ao passar uma lista Ion. Nas instruções manuais do PartiQL, colchetes angulares duplos denotam uma coleção não ordenada conhecida como bolsa.
Como atualizar 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
Neste exemplo, recomendamos ter um índice no campo GovId
para otimizar o desempenho. Sem um índice ativadoGovId
, as declarações podem ter mais latência e também podem levar a exceções de OCC conflito ou tempos limite de transação.
Como excluir 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
Neste exemplo, recomendamos ter um índice no campo GovId
para otimizar o desempenho. Sem um índice ativadoGovId
, as declarações podem ter mais latência e também podem levar a exceções de OCC conflito ou tempos limite de transação.
Como executar várias instruções em uma transação
// 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 novas tentativas
Para obter informações sobre a lógica de repetição integrada do driver, consulte Entendendo a política de repetição com o motorista na Amazon QLDB.
Implementação de restrições de exclusividade
QLDBnão oferece suporte a índices exclusivos, mas você pode implementar esse comportamento em seu aplicativo.
Suponha que você queira implementar uma restrição de exclusividade no campo GovId
da tabela Person
. Para fazer isso, você pode escrever uma transação que faça o seguinte:
-
Afirme que a tabela não tem documentos existentes com um
GovId
especificado. -
Insira o documento se a afirmação for aprovada.
Se uma transação concorrente passar simultaneamente pela declaração, somente uma das transações será confirmada com sucesso. A outra transação falhará com uma exceção de OCC conflito.
O exemplo de código a seguir mostra como implementar essa lógica de restrição de exclusividade.
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
Neste exemplo, recomendamos ter um índice no campo GovId
para otimizar o desempenho. Sem um índice ativadoGovId
, as declarações podem ter mais latência e também podem levar a exceções de OCC conflito ou tempos limite de transação.
Como trabalhar com o Amazon Ion
Há várias maneiras de processar dados do Amazon Ion noQLDB. Você pode usar a biblioteca Ion
As seções a seguir fornecem exemplos de código de processamento de dados Ion usando ambas as técnicas.
Sumário
Importar o módulo Ion
using Amazon.IonObjectMapper;
using Amazon.IonDotnet.Builders;
Criação de tipos de Ion
O exemplo de código a seguir mostra como criar valores de Ion a partir de objetos C# usando o mapeador de objeto 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);
Os exemplos de código a seguir mostram as duas maneiras de criar valores Ion usando a biblioteca Ion.
Como usar o 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);
Como usar o 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);
Obter um despejo de binário 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()));
Obter um despejo de texto 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 obter mais informações sobre como trabalhar com o Ion, consulte a documentação do Amazon Ion