Author: thinkthencode

DJ APP – THE STORY

Couple of years back, during my free time, I started working on an application which can be used as a basic music mixing application by novices and budding DJs. It took over an year for me to actually develop something that I thought was good enough to be published to Windows Store. Actual development time was quite less. Oh yes! application was developed for Windows 8. This is how the application looks.

apps_7549_9007199266709217_9067cd7c-a18b-43f9-91b4-91571cb009fa

You can download it from Windows Store – Mix’n Play Download and it is free.

Some Statistics [last 12 months]

13,000 downloads in last 12 months. Everyone looks for 5 star rating for their apps, but still 3.8 is not bad and looks like majority of users think good about the app.

image

Once application is published in app store, it can be downloaded by any interested user. This is one of the most important thing that you get out of common app stores. Reach of your app is amazing. Following are some stats that shows the markets where app is downloaded the most:

image
I expected some of above countries like US, UK and India to be in top markets, but not Mexico. Mexico is a surprise for me. Other than this, thing that made me smile was the name of countries which came last in the list, in other words, where only one or two downloads happened.

imageimageimageimage

I don’t even know where exactly on globe few of these countries are and it is quite astonishing that there is someone in these countries who has downloaded my app and (hopefully !) using it as well.

Usage of application is also good. Users are not only downloading but also using the app. This is very encouraging. Following are top user session numbers:

image

Again, all above numbers are for last 12 months.

Reviews – The Motivation

Bad Reviews ( StarStar or Star)  

Reviews are generally a source of inspiration and introspection. Majority of the bad ratings Star (one star) that I got did not give me any clue about what users missed in the app or what went wrong for them. I was not able to fully understand why few folks are giving single star as most of them did not provide any textual feedback. I think they disliked the app so much that they did not even think that it can be improved. Few bad rating + review that I got are:

It is not possible to save

Couldn’t find the record button to mix songs together, and after adding my songs to my playlist not all of them came up, which was frustrating because I had to go searching for them through my folder, otherwise pretty easy to use just wasn’t as good as I expected.

The interface is good. The problem is that it only supports mp3 music, we miss to select music without loss.

Hmm, I agree that you can not save it and it sucks. This was not possible with Windows 8 API (programming interface) that was available 2 years ago. Not sure if it is there now. About supporting other file formats, I’ll surely add other lossless formats if I start working on the application again. Till that time MP3 is the only format supported.

Good Reviews (StarStarStar or more)

You need positive reviews to get motivated. Honestly, the reviews I got are so amazing and I never expected anything like this. All I expected was start rating and some text, like good job, useful app etc. Below are few:

Mixes folders, playlists in sequence, at random, BPS. Nice cross fader too. Basic yes, but if you just want to mix songs on the fly this is the thing. A gem! Wish list: volume limiter.

Program standards for beginners everything convenient and understandable.

The fact that it delivers audio crisp sound without any distortions. Other apps are all scratchy and annoy you with popup ads and others.

Good, simple usage application. Very good for a party…

mix on beat like If you can refine beat counter but I love the rest of the software thank you for making if thanks very muah

Simple but equally powerful. Good!!!

Easy access to the play list and library, easy simple and fast.

A very good and functional program for on the go. Nice would be if the title in the player to pause would remain, instead of immediately running out and the controls were slightly larger (for slightly stronger fingers) – but otherwise top!

This is only app that is designed for touch. Very easy to mix tracks indeed. Love auto mix…

Direct User Emails

I never imagined that someone will take the pain of writing an email and ask for features they want. Some folks also followed up and asked when is the next release. Some of the comments in the mails were:

After someone asked few questions and I helped them
Thank you. This was very helpful. That may be good to put a tutorial video for this app as Windows 8.1 is a completely different ball game from the previous OS systems. I knew I could swipe right, but never noticed it showed settings for the apps.

Thank you very much for the support. I am every more grateful for your app. It is saving my wedding as my DJ friends have flaked on me. However, I will also use this in the future for my other events too.

After someone reported an issue and I released a fix
Just had a quick look at it very well done on your enhancements so far. I like it, but on the surface 2 it is not as good as on the laptop but I’m sure you can iron out those kinks I’m sure.

keep up the good work in developing the good dj app into a long term success and I will report back on bugs and enhancements to it.

Feedback on my blog

Surprisingly users also went to app page on my blog and gave comments there as well. You can check them our here.

