Com o Visual Studio 2010 aberto, vamos iniciar criando um projeto do tipo ASP.NET MVC 2 Web Application com o nome de ExportacaoExcel. Podemos também criar um DB no SQL Server com o nome de sua preferência, dentro do DB criamos uma tabela de Usuários com a seguinte estrutura:

CREATE TABLE [dbo].[tbUsuarios]
(
     [codUsuario] [int] IDENTITY(1,1) PRIMARY KEY NOT NULL,
      [nome] [varchar](200) NULL,
      [idade] [int] NULL,
      [codContato] [int] NULL,
      [dataCadastro] [datetime] NULL,
      [login] [varchar](100) NULL,
      [senha] [varchar](100) NULL,
      [ativo] [bit] NULL,
      [observacao] [varchar](max) NULL
)
 
No diretório Model do projeto vamos criar um novo item do tipo LINQ to SQL Classes com o nome de DB.dbml. Com o mesmo aberto, arrastamos a tabela criada anteriormente para dentro do mesmo e a renomeamos para Usuario.
 
Agora sim, mão a obra
Na Master Page vamos adicionar a referencia ao jQuery conforme o código abaixo:
 
<head runat="server">
    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
   
    <link href="../../Content/Site.css" rel="stylesheet" type="text/css" />
    <script src="../../Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
 
    <asp:ContentPlaceHolder ID="HeadContent" runat="server" />
</head>
 
 
Agora vamos adicionar um botão na View Index.aspx:
 
 
<input type="button" id="btnExportar" value="Exportar Usuários" />
 
 
E o evento referente ao click do botão:
 
 
<asp:Content ID="Content3" ContentPlaceHolderID="HeadContent" runat="server">
   <script type="text/javascript">
 
       $(document).ready(function () {
 
           $("#btnExportar").click(function () {
 
               var campos = "codUsuario,nome";
               window.location = "/Home/GeraExcel/?nomeEntidade=Usuario&campos=" + campos;
           });
       });
  
   </script>
</asp:Content>
 
 
Note que, o parâmetro nomeEntidade é o nome da tabela definida no arquivo DB.dbml que queremos obter, e a propriedade campos é justamente os campos que desejamos exportar da tabela.
 
Agora no HomeController vamos criar um método do tipo ActionResult chamado GeraExcel que é justamente o método chamado no evento acima, e um outro chamado SelecionaDados, conforme é exibido abaixo:
 
 
public ActionResult GeraExcel(string nomeEntidade, string campos)
        {
            Type tipoEntidade = Type.GetType("ExportacaoExcel.Models." + nomeEntidade);
 
            return this.Excel(new DBDataContext(),
                              this.SelecionaDados(tipoEntidade),
                              nomeEntidade + ".xls",
                              campos.Split(','));
        }
 
public IQueryable SelecionaDados(Type tb)
        {
            DBDataContext _db = new DBDataContext();
 
            return _db.GetTable(tb).AsQueryable();
        }
 
 
Observe, o método GeraExcel recebe os parâmetro (nomeEntidade e campos) enviados pela nossa View Index.aspx, a propriedade tipoEntidade como o próprio nome diz, recebe o tipo da entidade enviada como parâmetro. Porém, será exibida uma mensagem de que não existe uma definição para Excel e que não existe nenhum ExtesionMethod.
 
Para um melhor entendimento sobre ExtensionMethods veja: http://msdn.microsoft.com/en-us/library/bb383977.aspx
 
Vamos então criar um, na raiz do projeto criaremos um diretório chamado Helpers e dentro do mesmo adicionaremos uma classe chamada ExtensionMethods.cs com o seguinte código:
 
 
using System.Web.Mvc;
using System.Data.Linq;
using System.Linq;
using System.Web.UI.WebControls;
 
namespace ExportacaoExcel.Helper
{
    public static class ExcelControllerExtensions
    {
 
        public static ActionResult Excel
        (
            this Controller controller,
            DataContext dataContext,
            IQueryable rows,
            string fileName
        )
        {
            return new ExcelResult(dataContext, rows, fileName, null, null, null, null);
        }
 
        public static ActionResult Excel
        (
            this Controller controller,
            DataContext dataContext,
            IQueryable rows,
            string fileName,
            string[] headers
        )
        {
            return new ExcelResult(dataContext, rows, fileName, headers, null, null, null);
        }
 
        public static ActionResult Excel
        (
            this Controller controller,
            DataContext dataContext,
            IQueryable rows,
            string fileName,
            string[] headers,
            TableStyle tableStyle,
            TableItemStyle headerStyle,
            TableItemStyle itemStyle
        )
        {
            return new ExcelResult(dataContext, rows, fileName, headers, tableStyle, headerStyle, itemStyle);
        }
 
    }
}
 
 
Note agora que é apresentado um novo erro, explicando que não existe o tipo ou namespace ExcelResult, portanto vamos cria-lo também.
Dentro do diretório Helpers adicionaremos outra classe, agora chamada ExcelResult.cs, da seguinte forma:
 
 
using System;
using System.Data.Linq;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using TesteExcel.Models;
 
namespace ExportacaoExcel.Helper
{
    public class ExcelResult : ActionResult
    {
        private DBDataContext _db = new DBDataContext();
        private DataContext _dataContext;
        private string _fileName;
        private IQueryable _rows;
        private string[] _headers = null;
 
        private System.Web.UI.WebControls.TableStyle _tableStyle;
        private TableItemStyle _headerStyle;
        private TableItemStyle _itemStyle;
 
        public string FileName
        {
            get { return _fileName; }
        }
 
