Category: Gotcha!

Azure Notification Hub – Double Message Gotcha

I recently worked on a piece of Azure hosted software that was required to send a large volume of push notifications to mobile devices and so decided to give Azure Notification Hubs a whirl.

On the whole I found it to be pretty easy to set up and get going with and very well documented with walkthroughs for most scenarios (both server side and device) across all the major platforms.

But, and you knew there was a but coming didn’t you?, I did come across one major gotcha while building an implementation based off the tutorial on sending notifications from your server.

After implementing this and doing a bit of debugging I was finding that every time I sent a message from the server to the device my device was responding to it multiple times. Due to how devices and the registration process work you do have to be careful to ensure that your device isn’t already registered with the notification hub and Microsoft themselves show you how to do this in this code snippet:

public class DeviceRegistration
{
    public string Platform { get; set; }
    public string Handle { get; set; }
    public string[] Tags { get; set; }
}
 
// POST api/register
// This creates a registration id
public async Task<string> Post(string handle = null)
{
    string newRegistrationId = null;
 
    // make sure there are no existing registrations for this push handle (used for iOS and Android)
    if (handle != null)
    {
        var registrations = await hub.GetRegistrationsByChannelAsync(handle, 100);
 
        foreach (RegistrationDescription registration in registrations)
        {
            if (newRegistrationId == null)
            {
                newRegistrationId = registration.RegistrationId;
            }
            else
            {
                await hub.DeleteRegistrationAsync(registration);
            }
        }
    }
 
    if (newRegistrationId == null) 
        newRegistrationId = await hub.CreateRegistrationIdAsync();
 
    return newRegistrationId;
}
 
// PUT api/register/5
// This creates or updates a registration (with provided channelURI) at the specified id
public async Task<HttpResponseMessage> Put(string id, DeviceRegistration deviceUpdate)
{
    RegistrationDescription registration = null;
    switch (deviceUpdate.Platform)
    {
        case "mpns":
            registration = new MpnsRegistrationDescription(deviceUpdate.Handle);
            break;
        case "wns":
            registration = new WindowsRegistrationDescription(deviceUpdate.Handle);
            break;
        case "apns":
            registration = new AppleRegistrationDescription(deviceUpdate.Handle);
            break;
        case "gcm":
            registration = new GcmRegistrationDescription(deviceUpdate.Handle);
            break;
        default:
            throw new HttpResponseException(HttpStatusCode.BadRequest);
    }
 
    registration.RegistrationId = id;
    var username = HttpContext.Current.User.Identity.Name;
 
    // add check if user is allowed to add these tags
    registration.Tags = new HashSet<string>(deviceUpdate.Tags);
    registration.Tags.Add("username:" + username);
 
    try
    {
        await hub.CreateOrUpdateRegistrationAsync(registration);
    }
    catch (MessagingException e)
    {
        ReturnGoneIfHubResponseIsGone(e);
    }
 
    return Request.CreateResponse(HttpStatusCode.OK);
}

You’ll notice the comment in the built of the Post action about making sure their are no existing registrations for the push handle. The handle is a token you obtain from the device and use as illustrated as part of the registration process.

The problem is – this code sample doesn’t work.

I learned the hard way that when the registration is created (in the Put action) the notification hub converts the handle to all upper case text. The handle from my device (which I obtained using the Cordova PushPlugin – I was working in Ionic) contained a mix of lower case letters and numbers.

When you subsequently search for registrations that match the handle (var registrations = await hub.GetRegistrationsByChannelAsync(handle, 100)) it performs a case sensitive search and none of your previous registrations will be found.

The result is that you can find yourself registering the same device token multiple times against different registration IDs and when you do a message send it is sent multiple times – multiple registrations == multiple sends.

If you suspect you might have a similar problem you can enumerate all registrations with the GetAllRegistrationsAsync method on the NotificationHubClient.

Other than that, and as I said earlier, my experience with the Notification Hub was really very smooth.

Saving Images to a Blob Container with Azure SDK 2.2 and VS2013

On the face of it the below is quite an obscure post but since this has bitten me and image upload is so common I figure it will bite others too. And to be fair I have called this blog Azure From The Trenches so inevitably it’s going to get a bit grubby every now and then!

I’ve been quite happily working on my companion application for this blog over the last few evenings and tonight ported it all over to VS2013, Azure SDK 2.2 and the various NuGet updates that have been released alongside all that and hit an odd problem – a simple image upload that worked previously no longer does.

Part of my application uploads images into blob storage via Web API and I’d been using code that looks somewhat like the below:

1
2
3
4
5
Image myImage = new Image(); // paraphrasing here
using(CloudBlobStream uploadStream = blobRef.OpenWrite())
{
    myImage.Save(uploadStream, ImageFormat.Png);
}

This has been working with no issues at all (on my laptop, desktop and in Azure) until I ran the app after the upgrade this evening and then I began to get a NotSupportedException with the following root cause:

Microsoft.WindowsAzure.Storage.Blob.BlobWriteStreamBase.get_Length() is not supported

Cracking out dotPeek to take a look at the storage client code in the latest NuGet package (2.1.0.3) reveals that yes, getting the length of a stream on a block blob (the kind I am using) is unsupported:

1
2
3
4
5
6
7
8
9
10
public override long Length
{
  get
  {
    if (this.pageBlob != null)
      return this.pageBlobSize;
    else
      throw new NotSupportedException();
  }
}

However this is the exact same implementation on the previous version of the client and so it’s not that – and it makes sense right? You can’t really expect to get the length of a stream that is writing to a remote server.

This led me off into System.Drawing. I’ll spare you the grisly details but if your Image class doesn’t have raw data, and chances are you don’t, and you call Save without an EncoderParameters parameter (and even sometimes when you do) then a native method called GdipSaveImageToStream is called and this expects a COM stream. Your .net stream is converted to a COM stream using a class called ComStreamFromDataStream and unfortunately this class has a nasty habit of calling get_Length() on your .net stream.

And this is what causes the crash.

It’s easy to fix: grab yourself a byte array or memory stream first and upload that. I’ve wrapped this into my application framework with an UploadImage method but essentially this does the following:

1
2
3
4
5
6
7
Image myImage = new Image(); // paraphrasing here
using (MemoryStream ms = new MemoryStream())
{
    image.Save(ms, ImageFormat.Png);
    ms.Position = 0;
    blobRef.UploadFromStream(ms);
}

I’m still none the wiser as to why this has become a problem now but I guess with the move to VS2013 there is a lot of change and the layers of abstraction are now so deep that without spending hours reading Microsoft code I’m never going to know.

Contact

  • If you're looking for help with C#, .NET, Azure, Architecture, or would simply value an independent opinion then please get in touch here or over on Twitter.

Recent Posts

Recent Tweets

Invalid or expired token.

Recent Comments

Archives

Categories

Meta

GiottoPress by Enrique Chavez