HTTP 417 Expectation failed (falha de expectativa)

Dica rápida! Esse erro ocorre quando tenta fazer uma requisição por WebRequest, Web-Service ou outra requisição entre computadores. Para sanar o problema use a chamada antes de realizar o processo:

System.Net.ServicePointManager.Expect100Continue = false;

Para saber mais detalhes sobre erros HTTP 417, acesse aqui.

Localização de Usuário: Obter Cidade e Estado pelo IP em ASP.NET

A um certo tempo atrás isso era um pouco complicado de ser feito ou ficávamos dependentes (muito) de ferramentas de terceiros. Hoje, claramente, posso afirmar que temos mais flexibilidade porém continuamos atrelados. Mas não seja por isso! A Google disponibilizou uma API inovadora que nos ajuda nesse caso e em muitos outros (relativo ao GoogleMaps). O GoogleMaps.Subgurim.NET é um avançado controle do GoogleMaps para o ASP.NET e permite uma gama de manipulações sobre suas funcionalidades e a que iremos demonstrar aqui é a de obter informações do usuário pelo IP (no site deles pode encontrar bem como códigos avançados e demais exemplos). Em contrapartida, é preciso de um arquivo extra contendo faixas de IP distribuídas pelo mundo. Então a MaxMind disponibilizou também um arquivo binário contendo essas informações gratuitamente. Você pode obter o arquivo aqui ou aqui (a diferença é que um é o link direto e o outro é a página geral que pode-se obter em outros formatos). Esse arquivo é mensalmente atualizando inserindo novas faixas, logo, deixo o dica para dar sempre uma atualizada quando possível (com isso aumenta mais a precisão mas hoje, com esse arquivo, já realiza uma boa cobertura).
Para início dos trabalhos, baixe o componente GoogleMaps.Subgurim.NET e o arquivo de faixas GeoLiteCity.dat. Crie um novo Web Site. Adicione a GMaps.dll na pasta Bin do projeto e copie o GeoLiteCity.dat também (coloque em outra pasta afim de organizá-los - no meu caso coloquei em uma pasta chamada Data). Agora escreva o seguinte código abaixo:

using Subgurim.Controles;

private void ObtemInfoUser()
{
    string database = Server.MapPath("~/Data/GeoLiteCity.dat");
    LookupService servico = new LookupService(database);
    Location localizacao = servico.getLocation(Request.ServerVariables["REMOTE_ADDR"]);
    if (localizacao != null)
    {
        Response.Write("Cidade: " + localizacao.city + "<br />");
        Response.Write("País: " + localizacao.countryName + "<br />");
        Response.Write("Código do País: " + localizacao.countryCode + "<br />");
        Response.Write("Região: " + localizacao.region + "<br />");
        Response.Write("Código da Área: " + localizacao.area_code + "<br />");
        Response.Write("Latitude: " + localizacao.latitude + "<br />");
        Response.Write("Longitudade: " + localizacao.longitude);
    }
}
 
Você deve obter a saída algo do tipo:
 
Cidade: Salvador
País: Brazil
Código do País: BR
Região: 5
Código da Área: 0
Latitude: -12,7636
Longitude: -15,2821
 
* Obs: a latitude e longitude são dados fictícios que eu coloquei ;)
 
Se você estiver testando localmente, não terá resultado pois o Request.ServerVariables["REMOTE_ADDR"] irá trazer o valor 127.0.0.1. Para saber seu IP externo acesse o MeuIp e substitua-o para visualizar os valores e validá-los. Em breve estarei escrevendo outros artigos mostrando como aproveitar bem a API do GoogleMaps bem como demais funcionalidades. Até mais!

UPDATE: Após ler, vejam aqui sobre o plágio que fizeram com meu artigo e depois acusaram que eu que copiei. Quem lê o blog sabe como eu escrevo e como expresso minhas idéias...

Utilizando função de uma DLL Externa no SQL Server