The Journey

Starting from inception of this idea, to publishing of app and constantly releasing fixes, there are some things that I think I did right.

  • Identify that one thing that your app will be best at. I wanted Mix’n Play to be the most easy application to use with touchscreen devices and I think I was able to achieve this(based on user feedback).
  • Build an easy mechanism using which users can provide feedback and share error reports. Have something to identify application version as well with error report. Last point is essential as many people might not be updating applications regularly and are still using older versions.
  • Test for majority of screen sizes where your application can run. Identify your target resolution and identify a device which is mostly used by users which has touchscreen and Windows OS.
  • Respond to user queries quickly. If you help your users, either by telling how to use feature X or taking their feedback and releasing an update, they will feel more connected and there is high chance for positive word of mouth.
  • Understand platform limitations and your skill limitations. I spent good amount of time on learning how to make good Windows apps. I was also aware what all you can do and things that are not allowed. You can develop apps without knowing anything, but I think to build good quality apps, you should know the platform and it’s design language.
  • To know actual rating and user feedback, never share your apps first within your friend circle. They will oblige and give 5 stars with all positive feedback, but they will never use the application. Open it for public and see what actual users are saying about your app.

Challenges

Sometimes things can get frustrating and challenging due to various reasons. Few of them were:

  • Maturity and feature support provided by Windows media API was very basic. You can’t do majority of actual DJ related tasks without going deep in C++. I have to restrict scope to very very basic things and tried my best to do them well. It get’s very frustrating when you know something is important but sadly there is no API for that.
  • I still use obsolete Windows media API in my app. I wanted to update to newer API but realized that apart from fixing things, somehow, new API is also missing some features I was using in the older API.

What’s Next?

I did not do any active development this year on the app and somehow I’m not able to get motivation to do a new Universal Windows application for this. Universal Windows app looks like natural progression from Windows 8 app. Let’s see what next year brings to the table.

Till then, celebrate things you did in 2016 and start 2017 with a smile. If you are planning to do a party, don’t forget to checkout, Mix’n Play on Windows Store.

Happy New Year Smile

Advertisements

Azure AD–Using App roles for authorization

Role based authorization

In intranet applications, AuthZ is generally implemented using Windows security groups with ASP.NET IsInRole() functionality. Following is a list of steps we take to make this work:

  • Define high level application roles. For example; in an expense application we can have approver and administrator as two roles with higher (or different privileges) than normal user.
  • Identify initial set of Windows security groups that will be mapped to app roles. This list should be dynamic and will probably change.
  • Use a mapping mechanism, either DB or configuration file, to keep a mapping of application roles and security groups.
  • In your application, once you get the token after authenticating the user, write code to check group claim and assign appropriate role claim (based on your mapping) to the user.
  • Indicate to ASP.NET pipeline that you want to use Role claim for IsInRole functionality.
  • That is pretty much it. Now, you can just place Authorize attribute wherever you want to check for that role or call IsInRole() to do the same.

Here comes Azure Active Directory

Now we want to move our on-premises (intranet) application (Windows Auth) to cloud. I’m skipping ADFS (STS) here, as at a high level the claims you get in the SAML token are similar to claims present in Kerberos token.

As of today, recommended service for AuthN and AuthZ on cloud is Azure AD (AAD). Moving your app to AAD bring some challenges as well as provide some opportunities to improve things.

Challenge

  • Tokens that you receive from AAD does not contain group claims by default. You have to enable it using these steps.
  • Even if you enable, there is a limit to groups that you can get in the token. I will not go into those details. Please check this for more info around this. 

Opportunity

  • Did you notice all work that is required to make application roles really work even in traditional (intranet) application?
  • You have to have mechanism to map app roles with security groups, fetch the mapping and add role claim.
  • Just like authentication, all this should ideally be offloaded to some common service.
  • Let’s see how we can improve this.

Solution

AAD provides application roles that can be used for authorization. I’ll show you in detail how it is done. This is a step by step guide and will have lot of screen shots.

Note: Make sure you have a valid subscription before you proceed beyond this point.

Set up your infrastructure

Create User and Group in AAD

  • Select your directory
    1
  • Add 2 users. For example, add a users Bob and add another user Samantha.2
     
  • Add a group.
    3
  • Add Bob to the group.
  • At the end of this step you should have 2 users and a group. One of the user should be part of the group.

Create Application in AAD

  • Add application to the directory using Application option from top menu and then clicking Add link in the bottom menu. For more details use this link.45
  • Give a friendly name, sign on URL and app id URI. For more details use this link.
    67
  • Note down client ID from configuration tab. This we will use in our web app to set up authentication.
    9

Create web app and make authentication work

  • Create a simple ASP.NET MVC web app and update OWIN Auth configuration with your application’s client ID and tenant name.
  • Download completed code from github
  • After updating required values, try to run the sample. I used the Individual account template while creating the project due to which there are few extra files and steps before login. From home page, click login and then click OpenIDConnet button. This will initiate login process with AAD.
  • You can put breakpoint in Contact action method in Home Controller to check available claims. Notice that there is neither group nor role claim.

Add role to AAD application

  • Open application in AAD and download manifest file. Currently, you can not add application roles from portal UI. We will use manifest file to add roles. 10
  • Open manifest in a text editor (preferably JSON editor). App roles will be blank.
  • 11
  • Add following text to app roles section in manifest file. This is the place where we are adding 2 roles.
    “appRoles”: [
                {
                    “allowedMemberTypes”: [
                        “User”
                    ],
                    “description”: “Approvers can mark expenses as approved”,
                    “displayName”: “Approver”,
                    “id”: “8F29F99B-5C77-4FBA-A310-4A5C0574E8FF”,
                    “isEnabled”: “true”,
                    “value”: “approver”
                },
                {
                    “allowedMemberTypes”: [
                        “User”
                    ],
                    “description”: “Administrators can change all settings and do all other operations”,
                    “displayName”: “Administrator”,
                    “id”: “0866623F-2159-4F90-A575-2D1D4D3F7391”,
                    “isEnabled”: “true”,
                    “value”: “administrator”
                }
            ],
  • Resulting manifest should look like this12
  • Upload the manifest back to the application.

Assign group to a role

  • Go to application in AAD and open Users and Group option. Please note, I’m using a premium subscription to do this. In a non premium subscription only user can be assigned to a role. This is just a UI limitation and can be worked around by using Graph API. I’ll show how to do this in my next post. But for now, for simplicity, I’ll use portal UI.
    8
  • Search for the group you want to assign to the role. For now use the group we created above. Select the group from the grid and click Assign from bottom menu. Choose the role and complete the process.
    1314
  • We are done with infrastructure changes. Most of these are one time things and going forward we just have to change assignments based on requirements.

Final changes to web app and test

  • In your OWIN Auth configuration, add following code snippet to tell ASP.NET pipeline to use role claim for Authorization
    image
  • Modify controllers to use Authorize attribute and IsInRole() as required.
    image
  • We are done. We successfully offloaded our authorization infrastructure to Azure AD.
  • Inspect the claims by logging in with the user who is member of group assigned to role as well as the user who is not a member of that group.

Caution & other important stuff

  • User should be direct member of the group which is assigned to the role. If a user is member of inner group, claims will not contain relevant roles.
  • Apart of groups, users can also be assigned to the roles.
  • Current portal UI only supports assigning a group to only one role. What if you want a group to have multiple roles? For example, our group can be used for both approvers and administrators. Good thing is, this is just a portal UI limitation and we can easily do this via Graph API. I’ll add a sample in my next post.
  • As I told earlier, groups can only be assigned to roles from portal UI if you have premium subscription. Again this is just portal UI limitation and can be easily done via Graph API. I’ll add a sample in my next post.

That is it. I think it is pretty simple and powerful way to offload authorization infrastructure of your application.

References:

  1. https://www.simple-talk.com/cloud/security-and-compliance/azure-active-directory-part-4-group-claims/
  2. http://www.dushyantgill.com/blog/2014/12/10/roles-based-access-control-in-cloud-applications-using-azure-ad/

AZURE Notification HUB

I recently had to implement push notifications for a LOB Windows Phone application. After looking at few available options I decided to use Windows Azure Notification Hub (MSDN).

This article is about how I used notification hub in my app and my learning.

Goal

I have a Windows Phone application using which users can view their pending approvals and they can also approve those requests. These approvals are very critical to business and should be done as soon as possible. We needed a way to provide toast and tile notification with pending approval count.

High Level Solution

NotificationHub

We will create a service which will be called by client (Windows Phone) to register and un-register with notification hub. For sending notifications we will create a console application that can be used as a Windows task.

