您的位置:首页 > 编程语言 > ASP

[ZT] [绝对不是我写的]ASP.NET MVC Tip #2

2010-03-25 09:56 323 查看
[翻译] ASP.NET MVC Tip #2 - 创建可以返回Excel文档的自定义ActionResult

原文地址:http://stephenwalther.com/blog/archive/2008/06/16/asp-net-mvc-tip-2-create-a-custom-action-result-that-returns-microsoft-excel-documents.aspx

摘要:在这个Tip中,Stephen Walther创建了一个自定义的ActionResult,可以由ASP.NET MVC控制器action返回。该ActionResult从一个LINQ to SQL查询生成了一个Excel文档。

译注:从本篇开始,为了方便,仅保留了C#代码。对VB.NET感兴趣的朋友可以参见原文。

在MVC应用程序中,控制器action可以返回一个ActionResult。特别是,他能够返回一些从ActionResult基类继承的东西——

- ViewResult

- EmptyResult

- RedirectResult

- RedirectToRouteResult

- JsonResult

- ContentResult

例如,你可以使用ViewResult向浏览器返回一个特定的视图,使用ContentResult向浏览器返回文本内容。

但是,如果你想向浏览器返回其他类型的内容——如图片、PDF文件或Excel文档呢?在这些情况下,你可以创建自己的ActionResult。在这个Tip中,我会想你展示如何创建一个能返回Excel文档的ActionResult。

清单1包含了ExcelResult的代码。

清单1 - ExcelResult.cs

using System;
using System.Web.Mvc;
using System.Data.Linq;
using System.Collections;
using System.IO;
using System.Web.UI.WebControls;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Drawing;

namespace Tip2
{
public class ExcelResult : ActionResult
{
private DataContext _dataContext;
private string _fileName;
private IQueryable _rows;
private string[] _headers = null;

private 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, string fileName, IQueryable rows, string[] headers)
: this(dataContext, rows, fileName, headers, null, null, null)
{
}

public ExcelResult(DataContext dataContext, IQueryable rows, string fileName, string[] headers, TableStyle tableStyle, TableItemStyle headerStyle, TableItemStyle itemStyle)
{
_dataContext = dataContext;
_rows = rows;
_fileName = fileName;
_headers = headers;
_tableStyle = tableStyle;
_headerStyle = headerStyle;
_itemStyle = itemStyle;

// provide defaults
if (_tableStyle == null)
{
_tableStyle = new TableStyle();
_tableStyle.BorderStyle = BorderStyle.Solid;
_tableStyle.BorderColor = Color.Black;
_tableStyle.BorderWidth = Unit.Parse("2px");
}
if (_headerStyle == null)
{
_headerStyle = new TableItemStyle();
_headerStyle.BackColor = Color.LightGray;
}
}

public override void ExecuteResult(ControllerContext context)
{
// Create HtmlTextWriter
StringWriter sw = new StringWriter();
HtmlTextWriter tw = new HtmlTextWriter(sw);

// Build HTML Table from Items
if (_tableStyle != null)
_tableStyle.AddAttributesToRender(tw);
tw.RenderBeginTag(HtmlTextWriterTag.Table);

// Generate headers from table
if (_headers == null)
{
_headers = _dataContext.Mapping.GetMetaType(_rows.ElementType).PersistentDataMembers.Select(m => m.Name).ToArray();
}

// Create Header Row
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();

// Create Data Rows
tw.RenderBeginTag(HtmlTextWriterTag.Tbody);
foreach (Object row in _rows)
{
tw.RenderBeginTag(HtmlTextWriterTag.Tr);
foreach (string header in _headers)
{
string strValue = row.GetType().GetProperty(header).GetValue(row, null).ToString();
strValue = ReplaceSpecialCharacters(strValue);
if (_itemStyle != null)
_itemStyle.AddAttributesToRender(tw);
tw.RenderBeginTag(HtmlTextWriterTag.Td);
tw.Write( HttpUtility.HtmlEncode(strValue));
tw.RenderEndTag();
}
tw.RenderEndTag();
}
tw.RenderEndTag(); // tbody

tw.RenderEndTag(); // table
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(content);
context.Response.End();
}
}
}

所有的ActionResult都必须直接或间接继承自ActionResult基类。清单1中的ExcelResult就是这样,实际上,它直接继承了ActionResult类。ActionResult基类中有一个方法是必须实现的——Execute()方法。调用Execute()方法会生成ActionResult的结果产生的内容。
在清单1中,Execute()方法用于从Linq to SQL查询生成Excel文档。Execute()方法会调用WriteFile()方法将生成的Excel文档以正确的MIME类型写入到浏览器中。

