Wednesday, January 28, 2015

Making SignalR Hub’s internal

 

Today I ran into an interesting issue. In my current project, we’re making a web application out of discrete modules. These modules should not expose their internal workings to the outside, to minimize assembly versioning issues. So we’ll ILMerge all non-public dependencies into the assembly.

One example is using SignalR. One of my modules uses signalr to notify clients. This is an internal implementation detail of my module, so it shouldn’t be exposed to the outside world.

The nice thing of this approach is, we can have multiple versions of the same assembly running in the same appdomain. So if I want another module to use a different version of signalr, there’s no issue at all.

But then I ran into a snag. SignalR requires it’s hubs to be public. But, if my hub is public, I’m exposing SignalR and I can’t ILMerge this dependency.

So how to solve this?  After a bit of digging, it’s the IHubDescriptorProvider that finds the hubs. I’ve created a provider that finds ‘only’ my hub, but it’s not difficult to see how to fetch all hubs if you want to.

   1: internal class InternalHubDescriptorProvider : IHubDescriptorProvider
   2: {
   3:    private HubDescriptor _notificationsHubDescriptor;
   4:  
   5:    public InternalHubLocator()
   6:    {
   7:        // create the descriptor that's used to find the notifications hub
   8:        _notificationsHubDescriptor = new HubDescriptor()
   9:        {
  10:            HubType = typeof(NotificationsHub),
  11:            Name = GetHubTypeName(typeof(NotificationsHub))
  12:        };
  13:    }
  14:  
  15:    private static string GetHubTypeName(Type type)
  16:    {
  17:        var lastIndexOfBacktick = type.Name.LastIndexOf('`');
  18:        if (lastIndexOfBacktick == -1)
  19:        {
  20:            return type.Name;
  21:        }
  22:        else
  23:        {
  24:            return type.Name.Substring(0, lastIndexOfBacktick);
  25:        }
  26:    }
  27:    /// <summary>
  28:    /// Retrieve all avaiable hubs.
  29:    /// </summary>
  30:    /// <returns>
  31:    /// Collection of hub descriptors.
  32:    /// </returns>
  33:    public IList<HubDescriptor> GetHubs()
  34:    {
  35:        return new []{_notificationsHubDescriptor};
  36:    }
  37:  
  38:    /// <summary>
  39:    /// Tries to retrieve hub with a given name.
  40:    /// </summary>
  41:    /// <param name="hubName">Name of the hub.</param><param name="descriptor">Retrieved descriptor object.</param>
  42:    /// <returns>
  43:    /// True, if hub has been found
  44:    /// </returns>
  45:    public bool TryGetHub(string hubName, out HubDescriptor descriptor)
  46:    {
  47:        if(string.Equals(hubName, _notificationsHubDescriptor.Name, StringComparison.CurrentCultureIgnoreCase))
  48:        {
  49:            descriptor = _notificationsHubDescriptor;
  50:            return true;
  51:        }
  52:        descriptor = default(HubDescriptor);
  53:        return false;
  54:    }
  55: }

 


One little caveat that had me pulling out my hair. On the hubdescriptor, don’t specify the ‘NameSpecified’ flag, unless you actually have the HubNameAttribute on your hub. Else it will silently eat your outbound messages.

Tuesday, January 6, 2015

In-Memory FileStream

In my current project, I wanted to ‘fake out’ the filesystem for integration tests. In several places in my code, I would first write some data to a temporary file, then later, open the file again for background processing. Now my integration tests run fine using the real filesystem, but just to make sure my tests could run in parallel, I wanted to switch to a faked filesystem.

So, my first attempt used memory streams. But then I found a crucial difference between filestreams and memorystreams. If you dispose a filestream.. the actual file is still there! Not so much with a memorystream.

So here’s an in memory filestream. If you close / dispose it.. it will still be there. Note.. it will only be cleaned up by the garbage collector. So don’t hang on to them in definetly and don’t use this in production.

   1: public class InMemoryFileStream : Stream
   2: {
   3:     private MemoryStream _memoryStream = new MemoryStream();
   4:  
   5:  
   6:     public override void Flush()
   7:     {
   8:         _memoryStream.Flush();
   9:     }
  10:  
  11:     public override long Seek(long offset, SeekOrigin origin)
  12:     {
  13:         return _memoryStream.Seek(offset, origin);
  14:     }
  15:  
  16:     public override void SetLength(long value)
  17:     {
  18:         _memoryStream.SetLength(value);
  19:     }
  20:  
  21:     public override int Read(byte[] buffer, int offset, int count)
  22:     {
  23:         return _memoryStream.Read(buffer, offset, count);
  24:     }
  25:  
  26:     public override void Write(byte[] buffer, int offset, int count)
  27:     {
  28:         _memoryStream.Write(buffer, offset, count);
  29:     }
  30:  
  31:     public override bool CanRead
  32:     {
  33:         get { return _memoryStream.CanRead; }
  34:     }
  35:  
  36:     public override bool CanSeek
  37:     {
  38:         get { return _memoryStream.CanSeek; }
  39:     }
  40:  
  41:     public override bool CanWrite
  42:     {
  43:         get { return _memoryStream.CanWrite; }
  44:     }
  45:  
  46:     public override long Length
  47:     {
  48:         get { return _memoryStream.Length; }
  49:     }
  50:  
  51:     public override long Position
  52:     {
  53:         get { return _memoryStream.Position; }
  54:         set { _memoryStream.Position = value; }
  55:     }
  56:  
  57:     protected override void Dispose(bool disposing)
  58:     {
  59:         if (disposing)
  60:         {
  61:             _memoryStream.Position = 0;
  62:             base.Dispose(disposing);
  63:         }
  64:     }
  65: }