STEP 1 – Set up your notification hub on Azure

  • First step is to login to Azure portal and create a notification hub that you are going to use
  • Follow instructions given on MSDN to achieve this

STEP 2 – Registering for notifications

Challenges

Security

Registration with notification hub can be done either from a device directly or via some backend service. Process of registration is almost same in both cases. Notification hub provides two type of shared keys, one is for only listening and other one is for all operations. All operations include registration, un-registration and sending of notifications.

If you want to register from client then client should be aware of default full access key which is obviously a security concern. Anyone can use a de-compiler and read the key that you are using in your code. Once they have the key they can do anything 🙂

Ideal place for having registration logic is in your backend service.

Channel Uri

HttpNotificationChannel creates a notification channel between the Microsoft Push Notification Service and the Push Client and creates a new subscription for raw notifications. Currently active notification channel Uri is required to register for notifications.

To get ChannelUri on your device you need to look for ChannelUriUpdated event. This event is raised when the application first receives the notification channel URI from the Microsoft Push Notification Service, or when the notification channel URI is changed. This means that there is a possibility that ChannelUri for your device can change in future. We should register every time ChannelUri is changed.

User Specific Notification

There is high chance that your notifications are user specific. For example, if you are developing mail application and you want to send notification for new mail messages, user should only get notification about his email account.

This can be done by identifying an identifier and using it as a tag while registering channel Uri. Same tag can be used later to send notifications. Notification hub API provides methods that let’s you send notification to all devices (channel Uri) using a specific tag.

Solution

  1. Use client to fetch ChannelUri each time application is launched
  2. Call backend service and pass this ChannelUri. Backend service will register ChannelUri with notification hub
  3. Use user credential (domain/alias or it’s hash) as tag while registering. Create a simple DB table that keeps track of tags that are registered for notification. This will be used to send notifications later. Store tag in the table.
  4. Once registration is done, store ChannelUri in client’s local storage. From next time onwards, send both new and old ChannelUri to service. Service will make appropriate checks and complete the registration process.

Code 

Client Code

// Call this method from your splash screen
public static async Task RegisterForNotifications()
{
var channel = HttpNotificationChannel.Find("MyPushChannel");
if (channel == null)
{
channel = new HttpNotificationChannel("MyPushChannel");
channel.Open();

// I want to use toast notification
channel.BindToShellToast();

// I want to use tile notification
channel.BindToShellTile();
}

if (channel.ChannelUri != null)
{
await ChannelUriNotifier(channel.ChannelUri.ToString());
}

channel.ChannelUriUpdated += new EventHandler<NotificationChannelUriEventArgs>(async (o, args) =>
{
await ChannelUriNotifier(args.ChannelUri.ToString());
});
}

// Call backend service with old and new channel Uri
private static async Task ChannelUriNotifier(string channelUri)
{
// Simple POCO DTO which is sent to service operation
var notificationDTO = new NotificationRegistationDTO
{
// Read old channel Uri from local storage. MyProjectStorage setting is a helper class to manage local storage
OldUri = MyProjectStorageSettings.NotificationsOldUri,
NewUri = channelUri,
// In our project we had to support multiple devices and there is slight variation in code required to register each type.
DeviceType = DeviceType.WindowsPhone
};

// store current channel Uri in local storage
MyProjectStorageSettings.NotificationsOldUri = channelUri;
var rtnVal = await ServiceCallHandeler.RegisterForNotifications<NotificationRegistationDTO>(notificationDTO);
}

Service Code

// Service method calls this method. _notificationRegistrationDTO contains the data passed from the client
public void Register()
{
var newChannelUri = this._notificationRegistationDTO.NewUri;
var oldChannelUri = this._notificationRegistationDTO.OldUri;

// Do validations
if (string.IsNullOrEmpty(newChannelUri) && string.IsNullOrEmpty(oldChannelUri))
{
throw new ArgumentException("Both newChannelUri and oldChannelUri can not be empty for registration operation.");
}

if (string.IsNullOrEmpty(newChannelUri))
{
throw new ArgumentException("NewChannelUri can not be empty for registration operation.");
}

// Create notification hub client. Full shared acces key is stored in configuration
// which is read via ConfigReder.ServiceBusNotificationHubEndpoint()
NotificationHubClient hubClient = NotificationHubClient.CreateClientFromConnectionString(ConfigReader.ServiceBusNotificationHubEndpoint(), ConfigReader.ServiceBusNotificationHubName());

// username is used as tag so that correct notification is sent to the user
var tags = new[]
{
Thread.CurrentPrincipal.GetName())
};

// Create or update registration based in simple checks
if (!string.IsNullOrEmpty(oldChannelUri))
{
var existingRegistrations = hubClient.GetRegistrationsByChannelAsync(oldChannelUri, 100);
existingRegistrations.Wait();

// If registration exists for oldUri then update that with newUri and tag
if (existingRegistrations.Result != null && existingRegistrations.Result.Any())
{
foreach (var registrationDescription in existingRegistrations.Result)
{
this._device.UpdateRegistration(registrationDescription, newChannelUri, tags, hubClient);
}

return;
}
}

var regWithNewChannelUri = hubClient.GetRegistrationsByChannelAsync(newChannelUri, 100);
regWithNewChannelUri.Wait();

// if registration do not exists with new channel Uri then Create else Update
if (regWithNewChannelUri.Result == null || !regWithNewChannelUri.Result.Any())
{
this._device.CreateRegistration(newChannelUri, tags, hubClient);
}
else
{
foreach (var registrationDescription in regWithNewChannelUri.Result)
{
this._device.UpdateRegistration(registrationDescription, newChannelUri, tags, hubClient);
}
}
}

STEP 2 – Unregister

Challenges

Security

Just like registration, un-registration also requires access to full access key. Due to this we should not do this action from client. We will again do this from our backend service.

Performance

Ideally, when a user un-registers from notification hub and if there are no other registrations for that specific user (tag) then that tag should be deleted from our database. This is a time taking process.

Solution

  1. Send old channel Uri and current channel Uri to service
  2. Service for available registrations for both Uri’s and cancels registration
  3. Do not delete tag from database as it takes little time. We will do this from our notification task.

Code

Server side code

public void Unregister()
{
var newChannelUri = this._notificationRegistationDTO.NewUri;
var oldChannelUri = this._notificationRegistationDTO.OldUri;

if (string.IsNullOrEmpty(newChannelUri) && string.IsNullOrEmpty(oldChannelUri))
{
throw new ArgumentException("Both newChannelUri and oldChannelUri can not be empty for unregistration operation.");
}

NotificationHubClient hubClient = NotificationHubClient.CreateClientFromConnectionString(ConfigReader.ServiceBusNotificationHubEndpoint(), ConfigReader.ServiceBusNotificationHubName());

// Delete registration matching old channel URI
if (!string.IsNullOrEmpty(oldChannelUri))
{
var existingRegistrations = hubClient.GetRegistrationsByChannelAsync(oldChannelUri, 100);
existingRegistrations.Wait();

if (existingRegistrations.Result != null && existingRegistrations.Result.Any())
{
foreach (var registrationDescription in existingRegistrations.Result)
{
hubClient.DeleteRegistrationAsync(registrationDescription);
}
}
}

// Delete registration matching new channel URI
if (!string.IsNullOrEmpty(newChannelUri))
{
var existingRegistrations = hubClient.GetRegistrationsByChannelAsync(newChannelUri, 100);
existingRegistrations.Wait();

// If registration exists for oldUri then update that with newUri and tag
// else create new using newChannelUri
if (existingRegistrations.Result != null && existingRegistrations.Result.Any())
{
foreach (var registrationDescription in existingRegistrations.Result)
{
hubClient.DeleteRegistrationAsync(registrationDescription);
}
}
}

}

STEP 3 – Test registrations

This one is pretty easy :). Login to your azure notification hub portal and you will see debug option on top of the page.

From this screen you can send notification to all channel URIs or use specific tags to send notifications. This is pretty handy if you don’t have notification send task set up yet.

STEP 4 – Send Notifications

I think once you figure our first two steps, this one is a cake walk. I did not find any challenge in making this work, so I’ll simply go to the code.

  • Create a console application which will for each tag saved in your database (during registration) get notification hub registrations
  • If there are no registrations for a specific tag then remove that tag from database
  • If there is any registration for that tag then prepare appropriate tile and toast template
  • Use template and send notification using tag
