Desenvolvimento - ADO.NET
O que há de novo no ADO.Net 2.0
O ADO.Net 2.0 vem com uma nova gama de novidades. Isso inclui uma novo modelo base de classes para providers, especificamente para o já conhecido System.Data.SqlClient, pelo fato de que o .Net Framework 2.0 foi lançado juntamente com o novo SQL Server 2005. Neste artigo iremos dar um overview sobre essas novas features, e sempre que possível, com exemplos de uso.
por Daniel Henrique BragagnoloO ADO.Net 2.0 vem com uma nova gama de novidades. Isso inclui uma novo modelo base de classes para providers, especificamente para o já conhecido System.Data.SqlClient, pelo fato de que o .Net Framework 2.0 foi lançado juntamente com o novo SQL Server 2005. Neste artigo iremos dar um overview sobre essas novas features, e sempre que possível, com exemplos de uso.
O novo Base-Class-Based Provider Model
Nas versões 1.0 e 1.1 do ADO.Net os providers implementavam uma série de classes específicas. Por exemplo, System.Data.SqlClient possui uma classe SqlConnection e essa classe implementa IDbConnection. System.Data.OracleClient possui uma classe OracleConnection a qual também implementa IDbConnection. Agora com o novo modelo de providers da versão 2.0 é baseado em uma série de classes base no System.Data.common, e não mais classes específicas como SqlClient e OracleClient. É claro que o suporte a essas classes ainda continua.
Provider Factories
Novamente, nas versões 1.0/1.1 do ADO.Net quando precisávamos de acesso a dados, tínhamos que criar uma classe/objeto específicos referente ao Database usado no cenário: SqlClient, OracleClient ou OleDb. Temos agora, nesse novo modelo, uma nova classe (System.Data.Common.ProviderFactories) que faz isso pra gente, de acordo com um parâmetro informado (ProviderInvariantName). Isso acontece porque cada Provider tem um registro (string) no Machine.Config contendo suas informações. Importando o namespace System.Data.Commom temos acesso a classe DbProviderFactories e ao método GetFactoryClasses, o qual retorna um DataTable contendo todas as informações dos providers. Abaixo vemos como resgatamos isso, e na figura 1 os dados populados em um DataGridView:
DataTable dt = DbProviderFactories.GetFactoryClasses();
this.dataGridView1.DataSource = dt;
Figura1: Lista de Providers do Machine.Config
Observe que na coluna InvariantName temos o nome dos Providers disponíveis, passando isso como parâmetro para o método GetFactory da classe DbProviderFactories conseguimos então criar um objeto provider específico para o Database informado no parâmetro, neste caso SqlServer. No exemplo abaixo, criamos um provider e instanciamos uma Connection para o SqlServer:
IDbConnection conn = null;
string provstring = "System.Data.SqlClient";
DbProviderFactory fact = DbProviderFactories.GetFactory(provstring);
conn = fact.CreateConnection();
Observe mais uma vez que não há nada no código especificando explicitamente que a Connection criada deve ser uma SqlConnection. O objeto Factory sabe disso através do InvariantName informado no parâmetro. Então, para mudar essa conexão para Oracle por exemplo, basta alterar o InvariantName para System.Data.OracleClient.
Asynchronous Commands
Em alguns momentos em nosso código precisamos executar mais de um processo ao mesmo tempo. No ADO.Net 2.0 temos suporte a esse tipo de comando assíncrono, informando um Begin e um End para determinada execução. Abaixo temos os métodos que suportam esse tipo de execução Assíncrona, comparando com os antigos Síncronos:
Synchronous Method |
Asynchronous Methods |
ExecuteNonQuery |
BeginExecuteNonQuery, EndExecuteNonQuery |
ExecuteReader |
BeginExecuteReader, EndExecuteReader |
ExecuteXmlReader |
BeginExecuteXmlReader, EndExecuteXmlReader |
Apesar de ser de grande ajuda, não devemos usar esse tipo de comando sem que realmente seja necessário, pois pelo fato de disparar 2 processos paralelos, temos um impacto considerável de performance. O uso é indicado em situações onde já sabemos que um processo poderá demorar muito tempo para ser concluído, ou outros casos do tipo.
Abaixo segue um exemplo interessante com essa nova feature:
Em uma aplicação Windows Forms adicionei um novo form chamado Asynchronous, e incluí um TextBox multiline para exibir o texto. dentro deste form seguem os métodos abaixo:
private void Asynchronous_Load(object sender, EventArgs e)
{
// Este exemplo mostra o uso do comando BeginExecuteReader
// WAITFOR simplesmente faz a query aguardar um determinado tempo
string commandText =
"WAITFOR DELAY "00:00:03";" +
"SELECT Distinct ShipName FROM Orders " +
"WHERE ShipName LIKE "O%" " +
"ORDER BY ShipName";
RunCommandAsynchronously(commandText, GetConnectionString());
this.txtDisplay.Text = display;
}
private void RunCommandAsynchronously(string commandText, string connectionString)
{
// Verificamos em uma Thread paralela se o comando foi conluido
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = new SqlCommand(commandText, connection);
connection.Open();
// Pede para o Command executar um comando assincrono[Thread 1]
IAsyncResult result = command.BeginExecuteReader();
// Verifica se o comando foi conluido[Thread 2]
int count = 0;
while (!result.IsCompleted) {
count += 1;
display += "Waiting ({" + count + "}) " + "\r\n";
// Aguardar 10 segundos...
System.Threading.Thread.Sleep(100);
}
// Ao sair do While significa que a Thread 1 já foi conluída...
using (SqlDataReader reader = command.EndExecuteReader(result)) {
DisplayResults(reader);
}
}
}
private void DisplayResults(SqlDataReader reader)
{
// Exibe os dados do DataReader
while (reader.Read()){
// Exibe todas as colunas
for (int i = 0; i < reader.FieldCount; i++)
display += reader.GetValue(i) + "\r\n";
}
}
public static string GetConnectionString()
{
// Para conexões assincronas é necessário incluir o
// comando "Asynchronous Processing=true" na String de Conexão
return "Server=SERVER_NAME; User Id=USER_NAME; Password=PASS; Initial Catalog=Northwind; Asynchronous Processing=true";
}
Na Figura 2 abaixo podemos ver o resultado da operação, observe que enquanto o processo de execução da query não termina o texto “Waiting” é impresso. Depois do processo terminado entao o retorno da query é impresso.
Provider Statistics
Mais uma das novidades dessa nova plataforma são as Estatísticas. A classe SqlClient agora nos prove a possibilidade de resgatar estatísticas do database através do método RetrieveStatistics da classe SqlConnection.
Este recurso vem desabilitado por default, e devemos habilita-lo para fazer uso.
Abaixo temos um exemplo do uso das estatísticas:
string connectionString = GetConnectionString();
using (SqlConnection awConnection = new SqlConnection(connectionString))
{
// StatisticsEnabled é Falso por default
// Devemos setar como True para o processo ser iniciado
awConnection.StatisticsEnabled = true;
// Resgatando os dados
string productSQL = "SELECT * FROM Products";
SqlDataAdapter productAdapter = new SqlDataAdapter(productSQL, awConnection);
DataSet awDataSet = new DataSet();
awConnection.Open();
productAdapter.Fill(awDataSet, "ProductTable");
// Depois de executado o comando, resgatamos as informações das estatísticas
IDictionary currentStat = awConnection.RetrieveStatistics();
// Imprimindo os valores individuais,
// relacionados ao comando que acabou de ser executado
long bytesReceived = (long)currentStat["BytesReceived"];
long bytesSent = (long)currentStat["BytesSent"];
long selectCount = (long)currentStat["SelectCount"];
long selectRows = (long)currentStat["SelectRows"];
this.txtEst.Text = "Total Counters: " + currentStat.Count.ToString() + "\r\n";
this.txtEst.Text += "BytesReceived: " + bytesReceived.ToString() + "\r\n";
this.txtEst.Text += "BytesSent: " + bytesSent.ToString() + "\r\n";
this.txtEst.Text += "SelectCount: " + selectCount.ToString() + "\r\n";
this.txtEst.Text += "SelectRows: " + selectRows.ToString() + "\r\n";
}
No exemplo acima, foi usado um windows form contendo um TextBox multiline, e o código apresentado foi colocado no evento Load.
AttachDbFileName
Agora, a classe SqlClient possui uma nova opção chamada AttachDbFileName, com essa nova feature temos a possibilidade de dizer diretamente na connection string o nome do arquivo do database que deve ser atachado na instancia do usuário (User Instance). Uma User Instance é similar a uma instancia normal do Sql Server, as ela é criada em tempo de execução, de acordo com parâmetros passados na connection string.
AttachDbFilename=|DataDirectory|\Database1.mdf;
A opção DataDirectory determina o diretorio onde está o arquivo do database(.mdf) e é onde deve estar também o arquivo de log(.ldf). Caso já exista um database com este mesmo nome, a conexão abrirá o já existente.
Quando fazemos uso deste recurso, o usuário com direitos sobre essa instancia criada será o mesmo usuário do Windows o qual abriu a conexão. Por exemplo, se o usuário Paulo abriu uma conexão especificando a opção UserInstance na connection string, entao essa User Instance terá Paulo como user account.
Como disse anteriormente, uma User Instance é criada quando passamos esse parâmetro na connection string. Abaixo temos um exemplo deste caso:
<connectionStrings>
<add name="TestVB1.Settings.Database1ConnectionString"
connectionString="Data Source=.\SQLEXPRESS;
AttachDbFilename=|DataDirectory|\Database1.mdf;
Integrated Security=True;
User Instance=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
Tecnologias usadas:
Visual Studio .NET 2005 (C#)
SQL Server 2005 Express Edition