c# - protobuf-net:如何在用户 session 中存储

标签 c# .net caching protobuf-net

我目前可以将创建的对象存储到HttpContext.Current.Session中,并且遇到过protobuf-net。有没有一种方法可以通过使用protobuf序列化对象来存储它?

看来protobuf希望将信息存储到Stream中,所以我(可以吗?)应该将Stream对象存储到用户会话中吗?还是应该先将其从Stream转换为其他对象类型?如果是这样,转换序列化的对象是否会违反使用protobuf的原始目的(CPU使用率,内存使用率)?有人做过吗?

我的目标是使用protobuf作为将信息存储到用户会话中的压缩层。是否有更好的方法(较小的尺寸,更快的压缩,更易于维护,更小的实施开销),还是protobuf是完成此任务的正确工具?



更新资料

我正在使用此类对象

[Serializable]
public class DynamicMenuCache
{
    public System.DateTime lastUpdated { get; set; }
    public MenuList menu { get; set; }
}


此类是我的MenuList类的包装,(基本上)是一个包含内置类型(字符串,整数)的列表列表。我创建了包装器,将时间戳记与我的对象相关联。

如果我有一个会话高速缓存未命中(会话密钥为null或session.lastUpdated大于全局存储的时间),则执行常规的数据库查找(MSSQL),创建MenuList对象,并将其存储到会话中,例如所以

HttpContext.Current.Session.Add("DynamicMenu" + MenuType, new DynamicMenuCache()
{
    lastUpdated = System.DateTime.Now,
    menu = Menu
});


当前,我们的会话存储在内存中,但是将来我们可能会转移到数据库会话存储中。

我们在会话中的使用非常繁琐,因为我们将很多大对象存储到其中(尽管我希望在将来的某个时间清理在会话中存储的内容)。

例如,我们将每个用户的权限集存储到其会话存储区中,以避免数据库命中。当前有很多权限和权限存储结构存储到会话中。

现在,我只是在查看可用的选项,因为我想在以后更加智能,更严格地使用会话缓存。

如果您还有其他需要,请告诉我。

最佳答案

请注意,此处的protobuf-net仅在您打算迁移到持久化状态提供程序时才有意义。

首先,由于您当前正在使用内存(因此类型不会被序列化,即AFAIK),因此,有关更改会话以使用任何类型的基于序列化的提供程序的一些说明:


类型必须由提供程序可序列化(听起来很明显,但是如果您有圆形图等,这会产生特殊影响)
因为数据是序列化的,所以语义是不同的;您每次都会得到一个副本,这意味着您在请求期间所做的任何更改都会丢失-只要您确保再次明确地重新存储数据即可,这很好,并且可以避免一些线程问题-双刃
内置状态机制通常将会话作为单个操作来检索-如果(如您所述)其中有一些大对象,则可能会出现问题;与protobuf-net无关,但是我曾经被要求调查一台即将死机的服务器,该服务器原来是处于杀死系统状态的多MB对象,因为每个请求(甚至是那些不使用该数据的请求)都导致这个巨大的物体要通过网络(双向)运输


在许多方面,我实际上根本不是标准会话状态模型的忠实拥护者-而且这还早于我谈到它与protobuf-net的关系之前!

protobuf-net最终是一个序列化层。标准会话状态实现的另一个功能是,因为它最初是考虑到BinaryFormatter编写的,因此它假定可以在没有任何额外上下文的情况下反序列化对象。但是,protobuf-net(就像XmlSerializerDataContractSerializerJavaScriptSerializer一样)没有绑定到任何特定的类型系统-它采用的方法是“您告诉我要填充的类型,我会担心关于数据”。这实际上是一件非常好的事情,因为我看到发布新版本时Web服务器被BinaryFormatter杀死了,因为有人敢于稍微触摸碰巧与持久化会话中存储的对象相关的一种类型。 BinaryFormatter不喜欢这样;尤其是当您(gasp)重命名类型,或(震惊)将字段+属性更改为自动实现的属性时。提示:这些是Google设计protobuf可以避免的问题。

然而!这确实意味着与标准会话状态模型一起使用不是很方便。我已经实现了将类型名称编码到流中的系统(例如,我为protobuf-net编写了一个enyim / memcached转码器),但是...并不是很漂亮。 IMO,更好的方法是将知道数据是什么的负担转移给调用方。我的意思是,真的……呼叫者应该知道他们在任何给定密钥中期望的数据类型,对吗?

一种方法是存储byte[]。几乎任何状态实现都可以处理BLOB。如果无法处理,请使用Convert.ToBase64String / Convert.FromBase64String存储string-任何不处理string的实现都需要射击!要与流一起使用,您可以执行类似操作(此处为伪代码):

public static T GetFromState<T>(string key) {
    byte[] blob = {standard state provider get by key}
    using(var ms = new MemoryStream(blob)) {
        return Serializer.Deserialize<T>(ms);
    }
}


(和添加类似)

请注意,protobuf-net与BinaryFormatter不同-他们对合理的期望值不同,例如,默认情况下,protobuf-net希望事先知道数据的样子(即public object Value {get;set;}会很麻烦) ,并且不处理圆形图(尽管已有规定可以支持这两种情况)。作为一般经验法则:如果可以使用XmlSerializerDataContractSerializer之类的序列化数据,则可以使用protobuf-net轻松进行序列化; protobuf-net也支持其他方案,但是没有公开保证可以序列化每个任意数据模型。根据DTO进行思考将使生活更轻松。在大多数情况下,这根本不是问题,因为大多数人都有合理的数据。有些人没有合理的数据,我只想适当地设定期望值!

但是,就我个人而言,就像我说的那样-特别是当涉及大对象时,我根本不喜欢内置的会话状态模式。我可能建议使用的是单独的每个键数据存储(意味着:每个键每个用户一个记录,而不是每个用户仅一个记录)-可能只适用于较大的对象,可能适用于所有对象。这可能是SQL Server,或类似redis / memcached的东西。如果您正在使用希望使用会话状态的第三方控件(Web窗体等),这显然会有些痛苦,但是如果您在代码中手动使用状态,则实现起来非常简单。 FWIW,BookSleeve与redis耦合,可以很好地用于此类事情,并提供对基于byte[]的存储的良好访问。您可以从byte[]反序列化对象,如上所示。

无论如何-如果我走得太远,我会停在那里。随时回答任何问题,但执行摘要:


protobuf-net可以阻止许多您可能在BinaryFormatter中看到的版本控制问题
但这并不一定是直接1:1交换,因为protobuf-net不会对“类型”信息进行编码(内置会话机制期望)
可以使其正常工作,最常见的是使用byte[]
但是,如果您要存储大对象,则可能存在与会话状态工作方式有关的其他问题(与protobuf-net无关)
特别是对于较大的对象,我建议您使用自己的机制(即非会话状态);键值存储系统(redis,memcached,AppFabric缓存)可以很好地完成此任务

关于c# - protobuf-net:如何在用户 session 中存储,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8493351/

相关文章:

c# - 使用c#将数据库从一台服务器复制到另一台服务器

vb.net - 如何防止 CRM 2011 sdk 中的查询缓存?

c# - 在组合框中显示数据库值之前显示初始值和空字符串

c# - UWP - 如何平铺背景图像?

c# - Horizo​​ntalAlign.Center 在 GridView 中不起作用

.net - 为什么 Task.WhenAll 抛出 TaskCanceledException 和 Task.WhenAny 不在同一个测试用例中

c# - 如何通过 LINQ to XML 创建 Dictionary<int, string>?

c# - 多次调用 HttpContent ReadAsAsync

javascript - 在共享主机上使用 jquery 重新加载与刷新页面

javascript - 使用 GUID 作为键缓存 "deep"JSON 对象