5

У меня есть уровень доступа к данным, уровень обслуживания и уровень презентации. Уровень представления - это RTM (веб-интерфейс) ASP.NET MVC2, а уровень сервиса - WCF (услуги). Это все .NET 3.5 SP1.Использование WCF DataContract в MVC SessionState с использованием кэша AppFabric

Проблема в том, что в сервисах возвращаемые объекты помечены атрибутом [DataContract]. Для хранения состояния сеанса веб использует кеш AppFabric Cache (a.k.a Velocity) SessionStateProvider. Из-за этого все, что я храню в сеансе, должно быть сериализуемым.

Здесь возникает проблема: DataContracts не помечены [Serializable], и насколько я помню, вводя его в класс, уже отмеченный [DataContract], возникают некоторые проблемы, и поэтому я не считаю, что это решение ,

Сначала я планировал использовать DataContracts прямо на веб-уровне, используя их в качестве моделей для представлений, связанных с рендерингом DataContracts (возможно, вложенным в класс ViewModel более высокого уровня). Но из-за провайдера состояния сеанса, требующего, чтобы все объекты, хранящиеся внутри него, были сериализуемыми, я начинаю пересматривать эту стратегию. Было бы неплохо, хотя, поскольку они содержат логику проверки с использованием интерфейса IDataErrorInfo, и та же логика проверки может быть повторно использована в MVC как часть привязки модели.

Что вы считаете лучшим способом позволить мне сократить необходимую работу?

В настоящее время я думал о следующих различными способами:

A. Создание «» ServiceIntegration участие в веб-проекте.

Это был бы средний человек между моими контроллерами и моим сервисом WCF. Часть ServiceIntegration будет ссылаться на уровень обслуживания с использованием DataContracts и на веб-уровень с помощью ViewModels, но ему придется преобразовывать между DataContracts и ViewModels с использованием двухстороннего Transformer.

Кроме того, поскольку проверка идентификатора IDataErrorInfo не будет повторно использоваться, необходимо также создать валидатор для каждого DataContract, который использует Transformer для преобразования из ViewModel в DataContract, выполняет проверку с использованием IDataErrorInfo и возвращает его результаты. Это будет затем использоваться внутри методов действия контроллеров (например if (!MyValidator.IsValid(viewModel)) return View();)

Различные классы необходимы: xDataContract, xViewModel, xTransformer, xValidator

B. Создание часть 'SessionIntegration' в веб-проекте

Это будет средний человек между контроллерами (или что-либо доступ к сеансу) и сам сеанс. Все, что требует доступа к сеансу, будет проходить через этот класс. DataContracts будут использоваться во всем приложении, если они не хранятся в сеансе. Часть SessionIntegration возьмет на себя ответственность за преобразование DataContract в некоторую ISerializable форму и обратно. Никакой дополнительный валидатор не требуется из-за использования интерфейса IDataErrorInfo в DataContract.

Различные классы, необходимые: xDataContract, xTransformer, xSerializableForm


Примечание: все еще будет ViewModels вокруг в обоих случаях, однако, с (B) я смог бы составить ViewModels из DataContracts.

(B) имеет преимущество, не требуя дополнительного валидатора.


Прежде чем я уйду и реализую (A)/(B) полностью, мне нужна некоторая обратная связь. На данный момент я начинаю наклоняться к (B), однако (A) может быть более гибким. В любом случае, похоже, слишком много работы для того, что стоит. Кто-нибудь еще сталкивается с этой проблемой, согласны/не согласны со мной и/или у вас есть другой способ решения проблемы?

Спасибо,

Джеймс

+0

Я могу использовать AutoMapper как часть Tr ansformer, и поэтому данные отображения могут быть не такими накладными расходами. Вручную сопоставление ViewModels с DataContracts и наоборот - определенно большая работа для того, что стоит, IMHO – jamiebarrow

ответ

5

Не вдаваясь в полномасштабную маршрут А или В, может вы просто сделать общий 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

Спасибо за ответ. Попробуй, когда я буду на моей машине dev. Я новичок в C#, поэтому не на 100% на специфику. Созданный вами конструктор, содержащий SerializationInfo и StreamingContext, для чего это нужно? Предполагалось, что предполагаемое использование будет: MyDataContract c = ...; Сессия ["mykey"] = new Wrapper {Value = c}; – jamiebarrow

+0

Когда вы наследуете ISerializable, вам нужно определить конструктор с этой сигнатурой, что и используется BinaryFormatter (используемое внутри SessionState для сериализации) для повторной конструкции объекта во время десериализации. Это глоток, но прочтите это: http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserializable.aspx (первые несколько строк относятся к вашему вопросу) – Jeff

+0

Да, это использовать. – Jeff

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

Смежные вопросы