代码之家  ›  专栏  ›  技术社区  ›  jamiebarrow

使用AppFabric缓存在MVC会话状态中使用WCF DataContract

  •  5
  • jamiebarrow  · 技术社区  · 14 年前

    我有一个数据访问层、一个服务层和一个表示层。表示层是ASP.NET MVC2 RTM(Web),服务层是WCF(服务)。全是.NET 3.5 SP1。

    问题是,在服务中,返回的对象用 [DataContract] 属性。Web正在使用AppFabric缓存(A.K.A Velocity)sessionStateProvider存储会话状态。因此,我在会话中存储的任何内容都必须是可序列化的。

    问题来了:数据合同没有标记为 [Serializable] 据我所知,通过将它引入一个已经标记了 [数据通道] 有些问题出现了,所以我不认为这是一个解决办法。

    我最初计划直接在Web层中使用DataContracts,将它们用作与呈现DataContracts相关的视图的模型(可能嵌套在更高级别的ViewModel类中)。但是由于会话状态提供程序要求存储在其中的所有对象都是可序列化的,所以我开始重新考虑这个策略。不过,最好拥有,因为它们包含使用 IDataErrorInfo 接口和相同的验证逻辑可以作为模型绑定的一部分在MVC中重用。

    你认为让我减少所需工作的最好方法是什么?

    我现在想到了以下不同的方法:

    a.在Web项目中创建“ServiceIntegration”部分。

    这将是我的控制器和我的WCF服务层之间的中间人。ServiceIntegration部分将使用DataContracts与服务层通信,使用ViewModels与Web层通信,但必须使用双向转换器在DataContracts和ViewModels之间进行转换。

    此外,由于IDataErrorInfo验证不可重复使用,因此也需要为每个DataContract创建一个验证程序,该验证程序使用Transformer从ViewModel转换为DataContract,使用IDataErrorInfo执行验证并返回其结果。然后将在控制器的操作方法(例如 if (!MyValidator.IsValid(viewModel)) return View(); )

    需要不同的类:xdatacontract、xviewmodel、xtransformer、xvalidator

    b.在Web项目中创建“sessionintegration”部分

    这将是控制器(或任何访问会话的对象)和会话本身之间的中间人。任何需要访问会话的内容都将通过这个类。数据合同将在整个应用程序中使用,除非它们存储在会话中。会话集成部分将负责将数据合同转换为某种可ISerializable形式,然后再转换回来。由于在数据合同上使用IDataErrorInfo接口,因此不需要其他验证程序。

    需要不同的类:xDataContract、xtTransformer、xSerializableForm


    注意:在这两个场景中仍然有视图模型,但是使用(b)我可以从数据合同组合视图模型。

    (b)有不需要额外的验证器的利益。


    在我开始全面实施(a)/(b)之前,我想得到一些反馈。现在,我开始倾向于(b),但是,(a)可能更灵活。不管怎样,这似乎是太多的工作,为它的价值。有没有其他人遇到过这个问题,你同意/不同意我的观点,和/或你有其他方法来解决这个问题?

    谢谢,

    詹姆斯

    2 回复  |  直到 14 年前
        1
  •  5
  •   Jeff    14 年前

    如果不使用完整的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);   
                    }
                }
            }
        }
    
        2
  •  3
  •   jamiebarrow    14 年前

    作为对所提供答案的扩展,我添加了这两种方法来简化数据的存储/检索。

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