我們在 ASP.NET MVC (7) 中曾介紹了 ASP.NET MVC 的 View 內建了 9 種不同的 View,基本上這些 View 均足以應付大部份的資料呈現需求,但是它也不是不能擴充的,像是一些特殊的 View (例如圖表或特殊檔案或特別的資料格式),就需要由開發人員自己設計,不過 MVC 的 View 設計上也不難,只要將自己的 View 加入 HTTP 處理流程中即可。
我們在 ASP.NET MVC (7) 中曾介紹了 ASP.NET MVC 的 View 內建了 9 種不同的 View,基本上這些 View 均足以應付大部份的資料呈現需求,但是它也不是不能擴充的,像是一些特殊的 View (例如圖表或特殊檔案或特別的資料格式),就需要由開發人員自己設計,不過 MVC 的 View 設計上也不難,只要將自己的 View 加入 HTTP 處理流程中即可。
我們在 Controller 中的每一個動作,最後都會回傳一個 View,而預設的型別是 ActionResult,ActionResult 是 View 的基底類別,MVC 內建的 9 種 View 都衍生自 ActionResult,也就是說,我們可以透過繼承這個抽象類別,來取得產生自己需要的 View 的基本功能,而我們只需要覆寫 ActionResult.ExecuteResult() 方法即可。
所以我們在專案中新增一個類別,命名為 CsvResult,並設定它繼承 ActionResult 類別 (Visual Studio 會偵測是否已實作必要成員,將會以一個小的底線符號通知)。而本例是使用泛型的方式產生 CSV,因此可直接使用下列程式碼取代:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Reflection;
5: using System.Text;
6: using System.Web;
7: using System.Web.Mvc;
   8:  
		9: namespace MvcApplication1
  10: {
		11: public class CsvResult<TModel, T> : ActionResult where TModel : IEnumerable<T>
  12:     {
		13: public string FileName { get; set; }
14: public TModel Model { get; set; }
  15:  
		16: public override void ExecuteResult(ControllerContext context)
  17:         {
		18: StringBuilder csvBuilder = new StringBuilder();
  19:  
		
  20:             context.HttpContext.Response.Clear();
		
  21:  
		22: context.HttpContext.Response.Buffer = true;
23: context.HttpContext.Response.ContentType = "text/csv";
24: context.HttpContext.Response.AddHeader("content-disposition", "attachment; filename=" + this.FileName);
  25:  
		26: PropertyInfo[] modelProperties = typeof(T).GetProperties();
27: StringBuilder itemBuilder = new StringBuilder();
  28:  
		29: // render header.
30: foreach (PropertyInfo modelProperty in modelProperties)
  31:             {
		32: if (modelProperty.DeclaringType == typeof(T))
  33:                 {
		34: if (itemBuilder.Length == 0)
  35:                         itemBuilder.Append(modelProperty.Name);
		36: else
37: itemBuilder.Append(",").Append(modelProperty.Name);
  38:                 }
		
  39:             }
		
  40:  
		
  41:             csvBuilder.Append(itemBuilder);
		
  42:  
		43: // render body.
44: foreach (T item in this.Model)
  45:             {
		
  46:                 csvBuilder.Append(Environment.NewLine);
		
  47:                 itemBuilder.Clear();
		
  48:  
		49: foreach (PropertyInfo modelProperty in modelProperties)
  50:                 {
		51: if (modelProperty.DeclaringType == typeof(T))
  52:                     {
		53: if (itemBuilder.Length == 0)
54: itemBuilder.Append(modelProperty.GetValue(item, null).ToString());
55: else
56: itemBuilder.Append(",").Append(modelProperty.GetValue(item, null).ToString());
  57:                     }
		
  58:                 }
		
  59:  
		
  60:                 csvBuilder.Append(itemBuilder);
		
  61:             }
		
  62:  
		
  63:             context.HttpContext.Response.Write(csvBuilder.ToString());
		
  64:         }
		
  65:     }
		
  66: }
	
完成後,在 MyController 中,加入一個 CustomerCsv 的方法:
1: public ActionResult CustomerCsv()
   2: {
		
		
   3:     var model = Models.CustomerDataContext.LoadCustomers();
		
		4: return new CsvResult<IEnumerable<Models.Customer>, Models.Customer>() { FileName = "Customers.csv", Model = model };
   5: }
		
然後啟動程式,打開瀏覽器,瀏覽 http://[root]/My/CustomerCsv,你會發現檔案已自動下載到瀏覽器的下載資料夾內:
打開來看,Northwind 的 Customers 的資料都會在裡面,不過會只有宣告在 Model.Customer 的屬性的資料才會出現。
Reference: