Um stored procedure (procedimento armazenado) é um conjunto de instruções em linguagem Transact-SQL, guardadas no servidor de forma pré-compilada. A execução pode ser feita dentro do próprio SQL ou através de uma aplicação externa, como é p caso do Visual Studio.
Os stored procedures têm algumas vantagens em relação aos comandos directos ao servidor, como é o caso de modularidade (simples de modificar sem alterar o código da aplicação), rapidez (ficam pré-compilados e ficarem em cache), entre outras, mas gostava de destacar a segurança e na protecção a ataques denominados SQL Injection.
Os SQL injection são instruções que manipulam a query, alterando a entrada de dados no servidor, através de uma aplicação externa. São uma das maiores causas de falhas de segurança das aplicações e podem destruir toda a estrutura de dados.
Para uma melhor percepção da importância dos stored procedures e da protecção aos SQL Injection, recomendo a visualização de um webcast gratuito, disponível na MSDN Brasil de Luciano Moreira: https://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?culture=pt-BR&EventID=1032383827&CountryCode=BR
A utilização de stored procedures em Visual Basic.Net é bastante simples. Cria-se a tabela, depois um novo stored procedure e para finalmente o código.
O seguinte exemplo mostra como criar um stored procedure e como é simples, através do código, mostrar, inserir, modificar ou apagar dados. Não serão mostrados todos os exemplos, uma vez que as principais diferenças estão nos comandos T-SQL e não propriamente no código.
Para o seguinte exemplo foi criada uma tabela simples, com o nome produtos, com três campos apenas:
ID INT (primary key)
codigo INT
produto VARCHAR(50)
Um exemplo simples de um stored procedure para listar todos os dados da tabela produtos seria:
CREATE PROCEDURE dbo.sp_produtos
AS
BEGIN
SELECT * FROM produtos;
END
Após a gravação do stored procedure a instrução CREATE passa a ALTER, porque o que será feito posteriormente é uma alteração ao procedimento e não uma criação. Para o exemplo será feito um stored procedure um pouco mais complexo, que permita a pesquisa por código ou por produto (ou parte do nome).
/*
Stored Procedure que permite a pesquisa por
código ou nome (ou parte do nome) do produto
*/
CREATE PROCEDURE dbo.sp_produtos
/** Indicação dos parâmetros de entrada com um valor por defeito
o que permite que não sejam indicados através do código */
@codigo INT = NULL,
@produto VARCHAR(50) = NULL
AS
BEGIN
SELECT *
FROM produtos
-- Definição do filto da pesquisa
WHERE (@codigo IS NULL OR codigo = @codigo)
AND (@produto IS NULL OR produto LIKE '%'+ @produto + '%');
END
Depois no código de Visual Basic.Net:
' Informação da Connection String
Dim myConnectionString As String = _
"Data Source=.\SQLEXPRESS;AttachDbFilename='c:\myDatabase.mdf';Integrated Security=True;User Instance=True"
' Ligação à base de dados
Using connection As New SqlConnection(myConnectionString)
connection.Open()
' Indica o nome do Stored Procedure e que o tipo do comando
Dim command As New SqlCommand("sp_produtos", connection)
command.CommandType = CommandType.StoredProcedure
' Caso o código tenha sido indicado
If txtCodigo.Text <> String.Empty Then
command.Parameters.Add("@codigo", SqlDbType.Int).Value = txtCodigo.Text
End If
' Caso o produto tenha sido indicado
If txtProduto.Text <> String.Empty Then
command.Parameters.Add("@produto", SqlDbType.VarChar).Value = txtProduto.Text
End If
' Executa o comando e guarda o resultado no SqlDataReader
Dim reader As SqlDataReader = command.ExecuteReader()
' Caso tenha resultados
If reader.HasRows Then
' Ciclo que irá mostrar os produtos encontrados
While reader.Read
Dim result As String = _
String.Format("{0}-{1}-{2}", reader.Item(0), reader.Item(1), reader.Item(2))
Debug.WriteLine(result)
End While
Debug.WriteLine("Fim da pesquisa!") ' Informação no fim da pesquisa
Else
' Caso não tenham sido encontrados produtos ...
Debug.WriteLine("Não foram encontrados produtos!")
End If
reader.Close()
reader = Nothing
End Using 'connection
Este exemplo mostra como listar (na Immediate Window) os produtos de uma tabela, permitindo ou não, indicar dados de modo a estabelecer um filtro de pesquisa. Para inserir dados, modificar ou mesmo apagar é necessário adequar o stored procedure e o código é bastante semelhante, como já mostrado em artigos anteriores.
Este artigo pretende apenas mostrar, de uma forma geral, alguns dos benefícios dos stored procedures e a sua importância na melhoria de performance e segurança dos dados.
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!
Tratamento de Erros
Teoricamente é possível criar uma aplicação em que todos os erros são tratados de modo a não acontecerem, ou seja, nunca existirem. Esta teoria na realidade não acontece por vários motivos, sendo como principal o facto do programador não contemplar ou não conseguir contemplar todos os possíveis problemas durante o desenvolvimento.
Uma correcta gestão dos erros na aplicação evita mensagens difíceis de analisar, aplicações a fecharem inesperadamente e torna, obviamente, as aplicações mais profissionais e robustas.
Com a introdução do .NET apareceu um novo método em substituição ao On Error Go To <label>. O método Try … Catch … End Try permite criar um bloco no código onde o erro é verificado e tratado. É simples de implementar, muito eficiente e com diversas variações que dão uma maior flexibilidade. Num exemplo simples funciona desta forma:
Try
' código do programa
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
No entanto existem mais variações que permitem uma melhor gestão dos erros. São o caso de instruções com o Finally, que permite que o código execute sempre, ou seja, caso exista ou não erro, e o When, que permite decidir se o erro é ou não “agarrado”.
Além disso pode-se a qualquer momento dizer que ocorreu um erro e forçar a mensagem sem duplicar código, através da instrução Throw.
Eis um pequeno exemplo, com a alteração de um valor ou outro, permite alterar a forma como a mensagem/erro é tratado, e mostra alguns métodos que é possível implementar:
Sub CalculaResultado(ByVal valor1 As Integer, ByVal valor2 As Integer, ByVal ignoraErro As Boolean)
Try
' Verifica se os valores são zero
If valor1 = 0 AndAlso valor2 = 0 Then
' Define uma nova ArgumentNullException
Dim msgErro As String = "Os valores a calcular não podem ser os dois 0"
Throw New ArgumentNullException(msgErro, New Exception(String.Empty))
End If
' Caso o valor 1 seja zero, vai ser originada uma
' ArithmeticException devido à divisão por zero
Dim valorfinal As Integer = valor2 / valor1
' Neste exemplo caso o resultado seja menor que 10
' origina uma Exception simples
If valorfinal < 10 Then
Throw New Exception("O valor final não é valido")
End If
Catch ex As ArithmeticException When ignoraErro = False
Debug.WriteLine("Não é permitida uma divisão de um valor por zero")
Catch ex As ArgumentNullException When ignoraErro = False
Debug.WriteLine(ex.Message)
Catch ex As Exception
Debug.WriteLine(ex.Message)
Finally
Debug.WriteLine("Fim do exemplo!")
End Try
End Sub
E alguns resultados:
' Calcula o resultado com números a zero
CalculaResultado(0, 0, False)
Os valores a calcular não podem ser os dois 0
Fim do exemplo!
' Provoca uma divisão por zero
CalculaResultado(0, 50, False)
Não é permitida uma divisão de um valor por zero
Fim do exemplo!
' Provoca uma divisão por zero, mas não trata o erro
CalculaResultado(0, 50, True)
Arithmetic operation resulted in an overflow.
Fim do exemplo!
' Calcula onde o resultado é inferior a 10
CalculaResultado(45, 50, False)
O valor final não é valido
Fim do exemplo!
Inserir Imagens no SQL Server
Em dois artigos anteriores, foram abordadas algumas formas de trabalhar com dados num servidor SQL. Gestão de Dados em SQL Server - Parte I e Gestão de Dados em SQL Server - Parte II mostraram como inserir, modificar, apagar e ler dados, mas no entanto podem-se também inserir imagens e não basta simplesmente indicar qual é a imagem.
Existem duas alternativas para a gestão de imagens: guardar apenas a localização e colocar a imagem no servidor ou gravar a imagem directamente num campo.
Para inserir a imagem é necessário ler, gravar num array de bytes e finalmente utilizar esse array para gravar na base de dados.
Eis um exemplo de como o fazer:
' Define a Connection String
Dim myConnectionString As String = _
"Data Source=.\SQLEXPRESS;AttachDbFilename='c:\myDatabase.mdf';Integrated Security=True;User Instance=True"
Try
' Cria um novo FileStream para leitura da imagem
Dim fs As New IO.FileStream("c:\image.jpg", IO.FileMode.Open, IO.FileAccess.Read)
' Cria um array de Bytes do tamanho do FileStream
Dim ImageData(fs.Length() - 1) As Byte
' Lê os bytes do FileStream para o array criado
fs.Read(ImageData, 0, ImageData.Length)
' Fecha o FileStream ficando a imagem guardada no array
fs.Close()
Using connection As New SqlClient.SqlConnection(myConnectionString)
' Define o comando Transact-SQL para inserir dados
Dim SQL As String = "INSERT INTO contacts ([name],[img]) VALUES (@name,@img);"
Dim command As New SqlClient.SqlCommand(SQL, connection)
' Define os parâmetros para a inserção de dados, onde está o array
' de bytes(imagem) a ser inserida. O tipo do campo é Image
command.Parameters.Add("@name", SqlDbType.VarChar).Value = "jpaulino"
command.Parameters.Add("@img", SqlDbType.Image).Value = ImageData
connection.Open()
' Insere os campos no SQL
command.ExecuteNonQuery()
End Using
Catch ex As Exception
MessageBox.Show(ex.Message, My.Application.Info.Title, MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!
No VB.NET não está disponível uma label que permita criar um sombreado no texto de modo a tornar-lo com mais destaque. Esta propriedade é muito comum em ambiente Office, em programas como o Excel, Word, etc, mas está disponível também em muitos outros programas.
Para criar uma label que permita criar um sombreado é preciso desenhar o texto através do namespace System.Drawing. O conceito é bastante simples: a label escreve um texto, que será a sombra, e através do evento onPaint() desenha-se o texto que fica por cima.
Depois é escondida a propriedade ForeColor (que define a cor do texto) porque esta não será a cor do texto mas sim da sombra e são criadas algumas novas propriedades:
- OffsetX (define o offset, ou desvio, na vertical face ao texto inicial)
- OffsetY (define o offset, ou desvio, na horizontal face ao texto inicial)
- ForeColorFront (define a cor que fica por cima)
- ForeColorBack (define a cor que fica por baixo – sombra)
É necessário então criar uma nova classe e utilizar o seguinte código:
Imports System.Drawing
Imports System.ComponentModel
Public Class ShadowLabel
Inherits Label
#Region "Properties"
Private m_offsetX As Integer
Private m_offsetY As Integer
Private m_ForeColorFront As Color
''' <summary>
''' Definição do offset em X
''' </summary>
<DefaultValue(0)> _
<Category("Shadow")> _
<Description("Definição do offset em X")> _
Public Property OffsetX() As Integer
Get
Return m_offsetX
End Get
Set(ByVal value As Integer)
m_offsetX = value
Me.Invalidate()
End Set
End Property
''' <summary>
''' Definição do offset em Y
''' </summary>
<DefaultValue(-1)> _
<Category("Shadow")> _
<Description("Definição do offset em Y")> _
Public Property OffsetY() As Integer
Get
Return m_offsetY
End Get
Set(ByVal value As Integer)
m_offsetY = value
Me.Invalidate()
End Set
End Property
''' <summary>
''' Definição da cor da frente
''' </summary>
<DefaultValue(GetType(Color), "RoyalBlue")> _
<Category("Shadow")> _
<Description("Definição da cor da frente")> _
Public Property ForeColorFront() As Color
Get
Return m_ForeColorFront
End Get
Set(ByVal value As Color)
m_ForeColorFront = value
Me.Invalidate()
End Set
End Property
''' <summary>
''' Definição da cor de sombreado
''' </summary>
<DefaultValue(GetType(Color), "White")> _
<Category("Shadow")> _
<Description("Definição da cor de sombreado")> _
Public Property ForeColorBack() As Color
Get
Return Me.ForeColor
End Get
Set(ByVal value As Color)
Me.ForeColor = value
Me.Invalidate()
End Set
End Property
' Serve apenas para definir que a propriedade ForeColor
' não está disponível para alterar no editor e no design
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never)> _
Public Overrides Property ForeColor() As System.Drawing.Color
Get
Return MyBase.ForeColor
End Get
Set(ByVal value As System.Drawing.Color)
MyBase.ForeColor = value
End Set
End Property
#End Region
' Define algumas propriedades por defeito
Public Sub New()
' Definição dos valores iniciais de offset
Me.OffsetX = 0
Me.OffsetY = -1
' Definição das cores iniciais do texto
Me.ForeColorFront = Color.RoyalBlue
Me.ForeColorBack = Color.White
End Sub
' No evento Paint desenha o texto
Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
MyBase.OnPaint(e)
' Desenha o texto que fica na frente
Dim BackBrush As New SolidBrush(Me.ForeColorFront)
e.Graphics.DrawString(Me.Text, Me.Font, BackBrush, OffsetX, OffsetY, StringFormat.GenericDefault)
BackBrush.Dispose()
End Sub
End Class
No final é preciso compilar a aplicação e o controlo estará disponível na toolbar.
Este é um exemplo que pretende mostrar como criar uma label com uma sombra, mas também como utilizar algumas funcionalidades do namespace Drawing.
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!
A segurança de dados é algo fundamental em qualquer aplicação e cada vez mais é um motivo de preocupação. Quando se guarda determinado tipo de informação, numa base de dados por exemplo, é importante que a mesma esteja segura, principalmente em alguns campos como é o caso de passwords, números de cartões de crédito, informações confidenciais, etc.
É aí que a criptografia entra, através da encriptação ou codificação de dados. A encriptação é a transformação de informação electrónica de texto legível em texto incompreensível, através de um cifra, também conhecido por algoritmo.
Este processo é bastante complexo mas a .Net Framework através da Namespace System.Security.Cryptography simplifica bastante, tornando-o acessível e fácil de utilizar. Existem diversas classes disponíveis que implementam diferentes tipos de encriptação, como é o caso de: AES (Advanced Encryption Standard), DES (Data Encryption Standard), RC2 (Rivest Cipher), Rijndael, TripleDES (Triple Data Encryption Standard), entre outras.
De entre todos os métodos de encriptação disponíveis, alguns são mais seguros do que outros, como é o caso do Rijndael ou do AES. No entanto e para este exemplo, será mostrado a utilização do TripleDES, por ser bastante simples de implementar.
Como curiosidade o TripleDES baseia-se no algoritmo DES que foi utilizado pelo governo norte-americano e que, actualmente, pode ser decifrado. Posteriormente foi desenvolvida uma versão melhorada onde o algoritmo corre três vezes com duas chaves simétricas: o TripleDES.
Para a implementação de uma encriptação utilizando o algoritmo TripleDES devemos criar a seguinte classe:
Imports System.Text
Imports System.Security.Cryptography
''' <summary>
''' Encriptação simples usando TripleDES
''' (Triple Data Encryption Standard)
''' </summary>
Public Class Crypto
Private Shared TripleDES As New TripleDESCryptoServiceProvider
Private Shared MD5 As New MD5CryptoServiceProvider
' Definição da chave de encriptação/desencriptação
Private Const key As String = "CHAVE12345teste"
''' <summary>
''' Calcula o MD5 Hash
''' </summary>
''' <param name="value">Chave</param>
Public Shared Function MD5Hash(ByVal value As String) As Byte()
' Converte a chave para um array de bytes
Dim byteArray() As Byte = ASCIIEncoding.ASCII.GetBytes(value)
Return MD5.ComputeHash(byteArray)
End Function
''' <summary>
''' Encripta uma string com base em uma chave
''' </summary>
''' <param name="stringToEncrypt">String a encriptar</param>
Public Shared Function Encrypt(ByVal stringToEncrypt As String) As String
Try
' Definição da chave e da cifra (que neste caso é Electronic
' Codebook, ou seja, encriptação individual para cada bloco)
TripleDES.Key = Crypto.MD5Hash(key)
TripleDES.Mode = CipherMode.ECB
' Converte a string para bytes e encripta
Dim Buffer As Byte() = ASCIIEncoding.ASCII.GetBytes(stringToEncrypt)
Return Convert.ToBase64String(TripleDES.CreateEncryptor().TransformFinalBlock(Buffer, 0, Buffer.Length))
Catch ex As Exception
MessageBox.Show(ex.Message, My.Application.Info.Title, MessageBoxButtons.OK, MessageBoxIcon.Error)
Return String.Empty
End Try
End Function
''' <summary>
''' Desencripta uma string com base em uma chave
''' </summary>
''' <param name="encryptedString">String a decriptar</param>
Public Shared Function Decrypt(ByVal encryptedString As String) As String
Try
' Definição da chave e da cifra
TripleDES.Key = Crypto.MD5Hash(key)
TripleDES.Mode = CipherMode.ECB
' Converte a string encriptada para bytes e decripta
Dim Buffer As Byte() = Convert.FromBase64String(encryptedString)
Return ASCIIEncoding.ASCII.GetString(TripleDES.CreateDecryptor().TransformFinalBlock(Buffer, 0, Buffer.Length))
Catch ex As Exception
MessageBox.Show(ex.Message, My.Application.Info.Title, MessageBoxButtons.OK, MessageBoxIcon.Error)
Return String.Empty
End Try
End Function
End Class
Depois, para a implementar na aplicação, é só necessário
Dim source As String = "vbtuga"
' Mostra o texto original
Debug.WriteLine("Texto original : " & source)
' Encripta a string
Dim result As String = Crypto.Encrypt(source)
' Mostra o resultado da encriptação
Debug.WriteLine("Texto encriptado: " & result)
' Decripta e mostra o texto original
Debug.WriteLine("Texto decriptado: " & Crypto.Decrypt(result))
Neste pequeno teste, o resultado é o seguinte (na Immediate Window):
Texto original : vbtuga
Texto encriptado: 6ldtC7GfpGg=
Texto decriptado: vbtuga
Este artigo é um exemplo de encriptação de forma a tornar os dados e as aplicações mais seguras. Brevemente, assim que possível, irei mostra mais dois exemplos de encryptação em AES e Rijndael.
Exemplo do artigo: DOWNLOAD DO FICHEIRO
PS: Como sempre, qualquer dúvida, comentário ou correcção ao artigo é sempre bem vinda!