工作的專案有個小需求,使用者羅列了一堆報表匯出需求,基上都是從現存LINQ資料集合以不同條件取出不同欄位。

我想到最簡便的做法是套用Where()查詢,依需求產生匿名型別

Select(o => new {
    欄位1 = o.PropA,
    欄位2 = o.PropB,
    欄位3 = o.PropC …
})

連欄位名稱都隨使用者指定,最後再將查詢結果轉為CSV,幾個步驟就搞定一項報表需求,進行量產。

基於以上構想,我需要一個能將任意匿名型別陣列或List自動轉成CSV的共用函數,挑戰點在於陣列元素型別未知,但這可難不倒C#,祭出Reflection就能輕鬆克服。

試寫範例如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
 
namespace AnonymousTypeListReflectionTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var ary = "Jeffrey,Darkthread,Geek".Split(',')
                .Select(o => new
                {
                    文字 = o,
                    長度 = o.Length
                }).ToArray();
            Console.Write(convToCsv(ary));
            Console.Read();
        }
        //將任意型別陣列輸出為CSV,第一列為標題列舉欄位名稱
        static string convToCsv(Array ary)
        {
            //取得陣列元素的型別
            Type elemType = ary.GetType().GetElementType();
            PropertyInfo[] props = elemType.GetProperties();
            StringBuilder sb = new StringBuilder();
            //第一列輸出屬性名稱
            sb.AppendLine(string.Join("\t", props.Select(o => o.Name).ToArray()));
            //藉由foreach巡迴每一元件,透過Refelction取出屬性值
            foreach (object elem in ary)
            {
                sb.AppendLine(string.Join("\t",
                    props.Select(o => Convert.ToString(o.GetValue(elem, null)))));
            }
            return sb.ToString();
        }
    }
}

實際寫完,程式遠比預期來得單純: 傳入參數時,將匿名型別陣列轉型成Array、GetElementType()可以找出陣列元素型別、Reflection GetProperties()跟PropertyInfo.GetValue()已是老把戲不用多說,至於分隔符號我選擇用Tab "\t"取代逗號,以省去處理屬性值內含逗號的麻煩。

就這樣,任意型別陣列轉CSV的函數就寫完囉~ 又到了感恩時刻,讓我們一起高呼: .NET好威呀!


Comments

# by Lonka

您好! 這個方法去轉CSV真的很方便。 有個問題想問一下,您在文中有提到「連欄位名稱都隨使用者指定」,就是您提到的「依需求產生匿名型別」。 我想請問的就是要如何用程式動態的依照使用者需求產生匿名型別? 感謝

# by Jeffrey

to Lonka, 文中所提情境,主要在簡化使用者提了大量報表需求時的開發。若希望能做到不需事前提規格,100%即興演出,就得實做出動態LINQ查詢,方法蠻多的,我想最簡便的是透過Dynamic LINQ程式庫。[可參考: http://blog.miniasp.com/post/2008/06/23/Introduce-LINQ-Dynamic-Query-Library.aspx]

# by Lonka

to Jeffrey, 謝謝你撥空回答我的問題,還費心的給了我一個很好的方向。Dynamic LINQ library確實能解決我的問題。感謝 :)

Post a comment