public async void NotifyUsers()
{
Logger.Log.Info("Started notification task.");

// Get a list of tags registered. I get this from my database table
var notificationData = new NotificationData();
var registeredUsers = notificationData.GetRegisteredUsers();

foreach (var user in registeredUsers)
{
try
{
Logger.Log.Info(string.Format("Sending notifications for {0}.", user ));
var registrations = await HubClient.GetRegistrationsByTagAsync(user, 100);

// if there are no registrations for this tag (user) then delete from database
if (registrations == null || !registrations.Any())
{
Logger.Log.Info(string.Format("No existing registrations found for {0}. Deleting record from database.", user));
notificationData.DeleteNotificationRecipient(user);
continue;
}

// This is the number that I show in notification.
var notificationForUser = notificationData.GetPendingActionsCount(user);
if (notificationForUser == 0)
{
Logger.Log.Info(string.Format("No pending action found for {0}. Notification is not required.", user));
continue;
}

//Windows Phone 8
await SendWindowsPhoneNotifications(notificationForUser, user);

Logger.Log.Info(string.Format("Updating last notification sent time for {0}.", user));
notificationData.UpdateLastNotificationSentTime(user);
}
catch (Exception ex)
{
var message = string.Format("Sending notifications to user {0} failed. Error: {1}", user, ex);
Logger.Log.Error(message);
continue;
}
}
}

private async Task SendWindowsPhoneNotifications(int notificationForUser, string user)
{
Logger.Log.Info(string.Format("Sending Windows Phone 8 notifications for {0}.", user));

await HubClient.SendMpnsNativeNotificationAsync(
string.Format(NotificationTemplates.WP8Tile, notificationForUser),
user);

await HubClient.SendMpnsNativeNotificationAsync(
string.Format(NotificationTemplates.WP8Toast, notificationForUser),
user);
}

public const string WP8Toast = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Toast>" +
"<wp:Text1>MyProject</wp:Text1>" +
"<wp:Text2>You have {0} pending actions</wp:Text2>" +
"</wp:Toast>" +
"</wp:Notification>";

public const string WP8Tile = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Tile>" +
"<wp:Count>" + "{0}" + "</wp:Count>" +
"<wp:Title>" + "MyProject" + "</wp:Title>" +
"<wp:BackTitle>" + "MyProject" + "</wp:BackTitle>" +
"<wp:BackContent>" + "You have {0} pending actions." + "</wp:BackContent>" +
"</wp:Tile> " +
"</wp:Notification>";

Make sure you provide easy way for users to register and unregister from within your app. I’ll try to upload a working sample soon.

logging

Logging is an essential component of developing applications. It helps in many ways, but still many developers being overconfident about their code never add proper logging to their applications.

It is not a difficult task, I think it is one of the most easiest thing that developers can do to make their code more reliable and easy to debug. There are many frameworks that can be used, most of which are extremely simple. Sometimes developers add these framework to their projects, but they seldom use them properly.

For example, what will you do if you see a log message that says:

We can not submit your request.

Apart from knowing something failed, what help can anyone get from this? Nothing at all. This message is logged as a custom message and it could have been a little more verbose.

Another form of message is this,

Object reference not set to an instance of object.

Now you know exactly which error happened, but again, can you do anything about this? Little stack trace info would have been awesome. This message is of no use without information about error source location.

Many times we validate our data transfer objects for data validity. You submit something from client to service and request fails due to invalid data. Most of the time you will see a message like this:

Request failed due to validation issue. ID – 1. Followed by stack trace and error location.

Now this message is definitely more helpful, but still we can have more information here like which validation failed and what was the data value for which validation failed.

Most of the applications follow layered architecture which means they will have a data layer, business layer and UI. Business layer most of the time is implemented via services. In this architecture, you will usually have 2 log files. One for UI and other one for service. Now, suppose error happened and you go to UI log file and see this:

UI – Log. Service failed to process this request. Please contact administrator.

Now, you open service log and see more than one error messages. Huh…how will you now relate UI error message with service error message. If your app is used by few people, only few times a day, you can use log message time stamps to relate these errors. Most of the time this will not work and you will have multiple log message with almost similar time stamp. One way to easily solve this is by generating a correlation Id (Guid), logging that with error message in service log and then pass that same correlation Id as part of service fault. Log the UI error message with same correlation ID and you are done. So you will see something like this in UI log file;

(ID: 1234-23322-34322….) Service failed to process this request. Please contact administrator.

and service log will have error logged like this;

(ID: 1234-23322-34322….) Invalid argument [User Name – Brad John]. Data not found in data store. Followed by stack trace.

