5

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

问题是在服务中,被返回的对象被标记了[DataContract]属性。Web 使用 AppFabric 缓存(又名 Velocity)SessionStateProvider 来存储会话状态。因此,我存储在会话中的任何内容都必须是可序列化的。

问题来了:DataContracts 没有标记,[Serializable]据我所知,通过将它引入一个已经标记有[DataContract]一些问题的类会出现,所以我不相信这是一个解决方案。

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

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

我目前想到了以下不同的方法:

A. 在 Web 项目中创建一个“ServiceIntegration”部件。

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

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

需要不同的类:xDataContract、xViewModel、xTransformer、xValidator

B. 在 web 项目中创建一个 'SessionIntegration' 部分

这将是控制器(或访问会话的任何东西)和会话本身之间的中间人。任何需要访问会话的东西都将通过这个类。DataContracts 将在整个应用程序中使用,除非它们被存储到会话中。SessionIntegration 部分负责将 DataContract 转换为某种 ISerializable 形式并返回。由于在 DataContract 上使用了 IDataErrorInfo 接口,因此不需要额外的验证器。

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


注意:在这两种情况下仍然会有 ViewModels,但是使用 (B) 我可以从 DataContracts 组合 ViewModels。

(B) 的好处是不需要额外的验证器。


在我开始全面实施 (A)/(B) 之前,我想要一些反馈。目前,我开始倾向于(B),但是,(A)可能更灵活。无论哪种方式,对于它的价值来说,这似乎是太多的工作。有没有其他人遇到过这个问题,你同意/不同意我的观点,和/或你有其他解决问题的方法吗?

谢谢,

詹姆士

4

2 回答 2

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);   
                }
            }
        }
    }
于 2010-08-31T19:20:17.663 回答
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");
于 2010-09-01T09:43:43.473 回答