Esse é um tópico que irá mostrar algo que definitivamente você já precisou mas não sabia como fazer e que facilitará (e muito) alguns trabalhos. Programadores que usam o Bando de Dados para programar sendo por Stored Procedure, Functions e Triggers já passaram por maus bocados para preparar um algoritmo que realizasse um determinado processo, mas que esse mesmo algoritmo é mais fácil de ser escrito (ou possuir funcionalidades que o SGDB não possui) em outra linguagem de programação. Por exemplo: validar e-mail. É possível no SQL Server fazermos um Function que receba um VARCHAR e retorne um BIT informando que um e-mail é válido ou não, contudo seria uma função um tanto que trabalhosa para quem não está familiarizado com programação em banco. Para quem desenvolve em ASP.NET (C# ou VB.NET) basta usarmos expressão regular que, em duas linhas, resolve o problema. Portanto iremos utilizar uma função escrita em C# compilada em uma DLL e usarmos em um Function no SQL Server. Isso é possível através do CLR.
Primeiramente vamos criar a DLL contendo a função (no nosso exemplo vamos fazer a que valida e-mail - mas você pode desenvolver a que quiser). Abra o Visual Studio e crie um Project. Em Project Type escolha Visual C# Windows e o Template opte por uma Class Libary. Note que quando você cria uma classe vem com o nome Class1.cs. Para facilitar a compreensão, renomeie para FuncoesSql.cs (ou outra) e remova o namespace criado. Agora, escreva a função que deseja e adicione os namespaces necessários ficando da seguinte forma:

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.SqlServer.Server;

public partial class FuncoesSql
{
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlBoolean IsMailValido(SqlString email)
    {
        // Cria um objeto de expressões regulares para validar e-mail
        Regex expressaoRegular = new Regex(@"^(([^<>()[\]\\.,;:\s@\""]+"
        + @"(\.[^<>()[\]\\.,;:\s@\""]+)*)(\"".+\""))@"
        + @"((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
        + @"\.[0-9]{1,3}\])(([a-zA-Z\-0-9]+\.)+"
        + @"[a-zA-Z]{2,}))$");

        // Realiza um teste na validação da expressão
        return new SqlBoolean(expressaoRegular.IsMatch(email.ToString()));
    }
}

No caso, precisamos explicitar os tipos de entrada e saída utilizados pelo banco de dados para que não haja problemas de tipagem. Poderia ser qualquer função ou quantas que quisesse, contanto que respeite os tipos. Agora compile a DLL pressionando F6 ou pelo menu Build. Já temos a DLL com a função. Copie a DLL (que está na pasta bin\Debug de sua solução) e coloque-a em um local onde seu Banco de Dados possa buscá-la. Para exemplo, colocarei em C:\SqlDlls\ no Servidor.
O próximo passo é verificar se no SQL Server está ativo a funcionalidade CLR, para isso vá em Iniciar > Todos os Programas > Microsft SQL Server 2008 (ou 2005) > Configuration Tools > SQL Server Surface Area Configuration. Entre em Surface Area Configuration for Features e na guia CLR Integration verifique se a função está habilitada. Se não estiver, habilite.


Dê OK e agora já poderá usar CLR no SQL Server. Abra o SQL Server Management Studio, conecte-se na base desejada e execute as seguintes instruções SQL (abra um New Query para isso):

-- CRIA O ASSEMBLY INDICANDO SUA ORIGEM
CREATE ASSEMBLY FuncoesSql FROM 'C:\SqlDlls\FuncoesSqlServerASPNET.dll'
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- CRIA A FUNÇÃO
CREATE FUNCTION dbo.Fun_Valida_Mail (@email NVARCHAR(350))
RETURNS BIT
-- INDICA A ORIGEM DA FUNÇÃO SQL: ASSEMBLY > CLASSE > FUNÇÃO
AS EXTERNAL NAME [FuncoesSql].FuncoesSql.IsMailValido
GO

Agora execute a Query (ou pressione F5)... Foi criada a função! Pronto... agora é só usar como se fosse uam função do SQL Server normalmente. Abaixo tem alguns exemplos de consulta e seus retornos:

Execuções:
SELECT dbo.Fun_Valida_Mail('tmarcal@gmail')
SELECT dbo.Fun_Valida_Mail('tmarcal@gmail.com')
SELECT dbo.Fun_Valida_Mail('tmarcal.. @gmail.com')
 
Retorno:
0
1
0
 
Apesar do trabalho inicial, pode-se aproveitar muito de ambos os recursos e criar cada vez mais funções complexas. Quanto ao desempenho da CLR, em comparação ao T-SQL, deve-se ficar a mesma coisa. A diferença é praticamente imperceptível. Espero que tenham gostado... Até breve!

Não foi possível carregar arquivo ou assembly System.Web.Extensions, Version=2.0.0.0

Dica rápida para quem tiver esse erro...
Acontece quando está utilizando uma versão do ASP.NET Ajax diferente do Framework da aplicação. Muitas vezes, também, acontece quando está utilizando o Crystal Report ao renderizar um relatório. Então é exibido a seguinte mensagem:
A solução é simples: basta remover a System.Web.Extensions.dll e/ou System.Web.Extensions.Design.dll do projeto (na pasta Bin).
Não foi possível carregar arquivo ou assembly 'System.Web.Extensions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' ou uma de suas dependências. A definição do manifesto do assembly localizado não corresponde à referência do assembly. (Exceção de HRESULT: 0x80131040)

