最近在做公司日志導出的優化,需求一般都是將List<T>泛型列表數據導出到Excel中,並且只要求導出指定的字段。最終做出的優化方案是:通過自定義屬性比較實體類字段,將數據列表通過反射實體類轉換成DataTable,再將DataTable轉換成帶格式的XMl字符串(在轉換的過程中定義XSLT模板樣式,定義的樣式支持Excel或者CSV打開方法),最后保存到文件中。
組件說明:
組件主要由Export、Extension、DataDisplayNameAttribute三個部分組成。
DataDisplayNameAttribute:導出字段標記屬性,在導出時自動識別哪些字段需要導出。不需要收到去配置。
Extension:提供了List<T>的擴展方法ToDataTable,將List轉換為DataTable。
Export提供導出功能,該類里邊提供了ExportDetails方法以及其重載方法,將DataTable傳入ExportDetails方法,導出原理即是在生成XML時通過XSLT轉化,將XML轉化成可以通過Excel打開的格式,該方式導出效率比較高。
導出效率方面:10000條數據,導出時間不到1秒鍾;100000條數據,導出時間為12秒到13秒鍾;1000000條數據,導出時間為151秒鍾。
異常處理:如果在執行過程中出現異常,該組件直接將異常拋到上一級,讓使用者自己處理異常,以便信息提示。
下面就掛上詳細的代碼實現:
DataDisplayNameAttribute(主要是DisplayName屬性,用於比較實體類)
1 public class DataDisplayNameAttribute : Attribute 2 { 3 public DataDisplayNameAttribute(string displayName) 4 { 5 DisplayName = displayName; 6 } 7 8 public string DisplayName { get; set; } 9 }
List<T>的擴展方法ToDataTable(主要是結合自定義屬性篩選需要導出的字段,然后轉換成DataTable):
public static DataTable ToDataTable<T>(this IList<T> data) { var properties = typeof(T).GetProperties(); var dataTable = new DataTable(); var indexList = new List<int>(); for (var i = 0; i < properties.Length; i++) { var displayAttributes = properties[i].GetCustomAttributes(typeof(DataDisplayNameAttribute), false); if (displayAttributes.Length > 0) { var displayName = ((DataDisplayNameAttribute)displayAttributes[0]).DisplayName; dataTable.Columns.Add(displayName, properties[i].PropertyType); indexList.Add(i); } } foreach (var item in data) { var values = new object[indexList.Count]; for (var i = 0; i < values.Length; i++) { values[i] = properties[indexList[i]].GetValue(item, null); } dataTable.Rows.Add(values); } return dataTable; }
數據導出方法ExportDetails(ExportFormat包含Excel和CSV導出格式):
public void ExportDetails(DataTable detailsTable, ExportFormat formatType, string fileName) { try { if (detailsTable.Rows.Count == 0) throw new Exception("There are no details to export."); // Create Dataset DataSet dsExport = new DataSet("Export"); DataTable dtExport = detailsTable.Copy(); dtExport.TableName = "Values"; dsExport.Tables.Add(dtExport); // Getting Field Names string[] sHeaders = new string[dtExport.Columns.Count]; string[] sFileds = new string[dtExport.Columns.Count]; for (int i = 0; i < dtExport.Columns.Count; i++) { //sHeaders[i] = ReplaceSpclChars(dtExport.Columns[i].ColumnName); sHeaders[i] = dtExport.Columns[i].ColumnName; sFileds[i] = ReplaceSpclChars(dtExport.Columns[i].ColumnName); } Export_with_XSLT_Windows(dsExport, sHeaders, sFileds, formatType, fileName); } catch (Exception Ex) { throw Ex; } }
接下來給出組件的使用Demo。先定義一個實體類:
public class Device { public string Id { get; set; } [DataDisplayName("設備名稱")] public string Name { get; set; } [DataDisplayName("設備類型")] public string Type { get; set; } [DataDisplayName("設備狀態")] public string State { get; set; } [DataDisplayName("設備位置")] public string Location { get; set; } [DataDisplayName("設備地址")] public string ServerIp { get; set; } public string Scheme { get; set; } }
創建數據源:
private static List<Device> CreateDeviceList() { var dataList = new List<Device>(); for (var i = 0; i < 1000000; i++) { dataList.Add(new Device() { Id = i.ToString(), Location = "位置" + i, Name = "設備" + i, ServerIp = "25.30.5." + i, State = "可用", Type = "攝像頭" }); } return dataList; }
導出方法:
private static void Export<T>(List<T> dataList) { var fileName = "C:\\export.xls"; Console.WriteLine("文件默認導出在" + fileName); var startTime = DateTime.Now; var export = new Export(); var dataTable = dataList.ToDataTable(); export.ExportDetails(dataTable, TYComLib.Export.Export.ExportFormat.Excel, fileName); var endTime = DateTime.Now; var timeSpan = endTime - startTime; Console.WriteLine(string.Format("導出用時:{0}秒", timeSpan.Seconds)); }
由於自己也是小菜,歡迎各位土豪拍磚!