Wednesday, November 7, 2012

[C#] Custom Class in StateServer / SQLServer

這是最近遇到的一個問題,解決之後覺得有價值記錄一下,於是就來寫這篇了。

ASP.NET的 session state 有幾種模式可以設定

  • InProc : 將session state存在那台web server的記憶體中。(預設值)
  • StateServer : 將session stste存在特定的有開啟ASP.NET state service的機器上,這個模式在多台web server共用一台stste server的狀況下,可以在做web server重開、切換這些動作時,同時保持session存在,避免需要session的工作產生錯誤。
  • SQLServer : 將session stste存到指定的SQL server中,效果同StateServer只是儲存媒體不同。
  • Custom : 將session state存到自訂的儲存媒介內。
  • Off : 不使用session

一般常用到的模式會是InProc、StateServer、SQLServer這三種(Custom先跳過不理),InProc預設值通常是不會遇到問題的,但是如果你在開發時有用一些Custom Class(或是某些特定類別),那在Session State改成StateServer/SQLServer(以下稱為Session Server)時,就會遇到下面這個錯誤。

英文版
Server Error in '/' Application.
Unable to serialize the session state. In 'StateServer' and 'SQLServer' mode, ASP.NET will serialize the session state objects, and as a result non-serializable objects or MarshalByRef objects are not permitted. The same restriction applies if similar serialization is done by the custom session state store in 'Custom' mode.

[SerializationException: Type 'MyCustomObj' in Assembly 'MyObj, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.]
'/' 應用程式中發生伺服器錯誤。
無法序列化工作階段狀態。在 'StateServer' 和 'SQLServer' 模式中,ASP.NET 將序列化工作階段狀態物件,因此不允許無法序列化的物件或 MarshalByRef 物件。在 'Custom' 模式中,自訂工作階段狀態存放區執行類似的序列化作業時,也會有同樣的限制。

[SerializationException: 未將型別 'MyCustomObj' (於組件 'MyObj, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' 中) 標記為可序列化。]


原因是使用Session Server,ASP.NET必須將Session內容做序列化(Serialization),但是某些類別是無法序列化的,像是SessionStateSqlDataSource...都無法被序列化,另外Custom Class,因為ASP.NET不認得它,所以當然也不能自動的序列化放進session內,於是就會有上面那個錯誤。

以Custom Class來說,若Class內都是用一些簡單的型態像是String,Dictionary,List之類,這些ASP.NET都可以自動的做好序列化,那這時候在Class宣告上加上[Serializable]標記就好,這樣ASP.NET就會知道這個Custom Class是可序列化的物件,因而可以丟進Session。(簡單範例如下)
[Serializable]
public class MyCustomObj
{
  public int n1;
  public int n2;
  public string str;
}

但是在一些情況下,例如Custom Class又包了Custom Class的時候,ASP.NET就沒辦法自動認得要如何序列化這些物件,就必須要自己去實做方法,才能正常的丟到Session內了。


[Serializable]
public class People
{
  public int Age;
  public int ID;
  public string Name;
}

[Serializable]
public class Department
{
  public int BossID;
  public string Name;
  public List Member;
  public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("BossID", BossID);
            info.AddValue("Name", Name);
            info.AddValue("Member", Member);
        }
  protected Department(SerializationInfo info, StreamingContext context)
        {
            BossID= info.GetInt32("BossID");
            Name= info.GetString("Name");
            Member= (List)info.GetValue("Member", typeof(List));
        }

實作的方法大概類似如上,簡單來說就只是幫ASP.NET做好進出session時該做什麼事而已。

如果你遇到的情況是在Session裡面又放了一些沒辦法序列化的.Net原生物件,那只好自己想辦法改結構,讓這些東西不放進去了(但是會花很久時間就是)



資料參考:
Session-State Modes
Custom Serialization

No comments:

Post a Comment