Dicas de Instruções SQL para facilitar o uso no dia-a-dia

Bom, abaixo separei algumas instruções SQL que muitos programadores tem dúvidas de como usá-las e que facilitam muitas tarefas.

1) INSERT de SELECT: Fazer inserções em uma tabela puxando dados de outra(s)

Bem parecido com o SELECT INTO porém você pode fazer várias manipulações conforme desejar usando um simples SELECT. No caso vou mostrar um exemplo bem fácil: irei adicionar vários Alunos numa tabela sendo que os dados estão em outra tabela (Cadastros), logo minha instrução ficaria assim:

INSERT INTO Alunos (nome, email) SELECT nome, email FROM Cadastros

O nome dos campos não precisam serem iguais, basta estar na mesma ordem e respeitar o tipo de dados (tamanho também).

2) UPDATE relacionado com outra tabela

Nessa situação o que desejamos é fazer um UPDATE na tabela sendo que é preciso fazer um JOIN com outra tabela. No nosso exemplo: desejamos alterar o preco de um produto sendo que o valor do mesmo está relacionado com seu tipo e ele está armazenado em outra tabela. Logo podemos usar a seguinte instrução:

UPDATE Produto SET preco = T.preco FROM Produto AS P, Tipo AS T WHERE P.Id_Tipo = T.Id

Meio complicado quando se vê, né? Mas se você reparar detalhadamente verá que a atualização do preço é feita, para cada produto, quando o Id_Tipo (da tabela Produto) for igual ao Id (da tabela Tipo) obtendo assim o preco (da tabela Tipo).

3) Remover registros duplicados deixando apenas um

A explicação desse objetivo é bem óbvia: remover registros duplicados em uma determinada tabela sob,algum critério. Porém, sua sintaxe é mais complicada, mas vamos lá... Para nosso exemplo iremos remover os registros duplicados (ou mais que um) de e-mails deixando apenas um na tabela Mailing:

;WITH Listagem(email, ranking)
AS
(
SELECT email
,ranking = DENSE_RANK() OVER(PARTITION BY email ORDER BY NEWID() ASC)
FROM Mailing WITH (NOLOCK) WHERE email IS NOT NULL 
)
DELETE Listagem WHERE ranking > 1

Esse emaranhado de instruções se resume em gerar um ranking indicando quantas vezes o registro se repete. Esse ranking é armazenado em uma tabela "temporária" (tabela Listagem) com referência à original (tabela Mailing)... Logo que é gerado o ranking, é feita a exclusão dos registros que possuem ranking maior que 1, ou seja, apaga todos aqueles que estão se repetindo deixando apenas um deles.

Bem, é isso! Espero ter ajudado... Lembrando que os exemplos acima foram os mais simples e vocês podem encontrar situações semelhantes ou piores mas que, usando-os, podem ajudar a chegar na resolução.

Problema do Mootools dentro de um frame

O Mootools é um framework JavaScript para criar abas com um belo design e praticidade no uso. Porém verifiquei que ele tem um certo "probleminha de comportamento" quando o usa dentro de um frame / frameset no IE. O seu carregamento / aplicação de estilo se dá através de uma adição de uma função chamadora na janela (usando window.addEvent). A condição para execução do script se dá quando o DOM estiver pronto, ou seja, window.addEvent('domready',function({})); . O problema é que, para o Internet Explorer (IE) o carregamento da página é meio que desordenado enquanto o Firefox e Chrome (não testei nos demais navegadores) possuem uma ótima engine de carregamento de página. Ou seja, à vezes o conteúdo da página demora a ser carregado e o DOM fica pronto antes dos controles HTML serem devidamente carregados ocasionando erro na execução. Para contornar o problema, basta substituir o 'domready' por 'load'. Então a aplicação do Mootools sobre o conteúdo será providenciado logo que a página for totalmente carregada. O problema é se houver muito conteúdo na página e houver demora no carregamento da página pois o estilo será aplicado apenas no final... daí é só ter mesmo paciência (ou remove do frame)!

Obs: outros frameworks JavaScript podem vir a ocasionar o mesmo problema sob as mesmas circunstâncias.

Problema no uso de memória no SQL Server 2008

Se em algum momento, quando em trabalho, estiver recebendo uma das seguintes mensagens abaixo, você está tendo sérios problemas de uso de memória:

"There is insufficient system memory in resource pool 'internal' to run this query" *

ou

"Há memória de sistema insuficiente no pool de recursos internos para executar essa consulta" *

Isso ocorre quando há várias instruções SQL sendo executadas em seqüência. Como o pool não aguenta, os dados, em algumas vezes, podem travar... Mesmo que fisicamente eles estejam lá, não se pode mais ser utilizados até que o banco seja reiniciado. Para corrigir esse problema há duas formas: instalar o hotfix que a Microsoft disponibilizou ou reduzir quantidade de instruções a serem executadas por vez.

1) Instalar o HotFix

O arquivo está disponível em um pack cumulativo pós SP1 do SQL Server 2008. Para mais informações e download, clique aqui.

2) Reduzir a quantidade de instruções SQL

Fazendo um teste de vários UPDATE's seguidos, consegui obter tal erro. Gerei um arquivo .sql com quase 100mil updates e mandei o SQL Server executar, daí obtive o erro. Nunca tente isso! Se desejar executar vários UPDATE's ou INSERT's de vez fragmente a execução das instruções em blocos (nem tão grandes e nem tão pequenos) dando uma pausa entre as execuções. Pode-se utilizar uma Store Procedure buscando os dados em um tabela temporária e executar a instrução de forma genérica (INSERT com SELECT, por exemplo). Também pode-se usar um artifício do ASP.NET, Batch Update, para executar a instrução de forma genérica com alto desempenho.

* A depender da configuração do SQL Server, ao invés de ser o recurso interno (ou internal) pode ser o recurso default.

Gerando Gráficos em ASP.NET

Um componente que gosto de utilizar para gerar gráficos é o FusionCharts. Através dele é possível criar gráficos animados e interativos em Flash como gráficos em pizza, barra, colunas, funil, linha, área, bolha, etc. Além de trabalhar com ASP, ASP.NET, PHP, JSP, ColdFusion, Ruby on Rails, Python e HTML puro, pode-se utilizar em conjunto com bancos de dados como SQL Server, Oracle, MySQL, PostgreSQL, etc... O seu funcionamento consiste em apenas gerar o XML (em tempo de execução ou não) e indicar qual o gráfico. Vamos à prática...



Acesse o site da FusionCharts e baixe o pacote com os gráficos (na versão trial vem apenas os gráficos comuns - os mais complexos vem na versão paga). No arquivo baixado vem os SWF's dos gráficos, exemplos de XML's a serem gerados para cada gráficos, o JavaScript a ser incorporado na página e demais documentos (qualquer dúvida veja na pasta Code > CSNET que contêm um exemplo em ASP.NET e contêm todos os arquivos que irá precisar). Para nosso exemplo, vamos gerar um gráfico em pizza.

Crie um Web Site e adicione as pastas conforme a estrutura abaixo:

Adicione no App_Code as classes Util.cs e DbConn.cs. Na Bin adicione a referência à DLL da FusionCharts.dll, na Graficos adicione o SWF do gráfico que iremos gerar (no caso escolha o Pizza3D.swf) e na pasta Js adicione o FusionCharts.js. Todos esses arquivos são encontrados no ZIP que baixou no mesmo caminho dito anteriormente.

Adicione uma página ASPX agora e adicione no body ou no head o script:
<script language="Javascript" type="text/javascript" src="js/FusionCharts.js"></script>

Também adicione um Literal (Controle) no local onde deverá surgir o gráfico. Para fins didáticos coloque o ID do controle como GraphPizza. Agora tudo será no Code-Behind... Adicione os namespaces Utilities e InfoSoftGlobal usando a cláusula using. Em seguida criamos um método onde é gerado o XML e encapsulado sobre o SWF da seguinte forma:

public void CriarGraficoPizza3D()
{
    string strXML = "<graph showNames='1' caption='Tipos de Clientes' decimalPrecision='0'>";
    strXML += "<set name='Alvos' value='10' color='ED1C24'/>";
    strXML += "<set name='Prospects' value='5' color='FFFF00'/>";
    strXML += "<set name='Clientes' value='2' color='00E600'/>";
    strXML += "</graph>";
    GraphPizza.Text = FusionCharts.RenderChart("Graficos/Pizza3D.swf", "", strXML, "GraficoPizza3D", "400", "300", false, false);
}

No caso, gero todo o XML contendo os dados a serem apresentados e o passo por parâmetro ao método do FusionCharts para renderizá-lo e exibi-lo. Bem simples, não? Esse é o exemplo mais fácil... Há várias formas de apresentá-lo bem como suas opções fazem com que apresente mais variantes. O mais difícil (quer dizer trabalhoso) é gerar o XML...