Использование DataContrattratt WCF в SessionSate MVC с использованием AppFabric Cache
-
26-09-2019 - |
Вопрос
У меня есть уровень доступа к данным, сервисный слой и слой презентации. Слой презентации представляет собой ASP.NET MVC2 RTM (WEB), а сервисный слой является WCF (услуги). Это все .NET 3.5 SP1.
Проблема в том, что в сервисах возвращаются объекты, отмечены [DataContract]
атрибут. Веб использует сеанс SESSICSTATEPADER CACHE (AKA VELOCITY) для хранения состояния сеанса. Благодаря этому, все, что я храним в сеансе, должен быть сериализуемым.
Вот проблема: DataContracts не отмечены [Serializable]
И насколько я могу вспомнить, введя его на класс, уже отмеченный [DataContract]
Некоторые проблемы возникают, и поэтому я не верю, что это решение.
Первоначально я был планировал использовать DataContracts прямо в веб-слое, используя их в виде моделей для представлений, связанных с рендерингом данных DataContracts (вероятно, вложенные внутри класса ViewModel Review Models). Но благодаря провайдеру государства заседаний, требующих всех объектов, хранящихся внутри него, чтобы быть сериализуемым, я начинаю переосмыслить эту стратегию. Было бы неплохо иметь, так как они содержат логику валидации, используя IDataErrorInfo
Интерфейс, а та же самая логика валидации может быть повторно использована в MVC как часть моделей связывания.
Что вы считаете лучшим способом позволить мне уменьшить необходимую работу?
Я сейчас думал о следующих разных отношениях:
A. Создайте часть «Сервистеграция» в веб-проекте.
Это будет средний человек между моими контроллерами и моим уровнем обслуживания WCF. Сервисная часть будет говорить с сервисным слоем с использованием Datacontracts, а также к веб-слою с использованием ViewModels, но придется преобразовать между DataContracts и ViewModels, используя двусторонний трансформатор.
Кроме того, поскольку проверка IDATAERRINFO не будет повторно использоваться, необходимо будет также создать валидатор на DataContract, что использует трансформатор для преобразования от ViewModel в Datacontratt, выполните проверку, используя IDATATATARRINFO и вернуть его результаты. Это будет использоваться внутри методов действия контроллеров (например, if (!MyValidator.IsValid(viewModel)) return View();
)
Требуется разные классы: xdatacontratt, xviewmodel, xtransformer, xvalidator
B. Создайте часть «рассеяния» в веб-проекте
Это было бы средним человеком между контроллерами (или что-либо доступа к сеансу) и сама сеанса. Все, что требует доступа к сеансу пройдет через этот класс. DataContracts будут использоваться во всем приложении, если они не хранятся в сеансе. Часть сессионной интергревации не несет ответственности за преобразование DataContratt в некоторую изиселюализируемую форму и обратно. Никакой дополнительной валидатора не требуется из-за использования интерфейса IDATAERRORINFO на DataContratt.
Требуется разные классы: xdatacontract, xtransformer, xserializableform
ПРИМЕЧАНИЕ. В обоих сценариях все равно будет просмотреть модерты в обоих сценариях.
(B) имеет преимущество не нуждающегося в дополнительном валидатере.
Прежде чем уйти и внедрить (а) / (б) полностью, я хотел бы получить некоторые отзывы. На данный момент я начинаю опираться на (b), однако (а) может быть более гибким. В любом случае, это кажется слишком много работы для того, что она стоит. Кто-нибудь еще встречается с этой проблемой, вы согласны / не согласны со мной, и / или у вас есть какой-либо другой способ решения проблемы?
Спасибо,
Джеймс
Решение
Не собирая полноценный маршрут A или B, не могли бы вы просто сделать общий объект изисереализируемой обертки и поместите те, которые в вашей сессии?
[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);
}
}
}
}
Другие советы
В качестве расширения до предоставленного ответа я добавил эти два способа для облегчения хранения / извлечения данных.
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");