Invoke or BeginInvoke cannot be called on a control until the window handle has been created
Ao tentar instalar o SQL Server 2008 Express em uma máquina com Windows 7, me retornou o seguinte erro :
“Invoke or BeginInvoke cannot be called on a control until the window handle has been created.”
Para contornar, basta executar a instalação novamente
Mais detalhes em :
http://blogs.msdn.com/petersad/archive/2009/08/14/invoke-or-begininvoke-cannot-be-called-on-a-control-until-the-window-handle-has-been-created.aspx
[]s
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
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
Impedir múltiplos logons em uma rede com Active Directory
Em parceria com o Portal Tecnologia, blog mantido pelo amigo Tiago Souza, Administrador de Redes, publicamos um passo a passo de como implementar uma funcionalidade similar a alguns softwares de mercado como o Limit Login da Microsoft, a limitação de multiplos logins de uma rede com Active Directory. Tudo feito de forma bem mais simples que os conhecidos softwares, mas muito prática e funcional.
Espero que seja útil.
Críticas e opiniões serão muito bem vindas, confiram!
http://www.portaltecnologia.net/2009/05/27/como-impedir-multiplos-logons-em-uma-rede-com-active-directory/
[]s