【问题标题】:Serializable wrapper class for named pipes transport命名管道传输的可序列化包装类
【发布时间】:2012-05-01 19:59:10
【问题描述】:

我正在尝试使用命名管道创建跨应用程序的 asp.net 缓存服务(类似于 memcached 或 AppFabric/Velocity)。基本前提是您可以获取或设置在连接到命名管道的任何其他应用程序之间共享的缓存项。

其中一部分要求将您发送到缓存的对象序列化为 byte[] 以方便 PipeStream.Read() 和 PipeStream.Write() 方法。由于缓存将保存任意数量的任意对象类型/类,我不想在每个对象上都设置 [Serializable] 属性,因此我选择创建一个 [Serializable] 的包装类并具有我可以用来传输缓存项的通用对象类型字段,类似于此处使用的方法:http://www.eggheadcafe.com/articles/20060404.asp

一切似乎最初都适用于内置类型,但现在我正在尝试发送一个 List 对象,这些对象属于自定义类型(一个类),我遇到了一个我的自定义类需要的异常成为 [Serializable]...这是我试图避免的。

代码如下:

服务器-

class Server
{
    static Dictionary<string, object> Cache = new Dictionary<string, object>();
    static Dictionary<string, DateTime> CacheExpireTime = new Dictionary<string, DateTime>();
    static void Main(string[] args)
    {
        new Thread(HandleGets).Start();
        new Thread(HandleSets).Start();         
    }       
    static protected void HandleSets()
    {
        PipeSecurity ps = new PipeSecurity();
        PipeAccessRule par = new PipeAccessRule("Everyone",  PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow);
        ps.AddAccessRule(par);
        while (true)
        {
            using (NamedPipeServerStream pipeServer = new NamedPipeServerStream("MemCacheSet", PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.None, 0, 0, ps))
            {
                pipeServer.WaitForConnection();
                CacheAction ca = CacheAction.FromBytes(pipeServer.ReadAll());                   
                Cache[ca.DictionaryKey] = ca.DictionaryValue;
                CacheExpireTime[ca.DictionaryKey] = ca.TimeOfExpire;
            }
        }
    }
    static protected void HandleGets()
    {
        PipeSecurity ps = new PipeSecurity();
        PipeAccessRule par = new PipeAccessRule("Everyone", PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow);
        ps.AddAccessRule(par);
        while (true)
        {
            using (NamedPipeServerStream pipeServer = new NamedPipeServerStream("MemCacheGet", PipeDirection.InOut,1,PipeTransmissionMode.Byte,PipeOptions.None,0,0,ps))
            {
                pipeServer.WaitForConnection();
                CacheAction ca = CacheAction.FromBytes(pipeServer.ReadAll());                   
                CacheAction resp = new CacheAction();
                resp.DictionaryKey = ca.DictionaryKey;
                if (Cache.ContainsKey(ca.DictionaryKey) && CacheExpireTime[ca.DictionaryKey]>=DateTime.Now)                 
                    resp.DictionaryValue = Cache[ca.DictionaryKey];                 
                pipeServer.WriteAll(resp.ToBytes());
            }
        }
    }
}

客户端方法(在静态类中)-

    static object GetItem(string inKey)
    {
        object rVal;
        using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", "MemCacheGet", PipeDirection.InOut))
        {
            pipeStream.Connect();
            CacheAction ca = new CacheAction();
            ca.DictionaryKey = inKey;
            pipeStream.WriteAll(ca.ToBytes());
            ca = CacheAction.FromBytes(pipeStream.ReadAll());
            rVal = ca.DictionaryValue;
        }
        return rVal;
    }   
    static void SetItem(string inName, object inItem, TimeSpan? expireTime = null)
    {
        if (!expireTime.HasValue)
            expireTime = new TimeSpan(0, 10, 0);
        using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", "MemCacheSet", PipeDirection.Out))
        {
            pipeStream.Connect();
            CacheAction ca = new CacheAction();
            ca.DictionaryKey = inName;
            ca.DictionaryValue = inItem;
            ca.TimeOfExpire = DateTime.Now + expireTime.Value;
            pipeStream.WriteAll(ca.ToBytes());
        }
    }

共享代码:

[Serializable]
public class CacheAction
{
    public string DictionaryKey;
    public object DictionaryValue;
    public DateTime TimeOfExpire;
    public static CacheAction FromBytes(byte[] inBytes)
    {           
        BinaryFormatter bf = new BinaryFormatter();
        MemoryStream ms = new MemoryStream(inBytes);
        CacheAction p = (CacheAction)bf.Deserialize(ms);
        return p;
    }
    public byte[] ToBytes()
    {
        BinaryFormatter bf = new BinaryFormatter();
        MemoryStream ms = new MemoryStream();
        bf.Serialize(ms, this);
        return ms.ToArray();
    }
}
public static class MyExtensions
{
    public static byte[] ReadAll(this NamedPipeClientStream np)
    {
        byte[] size = new byte[4];
        np.Read(size, 0, 4);
        int iSize = BitConverter.ToInt32(size, 0);
        byte[] rVal = new byte[iSize];
        np.Read(rVal, 0, iSize);
        return rVal;
    }
    public static byte[] ReadAll(this NamedPipeServerStream np)
    {
        byte[] size = new byte[4];
        np.Read(size, 0, 4);
        int iSize = BitConverter.ToInt32(size, 0);
        byte[] rVal = new byte[iSize];
        np.Read(rVal, 0, iSize);
        return rVal;
    }
    public static void WriteAll(this NamedPipeClientStream np, byte[] toWrite)
    {
        byte[] size = BitConverter.GetBytes(toWrite.Length);
        np.Write(size, 0, size.Length);
        np.Write(toWrite, 0, toWrite.Length);
    }
    public static void WriteAll(this NamedPipeServerStream np, byte[] toWrite)
    {
        byte[] size = BitConverter.GetBytes(toWrite.Length);
        np.Write(size, 0, size.Length);
        np.Write(toWrite, 0, toWrite.Length);
    }
}

最后是导致问题的特定用例:

class MemCachedSession
    {
        public string SessionId { get; set; }
        public DateTime Created { get; set; }
        public DateTime Expires { get; set; }
        public DateTime LockDate { get; set; }
        public int LockId { get; set; }
        public int Timeout { get; set; }
        public bool Locked { get; set; }
        public string SessionItems { get; set; }
        public int Flags { get; set; }
    }

这样使用:

SetItem("MemCacheSessionStateProvider", new List<MemCachedSession>(), new TimeSpan(7, 0, 0, 0, 0))

在调用 bf.Serialize(ms,this) 的 ToBytes() 方法中抛出的异常,它说: "在程序集 'API、Version=1.0.0.0、Culture=neutral、PublicKeyToken=null' 中的类型 'MemCachedSession' 未标记为可序列化。'"

(编辑)为什么会这样,和/或我什至在做我想做的事情的正确轨道上?(编辑)

整个想法是封装类 (CacheAction) 将是可序列化的,以防止这种情况发生,这在某种程度上确实有效,但不适用于这个自定义类 (MemCachedSession)。

抱歉这个大问题,也许答案也不简单(即整个方法是错误的),但如果有人能花时间提供一些见解,我们将不胜感激! 谢谢,

【问题讨论】:

  • 你好像忘记写问题了。
  • 谢谢,明确添加问题。

标签: c# serialization deserialization named-pipes binaryformatter


【解决方案1】:

默认情况下,序列化器只能序列化原始类型。任何其他类型都必须使用属性Serializable 进行标记才能符合序列化条件。

举个例子:

[Serializable] class AClass {
    int myInt;
    long myLong;
    string myString;
}

[Serializable] class BClass {
    int myInt;
    AClass myAClass;
}

[Serializable] class CClass {
    int myInt;
    DClass otherClass;
}

class DClass {
    int myInt;
    long myLong;
    string myString;
}

AClass 可以被序列化,因为它只包含原语并且被修饰为Serializable

BClass 可以序列化,因为它的整个对象图是原始的或Serializable

CClass 被修饰为Serializable,但在尝试序列化时会抛出错误,因为它包含一个不是的成员。这可以通过将otherClass 声明替换为[NonSerialized] DClass otherClass; 来防止,但是这对您的情况没有帮助,因为该成员不会像属性所暗示的那样与对象图的其余部分一起序列化。

DClass 不能被序列化,即使它不包含任何非原始字段,因为它没有被修饰为 Serializable

更多信息可以在 MSDN 网站上找到:http://msdn.microsoft.com/en-us/library/vstudio/ms233843.aspx

TLDR;整个方法都是错误的——所有缓存的对象都必须标记为可序列化。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-16
    相关资源
    最近更新 更多