Now you can use this pattern to show generic error message with correlation ID on user interface and then ask user to pass that correlation ID to your admin/ support team. They can easily get to the real problem.

At a high level, you should do this for logging:

  • don’t shy away from logging verbose errors
  • don’t just log error message (ex.Message)
  • don’t forget to add stack trace information
  • use throw in place of throw ex. If throwing new exception, then add old exception as inner exception. This will preserve stack trace information.
  • provide extensive information that can be used for debugging
  • log exactly due to which data value a business validation failed
  • log informational messages that can be easily turned off via configuration
  • use correlation pattern to relate error message logs in different app layers

These are few things which I’ve on top of my head. Over the years I’ve used multiple logging frameworks with each one having their own pros and cons. In next few posts I’ll discuss these frameworks and thier usage.

Cancelling async tasks – C#

async await programming pattern has become very common these days. For developers working on Windows store apps there is no other option apart from learning this pattern.

Few days back I was working on an app with a simple control flow:

  • User starts the application
  • Using async await we try to start a long running process
  • Once process is completed, results are shown to the user

One feedback that we got was to allow user cancel this long running process if he wishes to do so. All we have to do is give user a progress indicator, progress text and a button to cancel the current long running async task.

Let’s see our existing code. In our view model we have a method that is used to load media files in a playlist;

public async Task<bool> AutoLoadPlaylist()
{
try
{
this.InProgress = true;
this.ProgressText = "We are trying to add your last saved playlist";
if (string.IsNullOrEmpty(AutoSavedPlayListPath))
return false;

var files = await _command.AddPlayList(AutoSavedPlayListPath);
await AddMusicFiles(files);
}
finally
{
this.InProgress = false;
}
return true;
}
 
Here we call another command which is responsible for actually creating this playlist;
 
public async Task<IList<StorageFile>> AddPlayList(string path)
{
try
{
var file = await StorageFile.GetFileFromPathAsync(path);
Playlist playList = await Playlist.LoadAsync(file);
return playList.Files;
}
catch (FileNotFoundException)
{
AutoSavedPlayListPath = string.Empty;
return null;
}
}

and, finally we call AutoLoadPlaylist(…) method from our view;

await this.viewModel.AutoLoadPlaylist();
 
Now, in order to provide a way to cancel this operation we need to change our code little bit to implement task cancellation pattern. This is done via CancellationTokenSource class defined in System.Threading namespace.
 
First, let’s modify our command class method to accept a cancellation token as method argument and then use that token while starting the task;
 

public async Task<IList<StorageFile>> AddPlayList(string path,

CancellationToken cancellationToken)
{
try
{
var file = await StorageFile.GetFileFromPathAsync(path);
Playlist playList = await Playlist
.LoadAsync(file)
.AsTask(cancellationToken);
return playList.Files;
}
catch (FileNotFoundException)
{
AutoSavedPlayListPath = string.Empty;
return null;
}
}

Now, modify the method in view model to accept cancellation token and then pass it on to command method. We also need to catch OperationCanceledException which is thrown when a task is cancelled.

public async Task<bool> AutoLoadPlaylist(CancellationToken cancellationToken)
{
try
{
this.InProgress = true;
this.ProgressText = "We are trying to add your last saved playlist";
if (string.IsNullOrEmpty(AutoSavedPlayListPath))
return false;

var files = await _command.AddPlayList(AutoSavedPlayListPath,

cancellationToken);
await AddMusicFiles(files);
}
catch (OperationCanceledException)
{
// no need to do anything
}
finally
{
this.InProgress = false;
}
return true;
}

Now modify the code that is calling view model’s AddPlayList(…) method and pass a cancellation token instance;

CancellationTokenSource cts;

CancellationToken _cancellationToken;


private async void InitializeControls()
{
try
{
cts = new CancellationTokenSource();
_cancellationToken = cts.Token;

await this.viewModel.AutoLoadPlaylist(_cancellationToken);
}
catch (Exception ex)
{
var dlg = new MessageDialog(ex.Message);
dlg.ShowAsync();
}
}
 
Infrastructure wise we are done. Only thing left now is to provide a button which user will click to cancel this long running operation. In your cancel button click (tapped) event handler add this code;
 
if (cts != null)
{
this.viewModel.ProgressText = "Trying to cancelling the operation";
cts.Cancel();
}

Make sure you cancel correct cancellation token source object 🙂