2014年7月31日 星期四

[ASP.NET]Global.asax功能筆記

在ASP.NET專案底下有個Global.asax的檔案

該檔案可以在系統發生某些事件的時候被執行

個人用到的是捕捉例外的功能

在此做個筆記

<%@ Application Language="C#" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Security" %>
<script RunAt="server">

    void Application_Start(object sender, EventArgs e)
    {
        // 應用程式啟動時執行的程式碼
    }

    void Application_End(object sender, EventArgs e)
    {
        //  應用程式關閉時執行的程式碼

    }

    void Application_Error(object sender, EventArgs e)
    {
        // 發生未處理錯誤時執行的程式碼
        HttpServerUtility server = HttpContext.Current.Server;
        HttpRequest request = HttpContext.Current.Request;
        HttpSessionState session = HttpContext.Current.Session;
        HttpBrowserCapabilities brower = HttpContext.Current.Request.Browser;
        Exception ex = server.GetLastError();
        System.Net.IPAddress ServerIP = new System.Net.IPAddress(System.Net.Dns.GetHostByName(System.Net.Dns.GetHostName()).AddressList[0].Address);

        StreamWriter sw = null;

        try
        {
            if (ex.InnerException != null)
            {
                ex = ex.InnerException;
            }

            string filePath = string.Format(@"{0}Log\err_log_" + DateTime.Now.ToString("yyyyMMdd") + ".txt", server.MapPath("~/"));

            sw = File.Exists(filePath) ? new StreamWriter(filePath, true) : File.CreateText(filePath);

            sw.WriteLine("----------Strat----------");
            sw.WriteLine("----------[目前時間:" + DateTime.Now.ToString() + "]----------");
            sw.WriteLine("事件發生網頁網址:" + request.Url);
            sw.WriteLine("事件發生路徑:" + request.Path);
            sw.WriteLine("例外狀況訊息:" + ex.Message);
            //事件發生網頁網址

            sw.WriteLine("例外堆疊:" + ex.StackTrace);

            sw.WriteLine("造成錯誤的程式名稱:" + ex.Source);

            //使用者名稱
            if (session != null)
                sw.WriteLine("使用者名稱:" + Session["UserID"].ToString());

            sw.WriteLine("識別系統別:" + request.UserHostAddress);

            sw.WriteLine("IP:" + ServerIP.ToString());

            sw.WriteLine("使用瀏覽器:" + brower.Type);

            sw.WriteLine("是否支援Cookie:" + (brower.Cookies ? "是" : "否"));

            sw.WriteLine("伺服器名稱:" + request.ServerVariables["SERVER_NAME"]);

            sw.WriteLine("----------END----------");

        }
        catch (HttpException HE)
        {
            throw HE;
        }
        catch (ArgumentNullException ANE)
        {
            throw ANE;
        }
        catch (ArgumentException AE)
        {
            throw AE;
        }
        catch (UnauthorizedAccessException UAE)
        {
            throw UAE;
        }
        catch (DirectoryNotFoundException DNFE)
        {
            throw DNFE;
        }
        catch (IOException IOE)
        {
            throw IOE;
        }
        catch (SecurityException SE)
        {
            throw SE;
        }
        finally
        {
            sw.Close();
            sw.Dispose();
        }
    }
</script>


Application_Error可以捕捉網站中的例外

但是如果該例外在網頁中就已經被捕捉

就不會被Application_Error給catch到

所以簡單說Application_Error專門接漏網之魚

這很適合用在你在本機執行沒問題

丟到IIS莫名其妙掛掉的情況

還有一些例外情況但被忽略掉的情形

上面範例就是當發生例外時

會取得該例外物件

然後描述該例外的情形

最好用的就是HttpContext.Current.Request的Url跟Path屬性

他可以指出當前的Request是來自於哪個網址跟路徑

這在IIS上執行有bug時又不曉得bug從何而來很好用

其餘的HttpContext.Current.Server、HttpContext.Current.Session、HttpContext.Current.Request.Browser

