Parte 2 Adicionando Consultas

Autor: Marcelo Franceschi de Bianchi - CTS LATAM

Revisor: Roberto Cavalcanti - CTS LATAM

Dando continuidade a série de três artigos relacionados ao Retry Logic, esse artigo é o de número 2 no qual você aprenderá como adicionar as consultas a sua aplicação que conterá o Retry Logic e fará a conexão com o banco de dados Windows Azure SQL Database (SQL Azure).

1. Adicionando Consultas

Você verá nessa etapa o básico sobre as consultas SQL, como criar e disparar um transient failure.

1.1 Configurando a connection string

Faça o download dos arquivos de projeto project files clip_image001 e abra a solução com o nome de RetryLogicTutorial_Skeleton.sln. Com o botão direito do mouse clique em Form1.cs e selecione View Code. Então você deverá adicionar a váriavel membro SqlConnectionStringBuilder ao formulário Form1 class.

SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();

In the Form1 constructor, initialize the SqlConnectionStringBuilder for the CusomerOrders database:

builder.DataSource = "xxxxxxxxxx.database.windows.net";
builder.InitialCatalog = "CustomerOrders";
builder.Encrypt = true;
builder.TrustServerCertificate = false;
builder.UserID = "xxxxxxxx";
builder.Password = "xxxxxxxx";

Substitua o nome do servidor atual, user ID and password pelo seu servidor SQL Database.

1.2 Adicionando um ADO.Net Query

Localize a função com o nome de AdoQueyWorker_DoWork e adicione o seguinte código:

try
{
    using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
    {
        connection.Open();
 
        SqlCommand command = new SqlCommand("select product_name from products with (READCOMMITTEDLOCK)");
        command.Connection = connection;
        command.CommandTimeout = 3;
 
        SqlDataReader reader = command.ExecuteReader();
 
        while (reader.Read())
        {
            (sender as BackgroundWorker).ReportProgress(0, reader["product_name"]);
        }
    }
}
catch (SqlException ex)
{
    MessageBox.Show(ex.Message, "SqlException");
}

A função é chamada de uma background worker thread. A consulta retorna uma lista de nomes de produtos, no qual serão passados para UI thread pela chamada de BackgroundWorker.ReportProgress. O parametro READCOMMITTEDLOCK bloqueia a consulta se a tabela está com um lock.

  1. Adicionando uma nova conexão de dados:
  2. Caso a janela do Server Exporer não esteja visivel, no menu View, clique em Other Windows e depois clique em Server Explorer.
  3. Na janela Server Explorer, dê um clique com o botão direito do mouse em Data Connections e selecione Add Connection.
  4. Na caixa de dialogo Add Connection, entre com o Fully qualified name do seu SQL Database server p. Ex. (xxxxxxxx.database.windows.net) no campo Server Name.
  5. Selecione SQL Server Authentication
  6. Entre com o login name e password
  7. Em Select or enter a database name, coloque “PedidosClientes”, como está demonstrado na figura 1.

clip_image002

Figura 1: Entrando com a autênticação (*)

1.3 Adicionando um LINQ a SQL Class

  1. Essa parte do artigo demonstrará como criar um LINQ query que retornará todos os pedidos dos clientes.
  2. Na janela do solution explorer, clique com o botão direito do mouse no projeto e selecione Add New Item.
  3. Em Data, selecione LINQ to SQL Classes. Nomeie o arquivo CustomerOrders.dml.
  4. Na janela do Server Explorer, expanda o nó Data Connections e localize o banco de dados CustoemrOrders.
  5. Expanda o nó do banco de dados e então expanda os nós de tabelas.
  6. Arraste todas as quatro tabelas (customers, order_items, orders e products) para o designer e você visualizará todas as tabelas graficamente como está demonstrado na figura 2.

clip_image004

Figura 2: Modelo do exemplo de banco de dados do SQL Database. (*)

O próximo passo será localizar a função denominada de LinqQueryWorker_DoWork e então adicionar o seguinte código:

try
{
    Deadlock();
 
    CustomerOrdersDataContext ctx = new CustomerOrdersDataContext();
    ctx.Connection.ConnectionString = builder.ConnectionString;
 
    var results = from c in ctx.customers
                    from o in c.orders
                    from i in o.order_items
                    select new { c.lname, c.fname, i.product.product_name, i.quantity };
 
    e.Result = results.ToList();
 
}
catch (SqlException ex)
{
    MessageBox.Show(ex.Message, "SqlException");
}

Então você deverá rodar a aplicação e clicar no botão “ADO.NET Query” e no botão “LINQ Query”. Você verá os resultados da consulta populados na Interface Gráfica da aplicação, de Retry Logic como está demonstrado na figura 3.

clip_image006

Figura 3: Interface Gráfica da aplicação exemplo de Retry Logic. (*)

1.4 Adicionando uma Block Transaction

Um aspecto interessante sobre o retry logic é que podemos induzir um erro transiente para efeito de teste. Para esse objetivo, intensionalmente iremos causar um deadlock.

Localize a função denominada longTransaction_DoWork e adicione o seguinte código:

System.Diagnostics.Debug.WriteLine("Starting long transaction");
try
{
    using (SqlConnection con = new SqlConnection(builder.ConnectionString))
    {
        con.Open();
 
        SqlTransaction transaction = con.BeginTransaction();
 
        SqlCommand cmd = new SqlCommand("UPDATE products WITH (TABLOCKX) SET product_name = 'x'");
        cmd.Connection = con;
        cmd.Transaction = transaction;
        cmd.ExecuteNonQuery();
 
        for (int i = 1; i <= LongTransactionTime; i++)
        {
            Thread.Sleep(1000);
            longTransactionWorker.ReportProgress((int)(i * 100) / LongTransactionTime);
        }
 
        cmd = new SqlCommand("SELECT * FROM customers with (READCOMMITTEDLOCK)");
        cmd.Connection = con;
        cmd.Transaction = transaction;
        cmd.ExecuteNonQuery();
 
        System.Diagnostics.Debug.WriteLine("Roll back long transaction");
        transaction.Rollback();
    }
}
catch (Exception ex)
{
    MessageBox.Show(ex.Message);
}

Essa função começa uma nova transação SQL e então permanece com um exlucisve lock na tabela Products. O tempo de espera será de 30 segundos antes da realização do rolling back da transação.

Então o próximo passo será localizar a função denominada Deadlock e então adicionar o seguinte código:

using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
{
    connection.Open();
 
    SqlTransaction transaction = connection.BeginTransaction(System.Data.IsolationLevel.Serializable);
 
    SqlCommand command = new SqlCommand(
        "UPDATE customers WITH (TABLOCKX) SET lname = 'x'; " +
        "SELECT * FROM PRODUCTS with (READCOMMITTEDLOCK)"
        );
    command.Connection = connection;
    command.Transaction = transaction;
    command.ExecuteNonQuery();
 
    transaction.Rollback();
}

Rode a aplicação e clique no botão Deadlock. Enquanto a barra de progresso avança, clique no botão ADO.NET Query ou então no botão LINQ Query. Você verá uma mensagem de erro, SQL Error -2 (timeout) ou então SQL Error 1205 (deadlock).

Vá agora para o artigo Retry Logic para erros transientes no SQL Azure parte 3, para a continuação da implementação do Retry Logic na aplicação que irá realizar o acesso ao banco de dados SQL Azure.

(*) Essas imagens foram retiradas do artigo Retry Logic for Transient Failures in SQL Azure

http://social.technet.microsoft.com/wiki/contents/articles/4235.retry-logic-for-transient-failures-in-sql-azure.aspx

2. Referências

Retry Logic para erros transientes no SQL Azure parte 1

Retry Logic para erros transientes no SQL Azure parte 3

Retry Logic for Transient Failures in SQL Azure

http://social.technet.microsoft.com/wiki/contents/articles/4235.retry-logic-for-transient-failures-in-sql-azure.aspx

Download the c sharp class directly the library from http://appfabriccat.com/2011/02/transient-fault-handling-framework/

SQL Azure Retry Logic Sample

http://code.msdn.microsoft.com/windowsazure/SQL-Azure-Retry-Logic-2d0a8401

SQL Azure Connection Retry (CODE WITH PARAMETERS)

http://blogs.msdn.com/b/bartr/archive/2010/06/18/sql-azure-connection-retry.aspx

SQL Azure Connectivity Troubleshooting Guide

http://social.technet.microsoft.com/wiki/contents/articles/sql-azure-connectivity-troubleshooting-guide.aspx