C#

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.

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 🙂

Exception dispatch info

Imagine you have a simple code as shown below:

class Program
{
static void Main()
{
try
{
SimpleErrorCheck();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}

public static void SimpleErrorCheck()
{
try
{
int b = 1;

int result = (b / (b - 1));

Console.WriteLine("Result is {0}", result);
}
catch (Exception ex)
{
// log

throw;
}
}
}

A call is made to SimpleErrorCheck() method where we do some calculations that results in a divide by zero exception.

Using throw ex overwrites the stack trace while using throw keeps it intact so that you can go back and see where exactly the error happened. You can also examine generated IL and see rethrow is used if we do throw ex.

throw is only available inside catch block. If there is some stuff like code related to rollback or anything else that you would like to do outside of catch block and then throw the error, you will have to rethrow which will overwrite the stack trace.

In .Net 4.5 ErrorDispatchInfo class is introduced to solve this problem.
 
Let’s change our code to use ErrorDispatchInfo class:
 
class Program
{
static void Main()
{
try
{
SimpleErrorCheck();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}

public static void SimpleErrorCheck()
{
ExceptionDispatchInfo exInfo = null;

try
{
int b = 1;

int result = (b / (b - 1));

Console.WriteLine("Result is {0}", result);
}
catch (Exception ex)
{
exInfo = ExceptionDispatchInfo.Capture(ex);
}

// do some operation
if (exInfo != null)
{
exInfo.Throw();
}
}
}

Let’s examine the stack trace that we got in console:

image

Stack trace of original exception is preserved and we can get exact line of code.

Fluent API – Improvements

Please read my earlier article on Fluent API before reading this post. I wrote my old article to get you started with Fluent API design. There are few essential improvement that we should do in that implementation.

In our Fluent interface all methods were returning similar type of object, IFluentPolicyEngine

public interface IFluentPolicyEngine
{
IFluentPolicyEngine CreatePolicy(string name, string description);

IFluentPolicyEngine PerformsOperationOnData(string operation);

IFluentPolicyEngine HavingDataFilter(string type);

IFluentPolicyEngine Where(string filter);

IFluentPolicyEngine Validate();

void Save();
}

This makes it very easy to chain methods but in real world applications this will not work well. There should be a predefined way of chaining methods. Above interface let’s you chain methods in any order. There will be some business rules that should be used while creating these chains. Designing your fluent API based on business rules helps in:

  • Business rule driven data validation
    Suppose our policy has a business rule, a policy can have filter statements added via Where(…) only if data filter is added to the policy via HavingDataFilter(…). It is not possible to restrict chaining using our above interface. Only possible solution is to do a check in API method and throw exception. Nahhh :(, we should strive for compile time safety rather than runtime.
  • Improving code readability
    If API is designed carefully based on business rules then rather than sprinkling validation code in all methods we can have far better data validation routines

Let’s modify our fluent API to support following business rules:

  • Client can only add policy filter after adding business operation
  • Client can only add filter statements via Where(…) after data filter is added via AddDataFilter(…)
  • Client can only validate policy when an operation and at least one filter statement is added to the policy
  • Validation can only be performed after adding operation and filter to the policy
  • Save can only be called on a validated policy

New interface design looks like this:

public interface IFluentPolicyEngine
{
IFluentPolicyEngine CreatePolicy(string name, string description);

IFluentPolicyWithOperation PerformOperationOnData(string operation);
}

public interface IFluentPolicyWithOperation
{
IFluentPolicyWithFilter HavingDataFilter(string type);
}
public interface IFluentPolicyWithFilter
{
IFluentPolicyWithFilter Where(string filter);

IValidatedPolicy Validate();
}

public interface IValidatedPolicy
{
void Save();
}

Modify the class where old single interface was implemented by implementing all new interfaces and changing method return types. Client code remains the same.

Now you can only write client code with proper business rule driven method chaining. Ad hoc chaining will result in compile time issues. Download the code and try changing things.

Download Code : http://1drv.ms/1o3lnyC

Starting with Fluent API

Last week, while trying out a new logging mechanism, Serilog, I stumbled upon a new style of API design. It is known as Fluent API and looked like an interesting topic to understand.

What is Fluent API?

This term was coined by object oriented programming guru Martin Fowler in 2005. Not so new 🙂 His idea was to develop client friendly code that is easy to understand (read) and easy to maintain.

Business processes are defined by developing mix of objects and then chaining them together via some internal domain specific language. LINQ is a perfect place to start with. The way multiple methods are chained together to create a SQL like business operation is an example of Fluent API. We keep on adding Where(), Select(), Take() etc. methods to our code statement and each operation returns IEnumerable object. This is commonly known as method chaining. See following example to understand it better:

var underAgeCustomerCount = Customers                
.Where(c => c.Age <= 18)
.OrderBy(c => c.FirstName)
.Count();

How it is done?

I’ll take one of my real life project as a sample and show how we can add a very basic Fluent interface to that.

Business Goal of the project

This project contains a policy engine where users can define policies based on which operations are performed on data. Users can create multiple policies and save them. When data passes through this policy engine, appropriate policy is kicked off and a specific operation is performed.

Each policy consists of an operation and set of filters. Consider filter as a SQL WHERE clause. It can have multiple filter statements and these statements are added together by either AND or OR. So a filter can look like this:

WHERE Name = 'Gaurav Sharma' AND Age > 18 AND City = 'HYD'

Above is a filter, having 3 statements and uses AND as separator.

Operation is a method that is executed if data matches with defined filter.

Current Implementation

There are POCO classes defined for Policy, Operation, Filter and FilterStatement. Then there is a PolicyManager class that provided functionality to Validate and Save policy. Code looks like this:

public class Policy
{
public Guid ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public Filter Filter { get; set; }
public Operation Operation { get; set; }
}


public class Filter
{
public string Type { get; set; }
public List<FilterStatement> FilterStatements { get; set; }
}
public class FilterStatement
{
public string Statement { get; set; }
}
public class Operation
{
public string Name { get; set; }
}
 
public class PolicyManager
{
public void Validate(Policy policy)
{
// check for invalid properties in policy object
// throw exception in case of validation failures
}

public Policy Save(Policy policy)
{
// create policy and save to store

// create a new ID and update the object
policy.ID = Guid.NewGuid();

return policy;
}
}
 
This is very basic representation of what we are actually doing in our project but for this blog I think it is sufficient. Now if you have to write client code for this then it would look similar to:
 
var policy = new Policy();

policy.Name = "Sample policy";
policy.Description = "This is a sample policy";

var operation = new Operation();
operation.Name = "Create Order";

var statement1 = new FilterStatement();
statement1.Statement = "City = New Delhi";

var statement2 = new FilterStatement();
statement1.Statement = "CustomerName = Gaurav Sharma";

var statement3 = new FilterStatement();
statement1.Statement = "TotalAmount > 100000";

var filterStatements = new List<FilterStatement>();
filterStatements.Add(statement1);
filterStatements.Add(statement2);
filterStatements.Add(statement3);

var filter = new Filter();
filter.FilterStatements = filterStatements;

policy.Operation = operation;
policy.Filter = filter;

var manager = new PolicyManager();
manager.Validate(policy);
policy = manager.Save(policy);

Console.WriteLine("Successfully saved policy with ID {0}", policy.ID);

Remember good old ADO.NET days when we use to write this type of code for data operations. Remember DataConnection, DataReader, DataSet etc. It was painful.

We can clean this a little more by using different constructors and object initializes. Let’s skip that for now. Now we will add very simple Fluent interface implementation to our project and see how it improves client code.

public interface IFluentPolicyEngine
{
IFluentPolicyEngine CreatePolicy(string name, string description);

IFluentPolicyEngine PerformsOperationOnData(string operation);

IFluentPolicyEngine HavingDataFilter(string type);

IFluentPolicyEngine Where(string filter);

IFluentPolicyEngine Validate();

void Save();
}
 
See the return type defined for our new fluent interface methods. This helps us in chaining these methods together. Let’s implement this interface.
 
public class FluentPolicyEngine : IFluentPolicyEngine
{
public Policy Policy { get; set; }

public FluentPolicyEngine()
{
Policy = new Policy();
}
public IFluentPolicyEngine CreatePolicy(string name,
string description)
{
Policy.Name = name;
Policy.Description = description;
return this;
}

public IFluentPolicyEngine PerformsOperationOnData(string operation)
{
var op = new Operation
{
Name = operation
};
Policy.Operation = op;
return this;
}

public IFluentPolicyEngine HavingDataFilter(string type)
{
var filter = new Filter
{
FilterStatements = new List<FilterStatement>(),
Type = type
};
Policy.Filter = filter;
return this;
}

public IFluentPolicyEngine Where(string filter)
{
var filterStatement = new FilterStatement
{
Statement = filter
};
Policy.Filter.FilterStatements.Add(filterStatement);

return this;
}

public IFluentPolicyEngine Validate()
{
if (Policy.Operation == null)
throw new InvalidDataException("Operation is not provided.");

if (!(Policy.Filter != null
&& Policy.Filter.FilterStatements != null
&& Policy.Filter.FilterStatements.Count > 0))
throw new InvalidDataException("Filter is missing.");

return this;
}

public void Save()
{
Policy.ID = Guid.NewGuid();
}
}
 
This is again a very simple implementation of our interface but sufficient for this write up. Methods are simple and self explanatory. Now our new client code looks like:
 
var policyEngine = new FluentPolicyEngine();
policyEngine
.CreatePolicy("Fluent Policy", "Fluent policy sample")
.PerformsOperationOnData("Create Order")
.HavingDataFilter("All")
.Where("City = New Delhi")
.Where("CustomerName = Gaurav Sharma")
.Where("TotalAmount > 100000")
.Validate()
.Save();

Console.WriteLine("Successfully saved policy with ID {0}",
policyEngine.Policy.ID);
 
See how readable this code is now.
Some important points
  • Unless client developer understand Fluent API business domain, it is very tough to write this type of code. Think about a developer knowing nothing about SQL and then trying his hands on LINQ.
  • Debugging is little difficult in this type of client code. You can not put breakpoint on a specific method call. 
  • There are multiple ways of doing what I just showed you. For example, in C# we can use extension methods to achieve similar results.
  • Lot of configuration APIs provide fluent interfaces. Entity framework is one of them.
  • Although it simplifies client code, your API code can become complex. Keep a watch on that.

It is not easy to create efficient Fluent API. You have to think a lot. So think.then.code.

Download Code: http://1drv.ms/1khBvvD