5

Tôi có Lớp truy cập dữ liệu, Lớp dịch vụ và Lớp trình bày. Lớp trình bày là ASP.NET MVC2 RTM (web) và Lớp dịch vụ là WCF (các dịch vụ). Đó là tất cả .NET 3.5 SP1.Sử dụng WCF DataContract trong MVC SessionState bằng bộ nhớ cache AppFabric

Vấn đề là trong dịch vụ, các đối tượng được trả lại được đánh dấu bằng thuộc tính [DataContract]. Web đang sử dụng bộ lưu trữ SessionFate Cache (a.k.a Velocity) SessionStateProvider để lưu trữ trạng thái phiên. Do đó, bất cứ điều gì tôi lưu trữ trong phiên phải được serializable.

Ở đây có vấn đề: DataContracts không được đánh dấu bằng [Serializable] và theo như tôi có thể nhớ, bằng cách giới thiệu nó vào một lớp đã được đánh dấu bằng [DataContract] một số vấn đề phát sinh, và vì vậy tôi không tin đây là giải pháp . Ban đầu tôi đã lên kế hoạch sử dụng quyền DataContracts trong lớp web, sử dụng chúng làm mô hình cho các khung nhìn liên quan đến việc kết xuất DataContracts (có thể lồng trong lớp ViewModel cấp cao hơn). Nhưng do nhà cung cấp trạng thái phiên yêu cầu tất cả các đối tượng được lưu trữ bên trong nó để được tuần tự hóa, tôi bắt đầu suy nghĩ lại chiến lược này. Nó sẽ được tốt đẹp để có mặc dù, kể từ khi họ có logic xác nhận bằng cách sử dụng giao diện IDataErrorInfo, và logic xác nhận tương tự có thể được tái sử dụng trong MVC như một phần của mô hình ràng buộc.

Bạn tin điều gì là cách tốt nhất để cho phép tôi giảm bớt công việc cần thiết?

Tôi hiện đã nghĩ ra những cách khác nhau sau đây:

A. Tạo một phần 'ServiceIntegration' trong dự án web.

Đây sẽ là một người đàn ông trung gian giữa bộ điều khiển của tôi và lớp dịch vụ WCF của tôi. Phần ServiceIntegration sẽ nói chuyện với lớp dịch vụ bằng cách sử dụng DataContracts, và tới lớp Web bằng cách sử dụng ViewModels, nhưng sẽ phải chuyển đổi giữa DataContracts và ViewModels bằng cách sử dụng Transformer hai chiều. Ngoài ra, vì IDataErrorInfo Validation sẽ không thể sử dụng lại được, nên cũng cần phải tạo một Validator cho DataContract, sử dụng Transformer để chuyển đổi từ ViewModel sang DataContract, thực hiện xác nhận bằng IDataErrorInfo và trả về kết quả của nó. Điều này sau đó sẽ được sử dụng trong phương pháp hành động của Bộ xử lý (ví dụ if (!MyValidator.IsValid(viewModel)) return View();)

lớp khác nhau yêu cầu: xDataContract, xViewModel, xTransformer, xValidator

B. Tạo một phần 'SessionIntegration' trong dự án web

Đây sẽ là một người đàn ông trung gian giữa các bộ điều khiển (hoặc bất kỳ thứ gì truy cập phiên) và chính phiên đó. Bất cứ điều gì yêu cầu truy cập vào phiên sẽ đi qua lớp này. DataContracts sẽ được sử dụng trong toàn bộ ứng dụng, trừ khi chúng được lưu trữ trong phiên. Phần SessionIntegration sẽ chịu trách nhiệm chuyển đổi DataContract thành một dạng ISerializable và ngược lại. Không cần Validator bổ sung vì việc sử dụng giao diện IDataErrorInfo trên DataContract.

lớp khác nhau yêu cầu: xDataContract, xTransformer, xSerializableForm


Lưu ý: vẫn sẽ là ViewModels xung quanh trong cả hai kịch bản, tuy nhiên với (B) Tôi muốn có thể soạn ViewModels từ DataContracts.

(B) có lợi ích là không cần thêm người xác thực.


Trước khi tôi đi và thực hiện (A)/(B) đầy đủ, tôi muốn một số phản hồi. Hiện tại, tôi bắt đầu nghiêng về phía (B), tuy nhiên, (A) có thể linh hoạt hơn. Dù bằng cách nào, nó có vẻ như cách quá nhiều công việc cho những gì nó có giá trị. Có ai khác bắt gặp vấn đề này không, bạn có đồng ý/không đồng ý với tôi và/hoặc bạn có cách nào giải quyết vấn đề này không?

Cảm ơn,

James

+0

Tôi có thể sử dụng AutoMapper như một phần của Tr ansformer, và vì vậy các chi tiết lập bản đồ có thể không phải là một chi phí trên. Lập bản đồ thủ công các ViewModels thành DataContracts và ngược lại chắc chắn là rất nhiều công việc cho những gì nó có giá trị, IMHO – jamiebarrow

Trả lời

5

Mà không đi con đường thổi đầy đủ của A hoặc B, có thể bạn chỉ cần thực hiện một đối tượng wrapper ISerializable chung chung và đưa những người trong sessionState của bạn?

[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ảm ơn câu trả lời. Sẽ thử nó khi tôi tiếp theo ở máy dev của tôi. Tôi kinda mới đến C#, do đó, không phải 100% trên chi tiết cụ thể. Hàm tạo mà bạn đã tạo có chứa SerializationInfo và StreamingContext, đó là gì? Tôi giả sử mục đích sử dụng sẽ là: MyDataContract c = ...; Phiên ["mykey"] = Trình bao bọc mới {Value = c}; – jamiebarrow

+0

Khi bạn kế thừa ISerializable, bạn phải định nghĩa một hàm tạo với chữ ký đó, đó là những gì mà BinaryFormatter (được sử dụng trong nội bộ bởi SessionState để tuần tự hóa) sử dụng để xây dựng lại đối tượng trong quá trình deserialization. Đó là một ngụm, nhưng đọc này: http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserializable.aspx (vài dòng đầu tiên là những gì địa chỉ câu hỏi của bạn) – Jeff

+0

Vâng, đó là dự định sử dụng. – Jeff

3

Là phần mở rộng cho câu trả lời được cung cấp, tôi đã thêm hai phương pháp này để dễ dàng lưu trữ/truy xuất dữ liệu.

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); 
    } 

Điều này làm cho nó dễ dàng hơn một chút để thiết lập/lấy giá trị từ phiên:

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

Để làm cho nó thậm chí còn dễ dàng hơn, thêm các phương pháp mở rộng:

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); 
    } 
} 

Và sử dụng như sau :

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

rất đẹp:) ... – Jeff