title: Serializable wrapper class for named pipes transport
body: <p>I'm attempting to create a cross-application asp.net caching service (similar to memcached, or AppFabric/Velocity) using named pipes. The basic premise is that you can get or set cache items shared across any other applications that connect to the named pipe.</p>
  
  <p>Part of this requires that the objects you're sending to the cache be serialized to a byte[] to facilitate the PipeStream.Read() and PipeStream.Write() methods. Since the cache will be holding any number of arbitrary object types/classes, I don't want to have to set the [Serializable] attribute on each one, so I opted to create a wrapper class that would be [Serializable] and have an generic object typed field that I can use to transport the cache item, similar to the method used here: <a href="http://www.eggheadcafe.com/articles/20060404.asp" rel="nofollow">http://www.eggheadcafe.com/articles/20060404.asp</a></p>
  
  <p>Everything seemed to work initially for built-in types, but now I'm trying to send a List&lt;> of objects that are of a custom type (a class) and I'm getting an exception that my custom class needs to be [Serializable]...which is what I'm trying to avoid.</p>
  
  <p>Here's the code:</p>
  
  <p>Server-</p>
  
  <pre><code>class Server
  {
      static Dictionary&lt;string, object&gt; Cache = new Dictionary&lt;string, object&gt;();
      static Dictionary&lt;string, DateTime&gt; CacheExpireTime = new Dictionary&lt;string, DateTime&gt;();
      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) &amp;&amp; CacheExpireTime[ca.DictionaryKey]&gt;=DateTime.Now)                 
                      resp.DictionaryValue = Cache[ca.DictionaryKey];                 
                  pipeServer.WriteAll(resp.ToBytes());
              }
          }
      }
  }
  </code></pre>
  
  <p>Client Methods (in a static class)-</p>
  
  <pre><code>    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());
          }
      }
  </code></pre>
  
  <p>Shared code:</p>
  
  <pre><code>[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);
      }
  }
  </code></pre>
  
  <p>And finally the particular use case that causes a problem:</p>
  
  <pre><code>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; }
      }
  </code></pre>
  
  <p>used like so:</p>
  
  <pre><code>SetItem("MemCacheSessionStateProvider", new List&lt;MemCachedSession&gt;(), new TimeSpan(7, 0, 0, 0, 0))
  </code></pre>
  
  <p>The exception thrown is in ToBytes() method on the call to bf.Serialize(ms,this), and it says:
  "Type 'MemCachedSession' in Assembly 'API, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.'"</p>
  
  <p>(edit)Why is that, and/or am I even on the right track for doing what I want?(edit) </p>
  
  <p>The whole idea was that the encapsulating class (CacheAction) would be serializable so as to prevent this, which does work to some extent but not for this custom class (MemCachedSession).</p>
  
  <p>Sorry for the large question, and maybe the answer isn't simple either (i.e. the whole methodology is wrong) but if anyone could take the time to give some insight it would be appreciated!
  Thanks,</p>