Implementando Log de Acessos usando Herança com ASP.NET e C#
Além do site estar bonito e funcional, é muito importante que saibamos o quanto ele está sendo acessado.
O intuito desse Post é mostrar como fazer um log de acessos bem simples, e de forma bem rápida, usando Herança.
1° Passo : Criar a tabela que gravará os dados de acesso
Vamos chamá-la de LOG_ACESSOS e armazenar a DATA e alguns campos retornados pela coleção Request.ServerVariables.
A coleção ServerVariables contém informações do cliente e do servidor. Estas variáveis são criadas quando uma nova página é requisitada.
O uso das ServerVariables é bem útil caso necessitemos de informações do servidor ou do navegador do cliente.
E são esses dados que iremos gravar em nosso Log.
As variáveis da coleção você confere na lista abaixo :
ALL_HTTP - Lista informações tais como: host origem, página de origem, caracteristicas do navegador cliente, entre outras.
ALL_RAW - Retorna todas as informações enviadas por cabeçalho HTTP da página em seu estado original.
APPL_MD_PATH - Devolve o caminho lógico da página (path do metabase).
APPL_PHYSICAL_PATH - Caminho físico do arquivo no disco.
AUTH_TYPE - Método de autenticação utilizado pelo servidor para validar usuários que solicitam um script.
AUTH_USER - Nome de usuário fornecido ao servidor em caso de não permitir acesso anônimo ao diretório.
AUTH_PASSWORD - Senha referente ao usuário logado.
CERT_COOKIE - ID do certificado digital do cliente.
CERT_FLAGS - Valor de dois bits: bit 0 igual a 1 indica se o certificado do cliente está presente, bit 1 igual a 1,
indica que a autoridade de certificação do certificado do cliente é inválida.
CERT_ISSUER - Campo Issuer do certificado do cliente (O=MS, OU=IAS, CN=usuário, C=USA).
CERT_KEYSIZE - Número de bits na chave de conexão SSL.
CERT_SECRETKEYSIZE - Número de bits na chave do "Server certificate private"
CERT_SERIALNUMBER - Campo Serial Number do certificado digital do cliente.
CERT_SERVER_ISSUER - Campo Issuer do certificado do servidor.
CERT_SERVER_SUBJECT - Campo Subject do certificado SSL do servidor.
CERT_SUBJECT - Campo Subject do certificado do cliente.
CONTENT_LENGTH - Tamanho em bytes, do conteúdo de uma requisição request ao servidor.
CONTENT_TYPE - Tipo de requisição enviada ao servidor pelo cliente.
GATEWAY_INTERFACE - Tipo de interface CGI usada no servidor web para manipular a solicitação (request).
HTTP_NOMEHEADER - Valor armazenado no header especificado. Podemos destacar os mais comuns: HTTP_ACCEPT, HTTP_ACCEPT_ENCODING
HTTPS - Retorna à uma solicitação request, dizendo se o canal é seguro (ON) ou não seguro (OFF).
HTTPS_KEYSIZE - Número de bits na chave de conexão Secure Sockets Layer - SSL.
HTTPS_SECRETKEYSIZE - Número de bits na chaver do "server certificate private".
HTTPS_SERVER_ISSUER - Campo Issuer do certificado do servidor.
HTTPS_SERVER_SUBJECT - Campo subject do certificado do servidor.
INSTANCE_ID - ID, em formato texto, para a instância do Internet Information Server (IIS).
INSTANCE_META_PATH - Path do metabase para a instância do Internet Information Server (IIS) que responde à solicitação (request).
LOCAL_ADDR - Retorna o endereço lógico do servidor onde foi feito a requisição
LOGON_USER - Conta do usuário usada para efetuar o logon no Windows.
PATH_INFO - Caminho virtual do arquivo que realizou a requisição, apartir da raiz do servidor.
PATH_TRANSLATED - Versão da variável "PATH_INFO" transformado em path físico.
QUERY_STRING - Retorna as variáveis passadas por cabeçalho HTTP à uma página via QueryString.
REMOTE_ADDR - Retorna endereço IP da máquina cliente que requisitou algo ao servidor.
REMOTE_HOST - Nome do host que requisitou algo ao servidor.
REMOTE_USER - Nome da máquina que solicitou o arquivo asp ao servidor. Disponível somente se disponível.
REQUEST_METHOD - Método usado pela requisição ao servidor (GET ou POST)
SCRIPT_NAME - Path virtual do arquivo requisitado ao servidor.
SERVER_NAME - Nome do servidor onde foi realizada a requisição (pode ser retornado o endereço IP, DNS alias ou nome do host).
SERVER_PORT - Retorna o número da porta onde foi realizada a requisição ao servidor.
SERVER_PORT_SECURE - Diz se a porta onde é recebida as solicitações pelo servidor é segura (1) ou não segura (0).
SERVER_PROTOCOL - Retorna nome e versão respectivamente do protocolo usado pelo servidor WEB para interpretar a solicitação
Para fins didáticos, vamos escolher apenas algumas dessas variáveis em nossa tabela e além disso armazenar um ID como chave e a DATA da inserção
dos dados
CREATE TABLE DBO.LOG_ACESSOS
(
ID INT IDENTITY(1,1) NOT NULL,
DATA DATETIME NOT NULL,
QUERY_STRING VARCHAR(MAX) NULL,
REMOTE_ADDR VARCHAR(MAX) NULL,
REMOTE_HOST VARCHAR(MAX) NULL,
LOGON_USER VARCHAR(MAX) NULL
)
GO
ALTER TABLE DBO.LOG_ACESSOS ADD CONSTRAINT [PK_LOG_ACESSOS] PRIMARY KEY (ID)
GO
Com nossa tabela criada, faremos uma procedure de INSERT.
CREATE PROCEDURE DBO.INSERT_LOG_ACESSOS
(
@QUERY_STRING VARCHAR(MAX) = NULL,
@REMOTE_ADDR VARCHAR(MAX) = NULL,
@REMOTE_HOST VARCHAR(MAX) = NULL,
@LOGON_USER VARCHAR(MAX) = NULL
)
AS
BEGIN
INSERT INTO DBO.LOG_ACESSOS
(
DATA ,
QUERY_STRING ,
REMOTE_ADDR ,
REMOTE_HOST ,
LOGON_USER
)
VALUES
(
GETDATE() ,
@QUERY_STRING ,
@REMOTE_ADDR ,
@REMOTE_HOST ,
@LOGON_USER
)
END
2º Passo : Criar uma classe LogAcessos com um método que executa nossa procedure de INSERT.
public class LogAcessos
{
// Pega String de Conexão do Web.Config
private readonly string ConnectionString
= System.Web.Configuration.WebConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
// Construtor Padrão
public LogAcessos()
{ }
// Metodo que Grava os Acessos
public void GravaAcesso()
{
using (SqlConnection conexao = new SqlConnection(this.ConnectionString))
{
SqlCommand comando = new SqlCommand();
comando.Connection = conexao;
comando.CommandType = System.Data.CommandType.StoredProcedure;
comando.CommandText = "INSERT_LOG_ACESSOS";
comando.Parameters.AddWithValue("@QUERY_STRING",
HttpContext.Current.Request.ServerVariables["QUERY_STRING"].ToString() );
comando.Parameters.AddWithValue("@REMOTE_ADDR",
HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"].ToString() );
comando.Parameters.AddWithValue("@REMOTE_HOST",
HttpContext.Current.Request.ServerVariables["REMOTE_HOST"].ToString() );
comando.Parameters.AddWithValue("@LOGON_USER",
HttpContext.Current.Request.ServerVariables["LOGON_USER"].ToString() );
try {
conexao.Open();
int i = comando.ExecuteNonQuery();
comando.Dispose();
}
catch (SqlException sql)
{
// trata Exception SQL
}
catch (Exception ex)
{
// trata Exception Genérica
}
finally {
if (conexao.State == System.Data.ConnectionState.Open)
{
conexao.Close();
}
}
}
}
}
3º Passo : Criar classe herdando de System.UI.Page, sobrescrever o OnLoad,
instanciar nossa classe LogAcessos e executar o método GravaAcesso()
public class BaseForm : System.Web.UI.Page
{
protected override void OnLoad(EventArgs e)
{
LogAcessos log = new LogAcessos();
log.GravaAcesso();
log = null;
base.OnLoad(e);
}
}
4º Passo : Ao criar as páginas, fazer com que herdem da nossa classe BaseForm
public partial class _Default : BaseForm
{
protected void Page_Load(object sender, EventArgs e)
{
}
}
Dessa forma, sempre que suas páginas carregarem, elas executarão o método
GravaAcesso, gravando na nossa tabela LOG_ACESSOS os dados que definimos
Espero que seja útil
[]s
Classe SqlConnection
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.aspx
Classe SqlCommand
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.aspx
Request.ServerVariables Collection
http://msdn.microsoft.com/en-us/library/ms525396.aspx
Inheritance (C# Programming Guide)
http://msdn.microsoft.com/en-us/library/ms173149.aspx
Função Split no SQL 2005 - Recursividade usando Common Table Expressions ( CTE )
Imagine que você tenha um aplicativo de pesquisa Online de múltipla escolha,
que armezene o ID do usuário e as respostas, uma ao lado da outra, no mesmo campo, delimitadas por "|".
Agora você precisa extrair os dados dessa coluna de forma que cada resposta apareca em uma linha,como na imagem abaixo :

Vamos iniciar a criação do nosso ambiente.1o nossa tabela PESQUISA,
que terá um campo ID ( Chave ) e um campo RESPOSTAS
CREATE TABLE DBO.PESQUISA
(
ID INT IDENTITY(1,1) NOT NULL,
RESPOSTAS VARCHAR(8000) NOT NULL
)
GO
ALTER TABLE DBO.PESQUISA ADD CONSTRAINT [PK_PESQUISA] PRIMARY KEY( ID )
GO
Vamos popular nossa tabela para simular alguns cadastros :
INSERT INTO DBO.PESQUISA ( RESPOSTAS )
SELECT 'a | b | c' UNION ALL
SELECT 'b | c' UNION ALL
SELECT 'a | c' UNION ALL
SELECT 'c' UNION ALL
SELECT 'a | b' UNION ALL
SELECT 'a' UNION ALL
SELECT 'b'
GO
Com nosso ambiente montado, agora usaremos Commom Table Expression
para criar nosso "Split" de dados na coluna RESPOSTAS,
separando os dados a cada "|" e mostrando cada um em uma linha:
DECLARE @delimitador VARCHAR(3)
SET @delimitador = '|'
;WITH SqlSplit(id, i, j, respostas)
AS(
SELECT id, i = 1, j = CHARINDEX(@delimitador, RESPOSTAS + @delimitador), respostas
FROM DBO.PESQUISA
UNION ALL
SELECT id, i = j + 1, j = CHARINDEX(@delimitador, RESPOSTAS + @delimitador, j + 1), respostas
FROM SqlSplit
WHERE CHARINDEX(@delimitador, RESPOSTAS + @delimitador, j + 1) <> 0
)
SELECT ID, LTRIM(RTRIM(SUBSTRING(RESPOSTAS, i, j-i))) RESPOSTAS
FROM SqlSplit
WHERE SUBSTRING(RESPOSTAS, i, j-i) <> ''
ORDER BY id Nosso resultado :
Espero que seja útil
[]s
Recursive T-SQL Split Function
http://www.kodyaz.com/articles/sql-server-t-sql-split-function.aspx
WITH common_table_expression ( Transact-SQL )
http://msdn.microsoft.com/pt-br/library/ms175972.aspx
CHARINDEX ( Transact-SQL )
http://msdn.microsoft.com/en-us/library/ms186323.aspx
SUBSTRING ( Transact-SQL )
http://msdn.microsoft.com/pt-br/library/ms187748.aspx
Simples Inventário de Máquinas com VBScript
Mais um post de WMI e VBScript. É um script bem simples que usa a Classe Win32_OperatingSystem pra retornar informações da máquina como Sistema Operacional, Service Pack, Serial.
Usando a criatividade, podemos evoluir nele e buscar esses dados de todas as máquinas de nossa rede, tendo assim um inventário de máquinas atualizado de forma bem prática.
Segue :
' "." para computador local ou o nome do computador remoto
strComputer = "."
' Objeto WMI
Set objWMIService = GetObject("winmgmts:\\" _
& strComputer & "\root\cimv2")
' Classe Win32_OperatingSystem
Set colItems = objWMIService.ExecQuery _
("Select * from Win32_OperatingSystem")
' Loop
For Each objItem in colItems
WScript.Echo "Computador: " & objItem.CSName & VbCr & _
"----------------------------------------------------" & vbCr & _
"Fabricante: " & objItem.Manufacturer & VbCr & _
"Sistema Operacional: " & objItem.Caption & VbCr & _
"Versão: " & objItem.Version & VbCr & _
"Service Pack: " & objItem.CSDVersion & VbCr & _
"CodeSet: " & objItem.CodeSet & VbCr & _
"CountryCode: " & objItem.CountryCode & VbCr & _
"OSLanguage: " & objItem.OSLanguage & VbCr & _
"SerialNumber: " & objItem.SerialNumber
Next
' Finalizando o objeto
Set objWMIService = Nothing
Nosso retorno :

Espero que seja útil
[]s
Recuperando Informações da BIOS com Windows Management Instrumentation e VBScript
Windows Management Instrumentation, ou simplesmente WMI, é uma biblioteca cujas classes nos permitem obter uma série de informações sobre o sistema.
Vou postar um exemplo simples onde recuperamos alguns dados da BIOS usando a classe Win32_BIOS com VBScript.
'-- Nome do computador remoto, ou simplesmente "." para local
strComputer = "."
'-- Objeto WMI
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
'-- Classe Win32_BIOS
Set colBIOS = objWMIService.ExecQuery _
("Select * from Win32_BIOS")
'-- Loop nas propriedades
For each objBIOS in colBIOS
Wscript.Echo "Build Number: " & objBIOS.BuildNumber & vbCrLf & _
"Current Language: " & objBIOS.CurrentLanguage & vbCrLf & _
"Installable Languages: " & objBIOS.InstallableLanguages & vbCrLf & _
"Manufacturer: " & objBIOS.Manufacturer & vbCrLf & _
"Name: " & objBIOS.Name & vbCrLf & _
"Primary BIOS: " & objBIOS.PrimaryBIOS & vbCrLf & _
"Release Date: " & objBIOS.ReleaseDate & vbCrLf & _
"Serial Number: " & objBIOS.SerialNumber & vbCrLf & _
"SMBIOS Version: " & objBIOS.SMBIOSBIOSVersion & vbCrLf & _
"SMBIOS Major Version: " & objBIOS.SMBIOSMajorVersion & vbCrLf & _
"SMBIOS Minor Version: " & objBIOS.SMBIOSMinorVersion & vbCrLf & _
"SMBIOS Present: " & objBIOS.SMBIOSPresent & vbCrLf & _
"Status: " & objBIOS.Status & vbCrLf & _
"Version: " & objBIOS.Version
Next
'-- Finaliza objeto WMI
Set objWMIService = Nothing
Salvando o código acima em um arquivo .vbs, ao executá-lo, o resultado é o da imagem abaixo :

Espero que seja útil
[]s
Pesquisando por Similaridade com as funções SOUNDEX e DIFFERENCE do SQL Server
Imagine a seguinte situação :
Você manda periodicamente um informativo por email para todos os contatos cadastrados em uma tabela de contatos, com nome e email. Só que você tem notado que muitos emails voltam por erro de digitação na hora do cadastro, produzindo algo como HOITMAIL ao invéz de HOTMAIL por exemplo. Você precisa corrigir isso, mas como fazer ?
Há duas funções no SQL que podem nos ajudar muito nesse caso. SOUNDEX e DIFFERENCE.
Ambas avaliam semelhança de strings, mas vamos entender melhor na prática.
Vamos criar a Tabela de Contatos
CREATE TABLE DBO.CONTATOS
(
ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
NOME VARCHAR(100) NOT NULL,
EMAIL VARCHAR(100) NOT NULL
)
GO
Inserir os Dados Simulando o Problema
INSERT INTO DBO.CONTATOS( NOME, EMAIL )
-- HOTMAIL SEM ERRO DE DIGITAÇÃO
SELECT 'MARCELO', 'MARCELO@HOTMAIL.COM' UNION ALL
-- AQUI INSERIMOS 4 VARIAÇÕES COM ERRO DE CONTAS HOTMAIL,
-- HOTMAL, HOITMAIL, HOITMAI e HOTMAIL.COM.BR
SELECT 'JOAO', 'JOAO@HOTMAL.COM' UNION ALL
SELECT 'MARIA', 'MARIA@HOITMAIL.COM' UNION ALL
SELECT 'JOSE', 'JOSE@HOITMAI.COM' UNION ALL
SELECT 'ANTONIO', 'ANTONIO@HOTMAIL.COM.BR' UNION ALL
-- AQUI MAIS 3 CONTAS DE OUTROS PROVEDORES PRA SIMULAR
-- VARIEDADE DE DOMINIOS
SELECT 'FULANO' , 'FULANO@GMAIL.COM' UNION ALL
SELECT 'BELTRANO' , 'BELTRANO@TERRA.COM.BR' UNION ALL
SELECT 'CICLANO' , 'CICLANO@YAHOO.COM'
GO
Depois disso, teremos nossa tabela populada como na imagem abaixo :
Com todo o ambiente montado, agora podemos fazer nossas buscas usando SOUNDEX e DIFFERENCE. Lembrando que nosso objetivo é trazer os emails HOTMAIL e as variações causadas por erros de digitação como HOITMAIL, HOTMAL e HOTMAIL.COM.BR para que possamos corrigir a base e diminuir os emails retornados.
Usando SOUNDEX
A sintaxe é essa :
SELECT * FROM TABELA
WHERE SOUNDEX( COLUNA ) = SOUNDEX( 'VALOR A COMPARAR' )
Para o nosso caso fica assim :
SELECT * FROM DBO.CONTATOS
WHERE SOUNDEX(
RIGHT( EMAIL, LEN( EMAIL ) - CHARINDEX('@', EMAIL ) )
) = SOUNDEX( 'HOTMAIL.COM' )
No Script acima, RIGHT( EMAIL, LEN( EMAIL ) - CHARINDEX('@', EMAIL ) )
nos retorna a parte do email após o @, e comparamos isso a HOTMAIL.COM
O resultado será o seguinte :
Repare que conseguimos o email correto e as variações com erros de digitação. Com isso podemos efetuar a correção com um UPDATE simples, fazendo uso da função REPLACE.
UPDATE DBO.CONTATOS
SET EMAIL = REPLACE( EMAIL,
RIGHT( EMAIL, LEN( EMAIL ) - CHARINDEX('@', EMAIL ) ),
'HOTMAIL.COM')
FROM DBO.CONTATOS
WHERE SOUNDEX(
RIGHT( EMAIL, LEN( EMAIL ) - CHARINDEX('@', EMAIL ) )
) = SOUNDEX( 'HOTMAIL.COM' )
Após rodar nosso UPDATE, teremos os 4 registros acertados
Vamos ao mesmo exemplo usando a função DIFFERENCE.
A sintaxe :
SELECT * FROM TABELA
WHERE DIFFERENCE( COLUNA, 'VALOR A COMPARAR' )
= NIVEL DE SIMILARIDADE
O nível de similaridade é um inteiro e o valor máximo é 4 e o mínimo é 0.
No nosso exemplo :
SELECT *
FROM DBO.CONTATOS
WHERE DIFFERENCE(
RIGHT( EMAIL, LEN( EMAIL ) - CHARINDEX( '@', EMAIL ) )
, 'HOTMAIL.COM' ) = 4
Obtemos o mesmo resultado que usando SOUNDEX.

Podendo também fazer um UPDATE com REPLACE pra corrigir os erros de digitação.
UPDATE DBO.CONTATOS
SET EMAIL = REPLACE(
EMAIL,
RIGHT( EMAIL, LEN( EMAIL ) - CHARINDEX('@', EMAIL ) ),
'HOTMAIL.COM'
)
FROM DBO.CONTATOS
WHERE DIFFERENCE(
RIGHT( EMAIL, LEN( EMAIL ) - CHARINDEX( '@', EMAIL ) )
, 'HOTMAIL.COM' ) = 4
Resultados corrigidos :
Lembrando que o 4 é o nível máximo de similaridade, podendo ser alterado pra que a busca fique mais flexível.
Espero que seja útil.
Aguardem os próximos artigos de SQL.
[]s
Artigo para download em DOC e PDF
REPLACE (Transact-SQL)
http://msdn.microsoft.com/en-us/library/ms186862.aspx
RIGHT (Transact-SQL)
http://msdn.microsoft.com/en-us/library/ms177532.aspx
SOUNDEX (Transact-SQL)
http://msdn.microsoft.com/en-us/library/ms187384.aspx
DIFFERENCE (Transact-SQL)
http://msdn.microsoft.com/en-us/library/ms188753.aspx