Tuesday, January 13, 2009

食Excel

背景
近期寫了個module係去食user一個upload的Excel,再將D data process過後放入DB。
基於flexibility及re-usability,我就寫了個C# function將個Excel transform 落 System.Data.DataSet入面,個logic大概係咁:
  1. 用OleDB開個connection落個Excel到
  2. 用GetSchema("Tables")去食個Excel個list of available worksheet
  3. 憑Step 2 return回來的list去用 SELECT * FROM "worksheet name" 去將每張worksheet的全部data放入dataset入面
本來試就好地地,到個system上了production,user真係用的時候(又一個缺乏user合作UAT的結果),就發現無法process 個uploaded excel。

GetSchema("Tables")
請求user提供那個excel後,行debug mode看,才發現當我用GetSchema("Tables")去get個list of worksheet時,有很多很多不知名table走了出來,Google後發現該是一些user於excel行filter後留下來的working temp worksheet。不過,基於MSDN並無提及到對個Excel 用GetSchema("Tables") return 回來的data有任何詳細的解釋,結果唯有在各forum的提示下,用一個不大安全的方法去做個workaround。

Solution
首先,經GetSchema("Tables")而穫得的table 名是在worksheet 名加上"$",在各Forum提示下,這類working temp worksheet個table name的pattern是 worksheet name + "$" + others (like GUID),因此,我就假設,逢table name最尾一個"$" 後有其他字串的話,就會ignore有關table,因此就即是:
// Get list of available worksheets by using GetSchema
DataTable dtSchemaInfo = conn.GetSchema("Tables");

foreach (DataRow drSchemaInfo in dtSchemaInfo.Rows)
{
    if (drSchemaInfo["TABLE_NAME"] is string && Convert.ToString(drSchemaInfo["TABLE_NAME"]) != null && Convert.ToString(drSchemaInfo["TABLE_NAME"]) != string.Empty)
    {
        // Only handle table ended with $ or $' 
        string tableName = Convert.ToString(drSchemaInfo["TABLE_NAME"]);
        if(!(tableName.LastIndexOf("$'") > 0 && tableName.Substring(tableName.LastIndexOf("$'") + 2) == string.Empty) &&
               !(tableName.LastIndexOf("$") > 0 && tableName.Substring(tableName.LastIndexOf("$") + 1) == string.Empty))
            continue;

        // Select all data from the worksheet
        DbDataAdapter adapter = factory.CreateDataAdapter();
        adapter.SelectCommand = conn.CreateCommand();
        adapter.SelectCommand.CommandText = string.Format(TEMPLATE_SQL_GET_ALL_DATA, tableName);
        try
        {
            adapter.Fill(_dsExcel, tableName);
        }
        catch
        {
            ;
        }
        finally
        {
            adapter.Dispose();
        }
    }
}

最後,本人coding經驗尚未算深,如有錯漏,敬請指正~

No comments: