Marcelo Ramos

Desenvolvimento em .NET, Banco de Dados e Tecnologia em Geral

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

Posted: jun 09 2009, 22:42 by marcelo | Comentários (4163) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: ASP.NET | C# | SQL

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

Posted: jun 07 2009, 12:56 by marcelo | Comentários (4019) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: Dicas de Tecnologia | SQL

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

Posted: jun 06 2009, 17:24 by marcelo | Comentários (4443) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

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

 

Posted: jun 03 2009, 23:17 by marcelo | Comentários (3099) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

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
Download em DOC Download em 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

Posted: mai 30 2009, 22:12 by marcelo | Comentários (2057) RSS comment feed |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Filed under: Dicas de Tecnologia | SQL