        public IQueryable Rows
        {
            get { return _rows; }
        }
 
 
        public ExcelResult(DataContext dataContext, IQueryable rows, string fileName)
            : this(dataContext, rows, fileName, null, null, null, null)
        {
        }
 
        public ExcelResult(DataContext dataContext, IQueryable rows, string fileName, string[] headers)
            : this(dataContext, rows, fileName, headers, null, null, null)
        {
        }
 
        public ExcelResult(DataContext dataContext, IQueryable rows, string fileName, string[] headers, System.Web.UI.WebControls.TableStyle tableStyle, TableItemStyle headerStyle, TableItemStyle itemStyle)
        {
            _dataContext = dataContext;
            _rows = rows;
            _fileName = fileName;
            _headers = headers;
            _tableStyle = tableStyle;
            _headerStyle = headerStyle;
            _itemStyle = itemStyle;
 
            if (_tableStyle == null)
            {
                _tableStyle = new System.Web.UI.WebControls.TableStyle();
            }
            if (_headerStyle == null)
            {
                _headerStyle = new TableItemStyle();
                _headerStyle.BackColor = Color.LightGray;
            }
        }
 
        public override void ExecuteResult(ControllerContext context)
        {
            // Cria um HtmlTextWriter
            StringWriter sw = new StringWriter();
            HtmlTextWriter tw = new HtmlTextWriter(sw);
 
            if (_tableStyle != null)
                _tableStyle.AddAttributesToRender(tw);
            tw.RenderBeginTag(HtmlTextWriterTag.Table);
 
            // Gera os headers
            if (_headers == null)
            {
                _headers = _dataContext.Mapping.GetMetaType(_rows.ElementType).PersistentDataMembers.Select(m => m.Name).ToArray();
            }
 
            // Cria o header para as Rows
            tw.RenderBeginTag(HtmlTextWriterTag.Thead);
            foreach (String header in _headers)
            {
                if (_headerStyle != null)
                    _headerStyle.AddAttributesToRender(tw);
                tw.RenderBeginTag(HtmlTextWriterTag.Th);
                tw.Write(header);
                tw.RenderEndTag();
            }
            tw.RenderEndTag();
 
 
            // Cria Data Rows
            tw.RenderBeginTag(HtmlTextWriterTag.Tbody);
            foreach (Object row in _rows)
            {
                tw.RenderBeginTag(HtmlTextWriterTag.Tr);
                foreach (string header in _headers)
                {
                    string strValue = "";
 
                    if (row.GetType().GetProperty(header).GetValue(row, null) == null)
                        strValue = "";
                    else
                        strValue = row.GetType().GetProperty(header).GetValue(row, null).ToString().Trim();
 
                    strValue = ReplaceSpecialCharacters(strValue);
                    if (_itemStyle != null)
                        _itemStyle.AddAttributesToRender(tw);
                    tw.RenderBeginTag(HtmlTextWriterTag.Td);
                    tw.Write(HttpUtility.HtmlEncode(strValue));
                    tw.RenderEndTag();
                }
                tw.RenderEndTag();
            }
            tw.RenderEndTag();
 
            tw.RenderEndTag();
 
            WriteFile(_fileName, "application/ms-excel", sw.ToString());
        }
 
        private static string ReplaceSpecialCharacters(string value)
        {
            value = value.Replace("’", "'");
            value = value.Replace("“", "\"");
            value = value.Replace("”", "\"");
            value = value.Replace("–", "-");
            value = value.Replace("…", "...");
            return value;
        }
 
        private static void WriteFile(string fileName, string contentType, string content)
        {
            HttpContext context = HttpContext.Current;
            context.Response.Clear();
            context.Response.AddHeader("content-disposition", "attachment;filename=" + fileName);
            context.Response.Charset = "";
            context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
            context.Response.ContentType = contentType;
            context.Response.Write("<html xmlns:x=\"urn:schemas-microsoft-com:office:excel\">");
            context.Response.Write("<head>");
            context.Response.Write("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=windows-1252\">");
            context.Response.Write("<!--[if gte mso 9]>");
            context.Response.Write("<xml>");
            context.Response.Write("<x:ExcelWorkbook>");
            context.Response.Write("<x:ExcelWorksheets>");
            context.Response.Write("<x:ExcelWorksheet>");
            context.Response.Write("<x:Name>" + fileName.Replace(".xls", "").Replace(".xlsx", "") + "</x:Name>");
            context.Response.Write("<x:WorksheetOptions>");
            context.Response.Write("<x:Panes>");
            context.Response.Write("</x:Panes>");
            context.Response.Write("</x:WorksheetOptions>");
            context.Response.Write("</x:ExcelWorksheet>");
            context.Response.Write("</x:ExcelWorksheets>");
            context.Response.Write("</x:ExcelWorkbook>");
            context.Response.Write("</xml>");
            context.Response.Write("<![endif]-->");
            context.Response.Write("</head>");
            context.Response.Write("<body>");
            context.Response.Write(content);
            context.Response.Write("</body>");
            context.Response.Write("</html>");
            context.Response.End();
        }
    }
}
 
 
Pronto, agora em HomeController basta adicionarmos a referência ao diretório Helpers:
 
using ExportacaoExcel.Helper;
 
 
Note que, ao executar a aplicação e clicar no botão de exportação, é disponibilizado o download do arquivo Excel
 
Em próximos artigos mostrarei como realizar a importação de um arquivo Excel.
Até a próxima !!
 
 
Rafael Zaccanini
rafael.zaccanini@gmail.com