如何在自定义会话状态提供程序中将会话数据转换为SessionStateStoreData类型

时间:2015-06-04 09:59:25

标签: c# asp.net json session serialization

我在我的Web应用程序中使用自定义会话提供程序,它继承了SessionStateStoreProviderBase并覆盖了方法。用于从会话获取数据的方法具有 SessionStateStoreData 的返回类型。

目前正在使用二进制序列化来序列化和反序列化数据。我想使用 json序列化而不是二进制但不确定如何将会话数据(json序列化后的字符串类型)转换为SessionStateStoreData类型。 SessionStateStoreData类型的构造函数使用SessionStateItemCollection类型对象,该对象可以从 sessionstateitemcollection 的反序列化方法获取,该方法仅将二进制流作为输入。

var ms = _serializedSessionData == null
            ? new MemoryStream()
            : new MemoryStream(_serializedSessionData);

        var sessionItems = new SessionStateItemCollection();

        if (ms.Length > 0)
        {
            var reader = new BinaryReader(ms);
            sessionItems = SessionStateItemCollection.Deserialize(reader);
        }

return new SessionStateStoreData(sessionItems,
          SessionStateUtility.GetSessionStaticObjects(context),
          _timeout);

我附加了用于二进制反序列化的代码。如何为json序列化对象做同样的事情?

1 个答案:

答案 0 :(得分:3)

SessionStateItemCollection非常接近Dictionary<string, object>,因此您可以将其序列化。但是,您需要考虑null密钥的可能性。 null允许SessionStateItemCollection密钥,但Dictionary<string, object>会在空密钥上引发异常。

由于SessionStateItemCollection中的值是无类型的,因此您还需要一个支持序列化任意类型的类型信息的JSON序列化程序。 Json.NET可以做到这一点。 JavaScriptSerializer也可以。 DataContractJsonSerializer可能,因为您必须提前知道所有可能的类型,并将其作为已知类型传递给constructor

对于答案的其余部分,我假设您选择了Json.NET。

首先,为序列化定义以下中间类:

public class StringDictionaryWrapper
{
    public StringDictionaryWrapper()
    {
        Items = new Dictionary<string, object>();
    }

    // Holds the value of the item with a null key, if any.
    [JsonProperty(ItemTypeNameHandling = TypeNameHandling.Auto, DefaultValueHandling = DefaultValueHandling.Ignore)]
    public object NullItem { get; set; }

    [JsonProperty(ItemTypeNameHandling = TypeNameHandling.Auto)]
    public Dictionary<string, object> Items { get; set; }
}

然后,要将SessionStateItemCollection序列化为json字符串,请执行:

        var dict = new StringDictionaryWrapper { Items = sessionItems.Keys.OfType<string>().ToDictionary(key => key, key => sessionItems[key]), NullItem = sessionItems[null] };
        var json = JsonConvert.SerializeObject(dict, Formatting.Indented);

JSON看起来像:

{
  "NullItem": 1123,
  "Items": {
    "foo": {
      "$type": "Question30640792.SomeClass, Tile",
      "SomeProperty": "foo2"
    },
    "TestClass": {
      "$type": "Question30640792.TestClass, Tile",
      "A": 101,
      "B": 102
    }
  }
}

要从json字符串反序列化,请执行:

        var sessionItems = new SessionStateItemCollection();

        var dict = JsonConvert.DeserializeObject<StringDictionaryWrapper>(json);
        if (dict != null && dict.NullItem != null)
            sessionItems[null] = dict.NullItem;
        if (dict != null && dict.Items != null)
            foreach (var pair in dict.Items)
                sessionItems[pair.Key] = pair.Value;

请注意,这将同时反序列化所有会话状态项集合,而内置二进制序列化使用on-demand deserialization for performance reasons。因此,您可能会看到性能受损。此外,由于Dictionary<TKey, TValue>是无序的,因此会话项可能不会按原来的顺序返回。如果这是一个问题,您可能需要为会话项集合创建类似custom proxy wrapper的内容。

最后,在使用TypeNameHandling时,请注意Newtonsoft docs中的这一注意事项:

  

当您的应用程序从外部源反序列化JSON时,应谨慎使用TypeNameHandling。使用非None以外的值进行反序列化时,应使用自定义SerializationBinder验证传入类型。

有关可能需要执行此操作的讨论,请参阅 TypeNameHandling caution in Newtonsoft Json