都可以讓你取得當下相關資訊並且在Application_Error寫入txt或是資料庫

方便你事後分析問題點

參考文獻
[1] http://www.dotblogs.com.tw/mis2000lab/archive/2008/04/28/3526.aspx
[2] http://sharebody.com/list.asp?id=50684
[3] http://msdn.microsoft.com/zh-tw/library/vstudio/2027ewzw%28v=vs.100%29.aspx
[4] http://www.webjx.com/htmldata/2006-08-14/1155516831.html
[5] http://myprogramlog.blogspot.tw/2013/09/globalasax-applicationbeginrequest.html

2014年7月2日 星期三

[C#]Debugger會呼叫ToString()的問題

為了紀念一下我逝去的青春小鳥(浪費了我一整個下午)

來紀錄一下這次遇到的事件

簡單說我寫了一個組合SQL字串的class

其中裡面有InsertString、DeleteString、UpdateString、WhereString等StringBuilder的物件紀錄執行的動作

當中我override ToString()這個來自System.Object的方法

來輸出組合並輸出的字串

詭異的事情就此發生

在Visual Studio偵錯模式底下執行看程式變化時

有用過的人應該知道可以按F10或F11一步一步執行

其中你游標指到變數還可以看到變數的內容

後來我發現每指向變數一次變數(StringBuilder)的內容就會改變一次

而且用ToString()輸出後的確是有改變

之後我嘗試在ToString()中回傳前指派InsertString、DeleteString、UpdateString、WhereString都為null

沒想到居然會發生參照未指定物件的錯誤

但問題是我根本還沒呼叫ToString()這個方法

怎麼「看起來」好像有去呼叫ToString()

難道是我學藝不精?還是C#有什麼神奇魔力…?

找了半天也跟別人討論

最後才發現這個不曉得算不算bug的問題來源

一般偵錯模式底下執行時我們游標指到變數會顯示的是這個物件完整的class name

但其實這個內容是呼叫ToString()來的

可是ToString()又已經被我Override過了

所以就會發生我遇到明明沒呼叫ToString()卻會執行ToString()內容的靈異事件了

這個問題好像一直到Visual Studio 2012都還有

難道微軟不認為這是bug嗎?

後來我有嘗試方法用new但還是不行

他看來就是一定會去執行這個物件的ToString()不管你是override還是new

看起來他並非去copy出一個新物件

再去呼叫這個新物件的ToString()

所以要解決這個問題你只能取別的名字避免他莫名其妙偷偷去呼叫ToString()

可以參考以下兩篇也是苦主的文章

http://mocheng.wordpress.com/2009/06/06/%E5%88%AB%E8%AE%A9tostring%E5%87%BD%E6%95%B0%E6%94%B9%E5%8F%98%E5%AF%B9%E8%B1%A1%E7%9A%84%E7%8A%B6%E6%80%81/
http://www.xuebuyuan.com/130963.html

2014年7月1日 星期二

[Oracle SQL]使用update指定修改日期欄位

最近遇到日期轉換錯亂的問題

因為程式中在還沒完全改成民國年的情況下就執行

會導致儲存到Oracle上之後

出現3925年(1911+2014)這種結果

但要把時間改回2014年資料有上千筆當然不可能一個一個去改

當然還是要靠update的指令

但問題來了

如果你直接去用to_date改日期

最後的結果可能是變成2014/07/01 00:00:00

原本的時分秒可能因為沒指定被蓋掉

那麼要如何只改年不動到月日時分秒呢?

後來我問到的方法如下

update Login k set k.date=ADD_MONTHS(k.date,-1911*12)
where to_char(k.date,'yyyy')='3925';

這段的意思是說我將現有的時間減掉1911*12個月

至於where的部份必須要用to_char取得年

因為如果是用to_date的話會變成3925/07/01 00:00:00這種結果

當然你的時分秒不會剛好是00:00:00

所以也不會找到符合的結果