Technè

Tecnologia & Experiência do Usuário no C.E.S.A.R

HTML5: Construindo um servidor de WebSockets com .net framework (C#)

Posted by Rafael Amaral On março - 10 - 2011

HTML5 é a quinta grande revisão do core da linguagem HTML trazendo novas features e elementos, tentando tratar tópicos que não eram adequadamente cobertos em versões anteriores e trazendo um meio padronizado de se criar aplicações web.

Websockets aparecem como uma interessante nova feature do HTML5. Essa feature define um canal de comunicação full-duplex através do qual mensagens podem ser trocadas entre o lado cliente e servidor de uma aplicação web de forma bidirecional. Abaixo temos uma imagem que mostra o atual suporte a websockets nos navegadores retirada da página http://caniuse.com/:

Suporte a Websockets

Assim como o HTML5, websockets ainda estão em versão draft, mas desenvolvedores podem começar a testar esse recurso implementando servidores que suportem uma das versões do protocolo e utilizar clientes que suportem a mesma versão. Atualmente alguns dos mais modernos browsers suportam a versão draft-hixie-thewebsocketprotocol-76 (http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76) também conhecida como draft-ietf-hybi-thewebsocketprotocol-00.

Neste post veremos como construir um protótipo de um simples servidor que suporta a versão draft-hixie-thewebsocketprotocol-76 do protocolo utilizando sockets em c#. O servidor será um console application que aceita uma conexão de websocket, recebe mensagens do cliente e envia os dados recebidos de volta em letras maiúsculas. No lado cliente usaremos uma página com HTML/javascript rodando sobre algum navegador que suporte a versão 76 do protocolo. Em testes durante a escrita deste post foi utilizada a versão mais atual do Chrome (no momento a 10.0.648.127).

Overview do projeto

Para estabelecer uma conexão via websocket cliente e servidor precisam se comunicar e verificar que conseguem falar na mesma língua, para isso é necessário se realizar um handshake. Abaixo um exemplo de handshake extraído do documento draft -hixie-thewebsocketprotocol-76:

Exemplo de mensagem de handshake vinda do cliente:

GET /demo HTTP/1.1
Host: example.com
Connection: Upgrade
Sec-WebSocket-Key2: 12998 5 Y3 1  .P00
Sec-WebSocket-Protocol: sample
Upgrade: WebSocket
Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5
Origin: http://example.com

^n:ds[4U

Exemplo de resposta vinda do servidor:

HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Origin: http://example.com
Sec-WebSocket-Location: ws://example.com/demo
Sec-WebSocket-Protocol: sample

8jKS'y:G*Co,Wxa-

Explicando de uma forma simplificada, para completar o handshake o servidor precisa analisar a mensagem que veio do cliente e realizar algumas operações sobre os valores de Sec-WebSocket-Key1, Sec-WebSocket-Key2 e os últimos 8 bytes da mensagem (no exemplo acima representada pelo valor  ^n:ds[4U) e construir uma mensagem de resposta garantindo ao cliente que os dois lados sabem falar a mesma língua.

Uma vez tendo estabelecida a conexão, os dois lados podem trocar informações via data frames que são delimitados pelos bytes 0x00 e  0xFF e contém dados com codificação UTF-8 entre os delimitadores.

Todos os métodos para realizar o handshake e as trocas de mensagens estão explicados mais detalhadamente no projeto disponibilizado neste post, bem como a implementação de um cliente HTML/javascript. É necessário que antes de testar o projeto seja modificado o ip no arquivo de configuração do servidor (App.config) e no HTML como  é visto a seguir.

App.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <appSettings>
                      <!--Coloque seu ip aqui-->
    <add key="ipaddress" value="SEU_IP_AQUI" /> 

    <add key="port" value="8181" />
    <add key="location" value="ts" />

  </appSettings>
</configuration>

WebSocketClient.htm

<!--Substuir "SEU_IP_AQUI" pelo ip
                              que foi configurado no .config do servidor-->
    <input id="txtConnect" type="text" value="ws://SEU_IP_AQUI:8181/ts" />

    <input id="btnConnect" type="button" value="Connect" onclick="Connect()" />
    <br />
    <input id="txtSend" type="text" />
    <input id="btnSend" type="button" value="Send" onclick="Send()" />
    <input id="btnClose" type="button" value="Close" onclick="Close()" />
    </form>
</body>

Após configurar o App.config do servidor e o HTML do cliente basta rodar o servidor e acessar o arquivo HTML pelo browser. O projeto com o código pode ser acessado aqui: http://cid-2279647898802a3c.office.live.com/self.aspx/.Public/WebSocket76.zip e os arquivos do servidor e do cliente são os seguintes:

Program.cs

using System;
using System.Configuration;
using System.Net;
using System.Net.Sockets;

namespace WebSocket76
{
    class Program
    {
        static void Main(string[] args)
        {
            //Pega dados do arquivo de configuracao WebSocket76.exe.config
            //(App.config), nao esqueca de colocar seu ip la e no html tambem
            string ipAddress = ConfigurationSettings.AppSettings["ipaddress"];
            int port = int.Parse(ConfigurationSettings.AppSettings["port"]);

            string location = “ws://” + ipAddress + “:” + port
                + “/” + ConfigurationSettings.AppSettings["location"];

            TcpListener tcpListener =
              new TcpListener(new IPEndPoint(IPAddress.Parse(ipAddress), port));

            tcpListener.Start();

            Console.WriteLine(“Esperando conexão…”);
            Socket client = tcpListener.AcceptSocket();

            Console.WriteLine(“Tentativa de conexão iniciada…”);
            WebSocketServer wss = new WebSocketServer(client, location);

            Console.Read();
        }
    }
} 

WebSocketServer.cs

using System;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading;

namespace WebSocket76
{
    public class WebSocketServer
    {
        /// <summary>
        /// Socket usado como servidor
        /// </summary>
        private Socket _socketClient;

        /// <summary>
        /// Thread para receber dados do cliente
        /// </summary>
        private Thread _dataReceivedThread;

        /// <summary>
        /// Indica se o servidor esta ativo
        /// </summary>
        private bool _webSocketServerActive;

        /// <summary>
        /// Tamanho do buffer de recebimento de dados
        /// </summary>
        private const int BUFFER_SIZE = 1024;

        /// <summary>
        /// Indica se o handshake ja foi feito
        /// </summary>
        private bool _handShakeDone;

        /// <summary>
        /// Local do servidor
        /// </summary>
        private string _location;

        /// <summary>
        /// Construtor
        /// </summary>
        /// <param name="client">Socket para ser usado como servidor</param>
        /// <param name="location">Location</param>
        internal WebSocketServer(Socket client, string location)
        {
            _socketClient = client;
            _webSocketServerActive = true;
            _dataReceivedThread = new Thread(dataReceived);
            _dataReceivedThread.Start();

            _location = location;

            _handShakeDone = false;

        }

        /// <summary>
        /// Recebe dados do cliente e os trata
        /// </summary>
        private void dataReceived()
        {
            try
            {
                StringBuilder receivedDataString = new StringBuilder();

                while (_webSocketServerActive)
                {

                    byte[] data = new byte[BUFFER_SIZE];
                    int receivedDataLength = _socketClient.Receive(data);

                    //verifica se a conexao foi fechada
                    if (receivedDataLength < 1)
                    {
                        _webSocketServerActive = false;
                    }
                    else if (_webSocketServerActive)
                    {
                        //realiza handshake se o mesmo ainda nao tiver sido
                        //feito
                        if (!_handShakeDone)
                        {
                            Console.WriteLine("Iniciando handshake...");
                            StartHandShakeProcess(data, receivedDataLength);
                            _handShakeDone = true;
                        }
                        else //pega dados nos outros casos
                        {
                            System.Text.UTF8Encoding decoder =
                                new System.Text.UTF8Encoding();

                            string stringData =
                                decoder.GetString(data, 0, receivedDataLength);
                            receivedDataString.Append(stringData);

                            //Verifica se e o final da mensagem
                            if (receivedDataLength > 0 &&
                                (data[receivedDataLength - 1]
                                == BitConverter.GetBytes(0xFF)[0]))
                            {
                                string strData = receivedDataString.ToString();
                                strData = strData.Substring(1,
                                                           strData.Length - 2);

                                Console.WriteLine("DADOS RECEBIDOS: "
                                    + strData);

                                Console.WriteLine("Enviando dados" +
                                    " de volta para o cliente");

                                //envia a mensagem recebida de volta
                                //ao cliente com tudo em letras maiusculas
                                SendData(strData.ToUpper());

                                receivedDataString = new StringBuilder();
                            }
                        }
                    }
                }
            }
            catch
            {
                _webSocketServerActive = false;
            }
            finally
            {
                Console.WriteLine("Encerrando servidor");

                Shutdown();

                Console.WriteLine("Pressione enter para fechar o servidor");
            }
        }

        /// <summary>
        /// Finaliza servidor
        /// </summary>
        internal void Shutdown()
        {
            _webSocketServerActive = false;

            if (_socketClient != null && _socketClient.Connected)
            {
                _socketClient.Shutdown(SocketShutdown.Both);
                _socketClient.Close();
            }
        }

        /// <summary>
        /// Envia dados para o cliente
        /// </summary>
        /// <param name="data">Dados a serem enviados</param>
        internal void SendData(string data)
        {
            byte[] toSend = Encoding.UTF8.GetBytes(data);
            SendDataToClient(toSend);
        }

        /// <summary>
        /// Envia dados para o cliente de acordo com thewebsocketprotocol-76
        /// </summary>
        /// <param name="toSendBack"></param>
        private void SendDataToClient(byte[] toSendBack)
        {
            //Os dataframes sao delimitados pelos bytes 0x00 e 0xFF e
            //contem dados com codificacao UTF-8 entre esses delimitadores
            try
            {
                _socketClient.Send(BitConverter.GetBytes(0x00));
                _socketClient.Send(toSendBack);
                _socketClient.Send(BitConverter.GetBytes(0xFF));
            }
            catch
            {
                Shutdown();
            }
        }

        #region Handshake

        /// <summary>
        /// Iniicia o processo de handshake
        /// </summary>
        /// <param name="data">Handshake vindo do cliente</param>
        /// <param name="receivedDataLength">Tamanho dos dados recebidos</param>
        private void StartHandShakeProcess(byte[] data, int receivedDataLength)
        {
            byte[] last8Bytes = new byte[8];
            Array.Copy(data, receivedDataLength - 8, last8Bytes, 0, 8);

            System.Text.UTF8Encoding decoder = new System.Text.UTF8Encoding();
            string stringHandShakeRequest =
                decoder.GetString(data, 0, receivedDataLength - 8);

            DoHandShake(stringHandShakeRequest, last8Bytes);
        }

        /// <summary>
        /// Controi a resposta e a envia para o cliente
        /// </summary>
        /// <param name="stringHandShakeRequest">Parte inicial
        ///                                     do handshake</param>
        /// <param name="last8Bytes">Ultimos 8 bytes que compoem
        ///                          o handshake</param>
        private void DoHandShake(string stringHandShakeRequest,
                                 byte[] last8Bytes)
        {
            StringBuilder handShakeSB = new StringBuilder();

            handShakeSB.Append("HTTP/1.1 101 Web Socket Protocol Handshake"
                               + Environment.NewLine);
            handShakeSB.Append("Upgrade: WebSocket" + Environment.NewLine);
            handShakeSB.Append("Connection: Upgrade" + Environment.NewLine);

            string connectionOrigin =
                LoadConnectionOriginFromHandShake(stringHandShakeRequest);
            handShakeSB.Append("Sec-WebSocket-Origin: " +
                               connectionOrigin + Environment.NewLine);

            handShakeSB.Append("Sec-WebSocket-Location: "
                              + _location + Environment.NewLine);
            handShakeSB.Append(Environment.NewLine);

            byte[] handShakeResponsePart1 =
                Encoding.UTF8.GetBytes(handShakeSB.ToString());
            byte[] handShakeChallengeResponse =
                BuildChallengeResponse(stringHandShakeRequest, last8Bytes);

            byte[] handshakeResponse = new byte[handShakeResponsePart1.Length +
                                            handShakeChallengeResponse.Length];
            Array.Copy(handShakeResponsePart1,
                       handshakeResponse, handShakeResponsePart1.Length);
            Array.Copy(handShakeChallengeResponse, 0, handshakeResponse,
                       handShakeResponsePart1.Length,
                       handShakeChallengeResponse.Length);

            Console.WriteLine("Enviando resposta de handshake...");
            //Envia resposta para o cliente
            int a = _socketClient.Send(handshakeResponse, 0,
                    handshakeResponse.Length, 0);

        }

        /// <summary>
        /// Gera a resposta a partir da duas chaves e dos ultimos 8 bytes
        /// do handshake
        /// </summary>
        /// <param name="stringHandShakeRequest">Parte inicial
        ///                                      do handshake</param>
        /// <param name="last8Bytes">Ultimos 8 bytes que compoem
        ///                          o handshake</param>
        /// <returns>A resposta</returns>
        private byte[] BuildChallengeResponse(string stringHandShakeRequest,
                                              byte[] last8Bytes)
        {
            byte[] keyResult1 = null;
            byte[] keyResult2 = null;

            string[] handShakeTextLines = stringHandShakeRequest.Split(
                new string[] { Environment.NewLine },
                System.StringSplitOptions.RemoveEmptyEntries);

            foreach (string line in handShakeTextLines)
            {
                if (line.Contains("Sec-WebSocket-Key1:"))
                {
                    keyResult1 = getKeyResult(line.Substring(line.IndexOf(":")
                          + 2));
                }
                else if (line.Contains("Sec-WebSocket-Key2:"))
                {
                    keyResult2 = getKeyResult(line.Substring(line.IndexOf(":")
                        + 2));
                }
            }

            byte[] challengeResponse = BuildMD5ChallengeResponse(keyResult1,
                keyResult2, last8Bytes);

            return challengeResponse;
        }

        /// <summary>
        /// Gera um pedaco da resposta final baseado na chave passada como
        /// parametro seguindo as especiicacoes de
        /// http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
        /// </summary>
        /// <param name="originalKey">Uma chave</param>
        /// <returns>Um numero no formato big-endian 32-bit(byte mais
        ///          significativo armazenado primeiro)</returns>
        private byte[] getKeyResult(string originalKey)
        {
            byte[] resultKey = null;
            StringBuilder keyNumbers = new StringBuilder();
            int whiteSpacesCount = 0;

            foreach (char c in originalKey.ToCharArray())
            {
                //peque os digitos da chave e concatene gerando um numero
                if (char.IsDigit(c))
                {
                    keyNumbers.Append(c);

                }//conte quantos espacos em branco existem
                else if (char.IsWhiteSpace(c))
                {
                    whiteSpacesCount++;
                }
            }

            //Divida o numero gerado pela quantidade de espaços em branco
            int numericResult = (int)(Convert.ToInt64(keyNumbers.ToString()) /
                                 whiteSpacesCount);

            resultKey = BitConverter.GetBytes(numericResult);

            //garanta que esta como big-endian
            if (BitConverter.IsLittleEndian)
            {
                Array.Reverse(resultKey);
            }

            return resultKey;
        }

        /// <summary>
        /// Constroi a resposta em md5 de key1 + key2 + key3
        /// 4 bytes big endian key1 + 4 bytes big endian key2 + 8 bytes key3.
        /// </summary>
        /// <param name="key1">4 bytes big endian key1</param>
        /// <param name="key2">4 bytes big endian key2</param>
        /// <param name="last8Bytes">8 bytes key3</param>
        /// <returns>16 bytes md5 de key1 + key2 + key3</returns>
        private byte[] BuildMD5ChallengeResponse(byte[] key1, byte[] key2,
                                                 byte[] last8Bytes)
        {
            byte[] challengeResponse = null;
            byte[] challengeArray = new byte[16];

            Array.Copy(key1, 0, challengeArray, 0, 4);
            Array.Copy(key2, 0, challengeArray, 4, 4);
            Array.Copy(last8Bytes, 0, challengeArray, 8, 8);

            MD5 md5Gen = MD5.Create();
            challengeResponse = md5Gen.ComputeHash(challengeArray);

            return challengeResponse;
        }

        /// <summary>
        /// Pega a origin do handshake
        /// </summary>
        /// <param name="stringHandShakeRequest">Handshake</param>
        /// <returns>Origin</returns>
        private string LoadConnectionOriginFromHandShake(
                                                 string stringHandShakeRequest)
        {
            string connectionOrigin = null;

            string[] handShakeTextLines = stringHandShakeRequest.Split(
                new string[] { Environment.NewLine },
                System.StringSplitOptions.RemoveEmptyEntries);

            foreach (string line in handShakeTextLines)
            {
                if (line.Contains("Origin:"))
                {
                    connectionOrigin = line.Substring(line.IndexOf(":") + 2);
                }
            }

            return connectionOrigin;
        }
    }
        #endregion
}

WebSocketClient.htm

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>

    <title>Web socket client test</title>

    <script type="text/javascript">

        var wsClient;

        //Verifica se o browser da suporte a websockets,
        //mas nao verifica versao do protocolo
        function WebSocketTest() {
            if (!("WebSocket" in window)) {
                alert("Este browser não suporta websockets!");
            }
        };

        //realiza a conexao
        function Connect() {

            try {

                //cria cliente websocket
                wsClient =
                   new WebSocket(document.getElementById("txtConnect").value);

                document.getElementById("txtConnect").disabled = true;
                document.getElementById("btnConnect").disabled = true;

                //cadastra eventos

                //chamado ao realizar a conexao com sucesso
                wsClient.onopen = OnOpen;
                //chamado quando o servidor manda mensagens para o cliente
                wsClient.onmessage = OnMessage;
                //chamado em caso de fechamento de conexao
                wsClient.onclose = OnClose;
                //chamado em caso de erro
                wsClient.onerror = OnError;

            } catch (ex) {
                alert(ex);
            }
        };

        //envia mensagem para o servidor
        function Send() {

            wsClient.send(document.getElementById("txtSend").value);

            document.getElementById("txtSend").value = "";
        };

        //fecha conexao com servidor
        function Close() {

            try {

                wsClient.close();

            } catch (ex) {
                alert(ex);
            }
        };

        function OnOpen() {
            alert("Conexão realizada com sucesso!");
        };

        function OnMessage(evt) {
            alert("Mensagem recebida: \"" + evt.data + "\"");

        };

        function OnClose() {
            alert("Conexão fechada!");
        };

        function OnError() {
            alert("Erro!");
        };

    </script>

</head>
<body onload="WebSocketTest()">
    <form>
                                        <!--Substuir "SEU_IP_AQUI" pelo ip
                               que foi configurado no .config do servidor-->
    <input id="txtConnect" type="text" value="ws://SEU_IP_AQUI:8181/ts" />

    <input id="btnConnect" type="button" value="Connect" onclick="Connect()" />
    <br />
    <input id="txtSend" type="text" />
    <input id="btnSend" type="button" value="Send" onclick="Send()" />
    <input id="btnClose" type="button" value="Close" onclick="Close()" />
    </form>
</body>
</html>

Considerações finais

Websocket parece ser uma tecnologia promissora. Vale ressaltar que o w3c pretende finalizar a normalização do html5 até 2014 e acontecerão melhorias até lá. O protocolo usado pelo websocket provavelmente ainda passará por algumas mudanças para melhorar, por exemplo, questões relacionadas à segurança.

Seguem alguns links interessantes:

.

IoC em .NET utilizando Unity

Posted by Thiago Veras On outubro - 13 - 2010

Inversão de controle (IoC) é um padrão que descreve técnicas genéricas para suportar uma arquitetura plug-in, onde os objetos podem “procurar” os outros objetos que eles necessitam para executar determinada ação.

IoC está sendo muito utilizado em projetos orientados a objeto. Conceitos como interface, herança e polimorfismo são bem presentes. O uso de IoC tem como objetivo reduzir o acoplamento, facilitar o reuso e os testes no projeto de software.

Em uma hierarquia, os módulos de nível mais alto não devem depender diretamente dos módulos de nível mais baixo. Ambos devem depender de abstrações. As abstrações não devem depender de detalhes da implementação, mas estes detalhes devem depender das abstrações. Desta forma, invertendo o controle.

A Injeção de Dependências é um caso especial do padrão IoC onde a técnica de programação é baseada em alterar o comportamento sem modificar classes internas. Além de classes independentes umas das outras por meio de abstrações, a injeção de dependências gera menor impacto para introdução de mudanças e um código mais facil de testar.

Um container de injeção de dependências é capaz de criar objetos com todas suas dependências injetadas e totalmente prontos para uso. Em geral estes containers podem ser configurados manualmente (programaticamente) ou dinamicamente (através de arquivos de configuração por exemplo).

Vamos falar neste post do uso de IoC na tecnologia Microsoft .NET.
O Unity da Microsoft foi escolhido como exemplo de container para entender melhor o uso de IoC.

O Unity suporta diversos tipos de injeção, como por exemplo: injeção por construtor, injeção por métodos gets e sets, injeção por arquivos de configuração, entre outras.

Como primeiro passo, precisamos criar uma Interface para ele.

Como segundo passo, vamos fazer o setup dele dentro do Global.asax da Aplicação Web.
OBS: Não é obrigatório fazer desta forma, é apenas uma abordagem que considero interessante e indicada por “gurus” da Microsoft.

Observem que no BuildContainer() tem um TODO para que seja adicionado o registro dos objetos.

Existem diversas formas de se registrar os objetos e cada uma está ligada a uma necessidade especifica, neste post, não irei tratar todas elas para termos oportunidade de discutir cada uma delas com mais tempo em um momento futuro.

Diante disto, vamos considerar a Injeção por Construtor utilizando a seguinte forma de registro:

unityContainer.RegisterType<INTERFACE/CLASSE ABSTRATA, CLASSE CONCRETA>();

Agora vamos olhar o código abaixo para entender o que este registro significa…

Observem que no exemplo, temos uma interface para o Logger chamada de ILogger. Temos também três implementações de Logger: uma para arquivo (FileLogger), uma para memória (MemoryLogger) e uma para banco de dados (DatabaseLogger). Além disto, existe uma Classe X que em seu construtor recebe a interface por parâmetro e fica apta a usar um dos 3 logs. Quem define qual deles será utilizado, é o registro que foi feito anteriormente no container do Unity.

Ou seja, voltando para o setup no Global.asax, em meu exemplo iremos colocar o seguinte registro:

unityContainer.RegisterType<ILogger, DatabaseLogger>();

e posteriormente faremos…

X x = container.Resolve<X>();

Neste momento o Unity irá resolver o objeto X e injetar a dependência do ILogger para a classe concreta do DatabaseLogger.

Espero que esta introdução ao IoC e Unity tenha ajudado de alguma forma… Em posts posteriores, podemos entrar em mais detalhes…

Referências:
http://unity.codeplex.com/
http://msdn.microsoft.com/en-us/library/cc440954.aspx/
http://msdn.microsoft.com/en-us/library/dd203101.aspx/

Exportando para MS Excel usando .NET

Posted by Carlos Pantoja On outubro - 5 - 2010

Existem várias maneiras de exportar um arquivo MS Excel utilizando .NET, a abordagem que será explicada nesse post será utilizando tags HTML com seus respectivos estilos.

A abordagem escolhida pode ser utilizada por qualquer linguagem de programação, porque esse tipo de exportação não está ligado ao framework .NET e sim ao padrão HTML, o qual ao definir no header da página qual o tipo do conteúdo, os browsers mais atuais reconhecem automaticamente o arquivo e inicia o processo de download do arquivo.

Na figura acima na linha 173 a 180 está um exemplo de como escrever o header para documentos MS Excel. Nesse tipo de exportação só é possível criar uma pasta por arquivo, portanto caso o projeto precise de uma planilha com mais de uma pasta seria melhor procurar uma outra solução.

O conteúdo que será exportado nesse caso será inserido dentro de uma ‘div‘, que é opicional, na linha 187 que possui uma string com uma ou várias tabelas HTML, com seus respectivos estilos css.

No code behind da página que vai realizar a ação de export do relatório é necessário colocar o conteúdo da página gerado no método mostrado na imagem anterior no Reponse.Write, como mostra a figura abaixo.

Na figura acima ilustra um trecho de código que configura a resposta da requisição para criar um conteúdo que seja reconhecido pelo browser como um conteúdo do MS Excel, na linha 71 é informado o nome do arquivo que será exportado, na linha 74 informando ao response que o conteúdo da página é do MS Excel, o valor da constante é ‘application/vnd.ms-excel‘ .

Na linha 76 o método da primeira figura é chamado para criar o conteúdo no formato desejado, e na linha 78 o resultado do método é passado como parâmetro para o Response.Write, que escreverá o conteúdo do relatório na página, e na linha 79 a resposta é finalizada.

Obs.: vale uma ressalva sobre CSS, nesse tipo de exportação o excel não reconhece mais de uma classe de CSS por tag, portanto, caso seja necessário utilizar mais de uma classe de CSS, utilize a classe que possua o maior conteúdo e a que possuir menor conteúdo coloque o estilo na mão.

Fluent NHibernate

Posted by Thiago Veras On setembro - 30 - 2010
O Mapeamento Objeto Relacional (Object Relational Mapping) é uma técnica para criação de mapeamentos entre as tabelas do Banco de Dados e as classes de uma Aplicação Orientada a Objetos.
O NHibernate é um framework .NET para Mapeamento Objeto Relacional.
A semelhança com a nomeclatura do Hibernate (framework Java) não é uma simples coincidência, ele foi originado por uma portabilidade do Core do Hibernate.
Quando um desenvolvedor opta por utilizar o NHibernate em um projeto .NET, é necessário por padrão da presença de arquivos HBM. Cada classe necessita de um HBM correspondente (em formato XML) mapeando os objetos nas tabelas e os atributos nas colunas, seguindo um sintaxe pré-definida pelo framework.
O Fluent oferece uma alternativa mais simples que arquivos HBMs.Com o uso das interfaces do Fluent (Fluent Interfaces) torna-se dispensável o uso dos HBMs e possível o mapeamento/configuração do NHibernate via código C#.
Desta forma, possibilitando uma fácil manutenção, melhorias na legibilidade, além de deixar mais conciso o código.
Exemplo:
HBM Tradicional
Fluent
O Fluent também possibilita eliminar dos arquivos de configuração do projeto (web.config /ou app.config) as configurações relacionadas ao NHibernate: tais como string de conexão, driver, timeout, entre outras, utilizando código C#.
Exemplo:

Referências:
Fluent NHibernate =>  http://www.fluentnhibernate.org/