5

我有一個數據訪問層,一個服務層和一個表示層。表示層是ASP.NET MVC2 RTM(web),服務層是WCF(服務)。這都是.NET 3.5 SP1。使用AppFabric緩存在MVC SessionState中使用WCF DataContract

問題是,在服務中,返回的對象被標記爲[DataContract]屬性。網絡使用AppFabric Cache(a.k.a Velocity)SessionStateProvider存儲會話狀態。由於這個原因,我在會話中存儲的任何內容都必須是可序列化的。

問題出在這裏:DataContracts沒有標記爲[Serializable],據我所知,通過將它引入已經標記爲[DataContract]的類中,出現了一些問題,所以我不相信這是一個解決方案。

我最初計劃在Web層中使用DataContracts,將它們用作與渲染DataContracts(可能嵌套在更高級別的ViewModel類中)相關的視圖模型。但由於會話狀態提供程序要求存儲在其中的所有對象都是可序列化的,因此我開始重新考慮這種策略。儘管如此,它會很好,因爲它們包含使用IDataErrorInfo接口的驗證邏輯,並且相同的驗證邏輯可以作爲模型綁定的一部分在MVC中重新使用。

您認爲什麼是最好的方式讓我減少所需的工作?

我目前認爲以下不同的方法:

A.在WEB項目「ServiceIntegration」的一部分。

這將是我的控制器和我的WCF服務層之間的中間人。 ServiceIntegration部分將使用DataContracts與服務層對話,使用ViewModels與Web層對話,但必須使用雙向Transformer在DataContracts與ViewModels之間進行轉換。

此外,由於IDataErrorInfo驗證不可重複使用,因此也需要爲每個DataContract創建一個Validator,它使用Transformer將ViewModel轉換爲DataContract,使用IDataErrorInfo執行驗證並返回其結果。這將被內部控制器的操作方法,使用(例如if (!MyValidator.IsValid(viewModel)) return View();

不同類別的要求:xDataContract,xViewModel,xTransformer,xValidator

B.在WEB項目 'SessionIntegration' 部分

這將是控制器(或任何訪問會話)和會話本身之間的中間人。任何需要訪問會話的東西都會通過這個類。 DataContracts將在整個應用程序中使用,除非它們存儲在會話中。 SessionIntegration部分將負責將DataContract轉換爲一些ISerializable形式,並返回。由於在DataContract上使用了IDataErrorInfo接口,因此不需要額外的Validator。

需要不同類別:xDataContract,xTransformer,xSerializableForm


注:仍然會存在的ViewModels圍繞這兩種方案,但是與(B)我能夠從DataContracts組成的ViewModels。 (B)具有不需要額外的驗證器的好處。


在我完全執行(A)/(B)之前,我想要一些反饋。目前,我開始傾向於(B),但是,(A)可能更加靈活。無論哪種方式,它的價值似乎太多了。有沒有人遇到過這個問題,你同意/不同意我和/或你有任何其他解決方法嗎?

感謝,

詹姆斯

+0

我可能會使用AutoMapper作爲Tr的一部分ansformer,所以映射細節可能不是這樣的開銷。將ViewModels手動映射到DataContracts,反之亦然,對於它的價值肯定是很多工作,恕我直言 – jamiebarrow 2010-08-31 18:59:18

回答

5

沒有去A或B的完全成熟的路線,可能你只是做一個普通的ISerializable的包裝對象,把那些在您的SessionState的?

[Serializable] 
    public class Wrapper : ISerializable 
    { 
     public object Value { get; set; } 

     void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) 
     { 
      if (Value != null) 
      { 
       info.AddValue("IsNull", false); 
       if (Value.GetType().GetCustomAttributes(typeof(DataContractAttribute), false).Length == 1) 
       { 
        using (var ms = new MemoryStream()) 
        { 
         var serializer = new DataContractSerializer(Value.GetType()); 
         serializer.WriteObject(ms, Value); 
         info.AddValue("Bytes", ms.ToArray()); 
         info.AddValue("IsDataContract", true); 
        } 
       } 
       else if (Value.GetType().IsSerializable) 
       { 
        info.AddValue("Value", Value); 
        info.AddValue("IsDataContract", false); 
       } 
       info.AddValue("Type", Value.GetType()); 
      } 
      else 
      { 
       info.AddValue("IsNull", true); 
      } 
     } 

     public Wrapper(SerializationInfo info, StreamingContext context) 
     { 
      if (!info.GetBoolean("IsNull")) 
      { 
       var type = info.GetValue("Type", typeof(Type)) as Type; 

       if (info.GetBoolean("IsDataContract")) 
       { 
        using (var ms = new MemoryStream(info.GetValue("Bytes", typeof(byte[])) as byte[])) 
        { 
         var serializer = new DataContractSerializer(type); 
         Value = serializer.ReadObject(ms); 
        } 
       } 
       else 
       { 
        Value = info.GetValue("Value", type); 
       } 
      } 
     } 
    } 
+0

感謝您的答案。當我在我的開發機器上時,會試用它。我對C#有點新,所以不是100%的具體細節。你創建的構造函數包含SerializationInfo和StreamingContext,這是什麼? 我假設預期的用途是: MyDataContract c = ...; Session [「mykey」] = new Wrapper {Value = c}; – jamiebarrow 2010-08-31 19:45:03

+0

當您繼承ISerializable時,您必須定義一個具有該簽名的構造函數,這是BinaryFormatter(由SessionState內部用於序列化)在反序列化期間用於重構對象的內容。這是一口,但閱讀:http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserializable.aspx(前幾行是什麼解決您的問題) – Jeff 2010-08-31 19:57:19

+0

是的,這是打算使用。 – Jeff 2010-08-31 20:11:19

3

作爲提供的答案的擴展,我添加了這兩種方法來簡化存儲/檢索數據。

public static void Set<T>(HttpSessionStateBase session, string key, T value) 
    { 
     session[key] = new Wrapper(value); 
    } 

    public static T Get<T>(HttpSessionStateBase session, string key) 
    { 
     object value = session[key]; 
     if (value != null && typeof(T) == value.GetType()) 
     { 
      return (T) value; 
     } 
     Wrapper wrapper = value as Wrapper; 
     return (T) ((wrapper == null) ? null : wrapper.Value); 
    } 

這使得它更容易一些設置/從會話中獲取值:

MyDataContract c = ...; 
    Wrapper.Set(Session, "mykey", c); 
    c = Wrapper.Get<MyDataContract>(Session, "mykey"); 

爲了使其更容易,加上擴展方法:

public static class SessionWrapperEx 
{ 
    public static void SetWrapped<T>(this HttpSessionStateBase session, string key, T value) 
    { 
     Wrapper.Set<T>(session, key, value); 
    } 

    public static T GetWrapped<T>(this HttpSessionStateBase session, string key) 
    { 
     return Wrapper.Get<T>(session, key); 
    } 
} 

並如下使用:

MyDataContract c = ...; 
    Session.SetWrapped("mykey", c); 
    c = Session.GetWrapped<MyDataContract>("mykey"); 
+0

非常好:)... – Jeff 2010-09-01 14:26:43