通常,你不会从控制器action中直接返回一个ActionResult,而是利用Controller类提供的某个方法——

- View()

- Redirect()

- RedirectToAction()

- RedirectToRoute()

- Json()

- Content()

例如,如果你想从一个控制器action中返回一个视图,不要直接返回一个ViewResult,而是调用View()方法。View()方法会实例化一个ViewResult并将这个新的ViewResult返回给浏览器。

清单2中的代码包含三个应用于Controller类的扩展方法。这些扩展方法向Controller类添加了一个名为Excel()的方法。Excel()方法会返回一个ExcelResult。

清单2 - ExcelControllerExtensions.cs (C#)

using System;
using System.Web.Mvc;
using System.Data.Linq;
using System.Collections;
using System.Web.UI.WebControls;
using System.Linq;

namespace Tip2
{
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);
}

}
}

清单3中的控制器展示了如何在控制器中使用Excel()扩展方法。该控制器包含三个方法,名字分别是GenerateExcel1()、GenerateExcel2()和GenerateExcel3()。所有这三个控制器action都返回Excel文档,这些Excel是从Movies数据表生成的。
清单3 - HomeController.cs (C#)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Web.UI.WebControls;
using System.Web;
using System.Web.Mvc;
using Tip2.Models;
using Tip2;

namespace Tip2.Controllers
{
public class HomeController : Controller
{

private MovieDataContext db = new MovieDataContext();

public ActionResult Index()
{
return View();
}

/// <summary>
/// Generates Excel document using headers grabbed from property names
/// </summary>
public ActionResult GenerateExcel1()
{
return this.Excel(db, db.Movies, "data.xls");
}

/// <summary>
/// Generates Excel document using supplied headers
/// </summary>
public ActionResult GenerateExcel2()
{
var rows = from m in db.Movies select new {Title=m.Title, Director=m.Director};
return this.Excel(db, rows, "data.xls", new[] { "Title", "Director" });
}

/// <summary>
/// Generates Excel document using supplied headers and using supplied styles
/// </summary>
public ActionResult GenerateExcel3()
{
var rows = from m in db.Movies select new { Title = m.Title, Director = m.Director };
var headerStyle = new TableItemStyle();
headerStyle.BackColor = System.Drawing.Color.Orange;
return this.Excel(db, rows, "data.xls", new[] { "Title", "Director" }, null, headerStyle, null);
}

}
}

最后,清单4中的Index.aspx视图展示了如何调用GenerateExcel()控制器action来生成Excel文档。注意其中的三个链接使用了GenerateExcel的三个版本。

清单4 - Index.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.aspx.cs" Inherits="Tip2.Views.Home.Index" %>

<!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 runat="server">
<title>Index Page</title>
<style type="text/css">

li
{
margin-bottom: 5px;
}

</style>
</head>
<body>
<div>

<h1>Generate Microsoft Excel Document</h1>

<ul>
<li>
<a href="/Home/GenerateExcel1">Generate</a> - Generates an Excel document by using the entity property names for column headings and the default
formatting.
</li>
<li>
<a href="/Home/GenerateExcel2">Generate</a> - Generates an Excel document by using supplied header names and default formatting.
</li>
<li>
<a href="/Home/GenerateExcel3">Generate</a> - Generates an Excel document by using supplied header names and supplied formatting.
</li>

</ul>
</div>
</body>
</html>

打开Index视图,可以看到如图1所示的页面
图1 - Index.aspx视图



当单击其中一个Generate Excel链接后,你可以得到不同的Excel文档。例如,单击第一个链接之后,你会得到如图2所示的Excel文档。

图2 - Data.xls



一点小瑕疵。单击了某个链接生成Excel文档后,你会看到一个如图3所示的警告。不幸的是,没有什么办法绕过这个警告(关于该警告的更多信息,请参见:http://blogs.msdn.com/vsofficedeveloper/pages/Excel-2007-Extension-Warning.aspx)。

图3 - 来自Microsoft Internet Explorer的警告



仿照该Tip介绍的方法,你可以创建各种类型的ActionResult。例如,你可以创建Image ActionResult、Word ActionResult或者PDF ActionResult。

此处下载源代码:http://weblogs.asp.net/blogs/stephenwalther/Downloads/Tip2/Tip2.zip
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: