The Bot equivalent of message boxes

In Windows (and most user interface frameworks) we have the concept of message boxes and dialog boxes.

We’ve already seen that we would create an implementation of an IDialog to interact with user input/commands, but the Bot framework also includes the equivalent of a Yes/No message box. For example

public async Task MessageReceivedAsync(
   IDialogContext context, 
   IAwaitable<IMessageActivity> argument)
{
   PromptDialog.Confirm(
      context,
      CalculateAsync,
      "Do you want to calculate risk?",
      "Unknown option");
}

public async Task CalculateAsync(
   IDialogContext context, 
   IAwaitable<bool> argument)
{
   var confirm = await argument;
   await context.PostAsync(confirm ? "Calculated" : "Not Calculated");
   context.Wait(MessageReceivedAsync);
}

What we have now is a prompt dialog which, when seen in the Bot framework channel emulator will ask the question “Do you want to calculate risk?”. This prompt will also display two buttons Yes & No. The user can press the button or type Y, N, Yes or No. Assuming a valid response is given by the user then the CalculateAsync method is called. If the response is not Y, Yes, N or No (obviously on an English language setup) then the prompt is displayed with the “Unknown option” reply that we specified and the dialog again waits for input (you can set the number of retries if you want the user to have three attempts, for example, to respond correctly to a prompt).

We can remove the Yes/No buttons by using promptStyle: PromptStyle.None, i.e.

PromptDialog.Confirm(
   context,
   CalculateAsync,
   "Do you want to calculate risk?",
   "Unknown option", 
   promptStyle: PromptStyle.None);

Using the Bot framework

Introduction

If you’re not sure what a Bot is, then check out What is Microsoft Bot Framework Overview.

A Bot doesn’t have to be intelligent and/or understand complex input. Although this sounds the most exciting aspects of using Bots we could also just use a Bot like an API into our services/applications.

What Bots do generally have is some simple user interface for a user to interact with the Bot. We’ve probably heard of or used a “chat bot” in the guise of a text input box which the user types into, but the bot could take input from buttons as well or other control/input mechanisms.

Prerequisites

Whilst we can write our Bot code from scratch we may as well use the Bot items templates which can be downloaded from http://aka.ms/bf-bc-vstemplate

Cortana skills templates can be downloaded from https://aka.ms/bf-cortanaskill-template but are not required for this post.

Once downloaded, place the zip files (for these templates) into %USERPROFILE%\Documents\Visual Studio 2017\Templates\ProjectTemplates\Visual C#.

Now when you start/restart Visual Studio 2017 these templates will be available in New | Project.

Next up you might like to download the Bot framework emulator from Microsoft/BotFramework-Emulator on github. I’m using the botframework-emulator-Setup-3.5.31 version for this and other posts.

Writing my first Bot

  • Create New | Project and select Bot application (mine’s named TestBot)
  • Build the project to pull in all the NuGet packages

At this point we have an ASP.NET application or more specifically we’ve created an ASP.NET Web API. The key areas of interest for us (at this point) is the Controllers/MessageController.cs file and the Dialogs/RootDialog.cs file.

At this point the template has created an Echo bot. We can run up this Bot application and you’ll be presented with a web page stating we need to register our Bot. Ofcourse we’d probably prefer to test things out first on our local machine without this extra requirement. We can run the Bot Framework Channel Emulator (as mentioned in the prerequisites section). So let’s see our Bot in action

  • Run you Bot application
  • Run the Bot emulator
  • In the emulator type the following http://localhost:3979/api/messages (obviously replace the localhost:3979 with whatever host/port you are running your Bot application from).
  • If all worked, simply type some message into the “Type your message…” textbox and the Bot your respond with “You sent which was

If you click on either the message or response, the emulator will display the JSON request and response data.

What’s the code doing?

We’ve seen that we can communicate with our Bot application but what’s really happening?

Obviously the Bot framework handles a lot of the communications for us. I’m not going to go too in depth with this side of things for the simple reason that, at the moment, I don’t know enough about how it works to confidently state how everything fits together. So hopefully I’ll get around to revisiting this question at another time.

I mentioned that MessagesController.cs was a key file. This is where we received messages and also where system messages can be handled. Think of it as the entry point to your application. In the template echo Bot we have this code

if (activity.Type == ActivityTypes.Message)
{
   await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
}

When a Message type is received (types can be Message, Event, Ping and more, see ActivityTypes).

If you take a look at Dialogs/RootDialog.cs within your solution you’ll see that this implements the IDialog<> interface which requires the Task StartAsync(IDialogContext context) to be implemented. This method basically calls the method MessagedReceivedAsync method (shown below) which is where our “echo” comes from

private async Task MessageReceivedAsync(
   IDialogContext context, 
   IAwaitable<object> result)
{
   var activity = await result as Activity;

   // calculate something for us to return
   int length = (activity.Text ?? string.Empty).Length;

   // return our reply to the user
   await context.PostAsync(
      $"You sent {activity.Text} which was {length} characters");

   context.Wait(MessageReceivedAsync);
}

Making the Bot a little more useful

We’ve looked at generating a echo Bot which is a great starting point but let’s now start code something of our own.

You’ll notice from the code

int length = (activity.Text ?? string.Empty).Length;

This shows that the activity is supplied with the text sent to the Bot via the message input. Obviously in a more complicated interaction, one might send this text to a NPL service, such as Luis or slightly more simply, use RegEx or ofcrouse creating your own parser. But if our Bot was just designed to respond to simple commands, be they sent as messages via a user textual input or from a button press, then the MessageReceivedAsync code could be turned into the equivalent of a switch statement.

Let’s rewrite the MessageReceivedAsync method to act as a basic calculator where we have three commands, using ? to list the commands, and the add and subtract commands, both of which take 2 parameters (we could easily extend these to more parameters or switch to using operators but let’s start simple).

Simply remove the existing MessageReceivedAsync method and replace with the following

private async Task MessageReceivedAsync(
   IDialogContext context, IAwaitable<object> result)
{
   var activity = await result as Activity;

   string response = "I do not understand this command";
   var command = activity
      .Text
      .ToLower()
      .Split(new []{' '}, 
         StringSplitOptions.RemoveEmptyEntries);

   switch (command[0])
   {
      case "?":
         response = ListCommands();
         break;
      case "add":
         response = Add(command[1], command[2]);
         break;
      case "subtract":
         response = Subtract(command[1], command[2]);
         break;
   }

   await context.PostAsync(response);

   context.Wait(MessageReceivedAsync);
}

private string ListCommands()
{
   return "? => list commands\n\nadd a b => adds a and b\n\nsubtract a b => subtract a from b\n\n";
}

private string Add(string a, string b)
{
   double v1, v2;
   if (Double.TryParse(a, out v1) && Double.TryParse(b, out v2))
   {
      return (v1 + v2).ToString();
   }

   return "Non numeric detected";
}

private string Subtract(string a, string b)
{
   double v1, v2;
   if (Double.TryParse(a, out v1) && Double.TryParse(b, out v2))
   {
      return (v2 - v1).ToString();
   }

   return "Non numeric detected";
}

Obviously we’re light on error handling code etc. but you get the idea.

Note: In the ListCommands, to output a new line (i.e. display the list of commands in a multi-line response using the emulator) we need to use two newline characters. We could call context.PostAsync multiple times but this would appear as different message responses.

Fixie, convention based unit testing

I’m always looking around at frameworks/libraries etc. to see if they enhance my development experience or offer new tools/capabilities that could be of use. I’m not wholly sure how I came across Fixie, but I wanted to check it out to see what it had to offer.

Introduction

Fixie is a convention based unit testing library, think NUnit or xUnit but without attributes.

It’s amusing that after re-reading the sentence above, I totally missed the irony that Fixie is really more of a throw back to jUnit with it’s convention based testing than the .NET equivalents with their attributes.

I’m not usually a big fan of convention based libraries, but there’s no doubt that if you’re happy with the convention(s) then code can look pretty clean.

The coolest thing with Fixie (well it’s cool to me) is that you simply add the nuget package to your solution, but don’t need to add a using clause that references Fixie (unless you wish to switch from the default conventions).

Fixie is immediately (or near as immediately) recognized by the Visual Studio Test Runner, but Resharper’s test runner, sadly, does not work with it “out of the box” (I believe there is a Resharper test runner, but I’ve not tried it and it didn’t appear in their extension manager, so I’m not sure what the state is of this at the time of writing.

Default Convention

The default convention is that your test class name is suffixed with the word Tests and methods can be named anything, but must return either void or async and be public, i.e.

public class MatrixTests
{
   public void AddMatrix()
   {
      // test based code
   }

   public async Task SubtractMatrix()
   {
      // test based code
   }
}

I’ve purposefully not written any assertion code because, Fixie does not supply any. Such code should be supplied using the likes of Should, Shouldly, FluentAssertions etc.

Custom Convention

Fixie gives us the tools to define our own custom convention if we want to change from the default. This includes the ability to not only change the naming conventions but also to include attributes to mark tests (like NUnit/xUnit uses). In fact, with this capability Fixie can be used to run NUnit or xUnit style tests (as we’ll see later). For now let’s just create our own test attribute

AttributeUsage(AttributeTargets.Method)]
public class TestAttribute : Attribute
{
}

So this attribute is only declared on methods. To implement a new convention we simple create a class derived from Fixies.Convention, for example

public class TestConvention : Convention
{
   public TestieConvention()
   {
      Methods.HasOrInherits<TestAttribute>();
   }
}

so now our tests might look like this

public class MatrixTests
{
   [Test]
   public void AddMatrix()
   {
      // test based code
   }

   [Test]
   public async Task SubtractMatrix()
   {
      // test based code
   }
}

So in essence, using Fixie, we could create our own test framework (of sorts) and using (as already mentioned) Should, Shouldly, Fluent Assertions etc. libraries to handle asserts. However, I’m sure that’s ultimately not what this functionality is for, but instead allows us to define our own preferred conventions.

See also Custom Conventions

NUnit & xUnit Convention

I mentioned that we could use Fixie to execute against other testing frameworks and on the Fixie github repository, we can find two such convention classes (and support classes) to allow Fixie to run NUnit or xUnit style tests.

See NUnit Style and xUnit Style.

I doubt the intention here is to replace NUnit or xUnit with Fixie as obviously you’d need to support far more capabilities, but these convention repositories give a great overview of what can be done on the Fixie convention front.

Using Azure storage emulator

For all of my posts on using Azure Storage I’ve been using my online account etc. but ofcourse it’s more likely we’d normally want to test our code using an offline solution, hence it’s time to use the storage emulator.

In your App.config you need to have the following

<appSettings>
   <add key="StorageConnectionString" value="UseDevelopmentStorage=true;" />
</appSettings>

Now the following code (taken from my Azure table storage post) will use the local storage/Azure storage emulator.

var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
   "StorageConnectionString"));

var client = storageAccount.CreateCloudTableClient();
var table = client.GetTableReference("plants");

table.CreateIfNotExists();

var p = new Plant("Fruit", "Apple")
{
   Comment = "Can be used to make cider",
   MonthsToPlant = "Jan-Mar"
};

table.Execute(TableOperation.InsertOrMerge(p));

Note: If not already installed, install Azure Storage Emulator from https://azure.microsoft.com/en-gb/downloads/

and Azure storage emulator will be installed in

C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator

Microsoft Azure Storage Explorer can be used to view the storage data on your local machine/via the emulator and when you open the Development account in the Local and Attached Storage Accounts this will run the emulator, however I had several problems when running my client code.

This firstly resulted in no data in the table storage and then a 400 bad request.

So I had to open my command prompt at C:\Program Files (x86)\Microsoft SDKs\Azure\Storage Emulator (or run from the Window run/search textbox “Microsoft Azure Storage Emulator” to get a command prompt which will already have the emulator running and offers a CLI to interact with the emulator) and from here you can check the status, start, stop and create local storage using AzureStorageEmulator.exe. All looked fine but still didn’t work. Azure storage emulator, ultimately uses a default local MSSQL DB, so time to check things on the DB side.

Using Microsoft SQL Server Management Studio I connected to (local)\MSSQLLocalDb and under databases deleted the AzureStorangeEmulatorDb52 (ofcourse Azure Storage Emulator needs to have been stopped) as I was using Azure Storage Emulator 5.2, the DB name may differ depending on the version of the emulator you’re running.

From the Azure Storage Emulator, I ran

AzureStorageEmulator init

to recreate the DB (you might need to use the /forceCreate switch to reinitialize the DB). Oddly I still got an error from this command (see below)

Cannot create database 'AzureStorageEmulatorDb52' : The login already has an account under a different user name.
Changed database context to 'AzureStorageEmulatorDb52'..
One or more initialization actions have failed. Resolve these errors before attempting to run the storage emulator again.
Error: Cannot create database 'AzureStorageEmulatorDb52' : The login already has an account under a different user name.
Changed database context to 'AzureStorageEmulatorDb52'..

However if Microsoft SQL Server Management Studio all looked to be in place. So I re-ran my code and this time it worked.

Now we can run against the emulator and work away for the internet and/or reduce traffic and therefore possible costs of accessing Azure in the cloud.

References

Use the Azure storage emulator for development and testing

Combining Serilog and Table Storage

As I’ve written posts on Serilog and Azure Storage recently, I thought I’d try to combine them both and use Azure table storage for the logging sink. Thankfully, I didn’t need to code anything myself, as this type of sink has already been written, see the AzureTableStorage sink.

If we take the basic code from Serilog revisited (now version 2.5) and Azure Table Storage and include the glue of AzureTableStorage. We get the following

  • Add NuGet Package Serilog.Sinks.AzureTableStorage
var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
   "StorageConnectionString"));

Log.Logger = new LoggerConfiguration()
   .WriteTo.AzureTableStorage(storageAccount, storageTableName: "MyApp")
   .CreateLogger();

Log.Logger.Information("Application Started");

for (var i = 0; i < 10; i++)
{
   Log.Logger.Information("Iteration {I}", i);
}

Log.Logger.Information("Exiting Application");

Without the storageTableName we’ll have the table LogEventEntity as the default table name. That’s all there is to it, now we’re sending our log entries into the cloud.

Azure Table Storage

Table storage is a schema-less data store, so has some familiarity to No-SQL databases.

For this post I’m just going to cover the code snippets for the basic CRUD type operations.

Creating a table

We can create a table within table storage using the following code.

At this point the table is empty of both data and ofcourse, being schema-less it has no form, i.e. it’s really just an empty container at this point.

var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
      "StorageConnectionString"));

var client = storageAccount.CreateCloudTableClient();
var table = client.GetTableReference("plants");

table.CreateIfNotExists();

Deleting a table

Deleting a table is as simple as this

var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
      "StorageConnectionString"));

var client = storageAccount.CreateCloudTableClient();
var table = client.GetTableReference("mytable");

table.Delete();

Entities

Using the Azure table API we need to implement the ITableEntity interface or derive our entities from the TableEntity class. For example

class Plant : TableEntity
{
   public Plant()
   {
   }

   public Plant(string type, string species)
   {
      PartitionKey = type;
      RowKey = species;
   }

   public string Comment { get; set; }
}

In this simple example we map the type of plant to a ParitionKey and the species to the RowKey, obviously you might prefer using Guid’s or other ways of keying into your data. The thing to remember is that, the ParitionKey/RowKey must be unique to the table. Obviously the example above is not going to make code very readable so it’s more likely that we’d also declare properties with more apt names, such as Type and Species, but it was meant to be a quick and simple piece of code.

Writing an entity to table storage

Writing of entities (and many other entity operations) is handled by the Execute method on the table. Which operation we use is determined by the TableOperation passed as a parameter to the Execute method.

var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
      "StorageConnectionString"));

var client = storageAccount.CreateCloudTableClient();
var table = client.GetTableReference("plants");

table.CreateIfNotExists();

var p = new Plant("Flower", "Rose")
{
   Comment = "Watch out for thorns"
};

table.Execute(TableOperation.Insert(p));

This will throw an exception if we already have an entity with the PartitionKey/RowKey combination in the table storage. So we might prefer to tell the table storage to insert or update…

Updating entities within table storage

If we prefer to handle both insertion OR updating within a single call we can use the TableOperation.InsertOrReplace method

var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
      "StorageConnectionString"));

var client = storageAccount.CreateCloudTableClient();
var table = client.GetTableReference("plants");

var p = new Plant("Flower", "Rose")
{
   Comment = "Thorns along the stem"
};

table.Execute(TableOperation.InsertOrReplace(p));

There’s also a TableOperation.InsertOrMerge which in essence merges new properties (if new one’s exist) onto an existing entity if the entity already exists.

Retrieving entities from table storage

Retrieving an entity by it’s ParitionKey/RowKey is accomplished using the TableOperation Retrieve.

var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
      "StorageConnectionString"));

var client = storageAccount.CreateCloudTableClient();
var table = client.GetTableReference("plants");

var entity = (Plant)table.Execute(
   TableOperation.Retrieve<Plant>(
      "Flower", "Rose")).Result;

Console.WriteLine(entity.Comment);

Deleting an entity from table storage

Deleting an entity is a two stage process, first we need to get the entity and then we can pass this to the Execute method with the TableOperation.Delete and the entity will be removed from the table storage.

Note: obviously I’ve not included error handling in this or other code snippets. Particularly here where a valid entity may not be found.

var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
      "StorageConnectionString"));

var client = storageAccount.CreateCloudTableClient();
var table = client.GetTableReference("plants");

var entity = (Plant)table.Execute(
   TableOperation.Retrieve<Plant>(
      "Flower", "Crocus")).Result;
table.Execute(TableOperation.Delete(entity));

Query Projections

In cases where, maybe our data has many properties (for example), we might prefer to query against our data and use projection capabilities to reduce those properties retrieved. To do this we use the TableQuery. For example let’s say all we’re after is the Comment from our entities, then we could write the following

var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
   "StorageConnectionString"));

var client = storageAccount.CreateCloudTableClient();
var table = client.GetTableReference("plants");

var projectionQuery = new TableQuery<DynamicTableEntity>()
   .Select(new [] { "Comment" });

EntityResolver<string> resolver = 
   (paritionKey, rowKey, timeStamp, properties, etag) => 
      properties.ContainsKey("Comment") ? 
      properties["Comment"].StringValue : 
      null;

foreach (var comment in table.ExecuteQuery(projectionQuery, resolver))
{
   Console.WriteLine(comment);
}

The TableQuery line is where we create the projection, i.e. what properties we want to retrieve. In this case we’re only interested in the “Comment” property. But we could add other properties (excluding the standard ParitionKey, RowKey, and Timestamp properties as these will be retrieved anyway).

The next line is the resolver which is passed to ExecuteQuery along with the projectionQuery. This is basically a predicate which acts as the “custom deserialization logic”. See Windows Azure Storage Client Library 2.0 Tables Deep Dive. Whilst an old article, it’s still very relevant. Ofcourse, the example above, shows using an anonymous delegate, in situations where we’re doing a lot of these sorts of projection queries we’d just create a method for this and pass that into ExecuteQuery as the resolver.

Querying using LINQ

Whilst LINQ is supported for querying table storage data, at the time of writing, it’s a little limited or requires you to write your queries in a specific way.

Let’s first look at a valid LINQ query against our plant table

var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
   "StorageConnectionString"));

var client = storageAccount.CreateCloudTableClient();
var table = client.GetTableReference("plants");

var query = from entity in table.CreateQuery<Plant>()
   where entity.Comment == "Thorns along the stem"
   select entity;

foreach (var r in query)
{
   Console.WriteLine(r.RowKey);
}

In this example we’ll query the table storage for any plant’s with a Comment “Thorns along the stem”, but now if we were to try to query for a Comment which contains the word “Thorns”, like this

var query = from entity in table.CreateQuery<Plant>()
   where entity.Comment.Contains("Thorns along the stem")
   select entity;

Sadly we’ll get a (501) Not Implemented back from the table storage service. So there’s obviously a limit to how we query our table storage data, which is fair enough. Obviously if we want more complex query capabilities we’d probably be best served using a different data store.

We can also use projections on our query, i.e.

var query = from entity in table.CreateQuery<Plant>()
   where entity.Comment == "Thorns along the stem"
   select entity.RowKey;

or using anonymous types, such as

var query = from entity in table.CreateQuery<Plant>()
   where entity.Comment == "Thorns along the stem"
   select new
   {
      entity.RowKey,
      entity.Comment
   };

Using Azure Queues

Azure Queue sits inside your Azure storage and allows messages to flow through a queue system (similar in some ways, but not as fully featured as MSMQ, TIBCO etc.).

Creating a Queue

Obviously we can create a queue using the Azure Portal or Azure Storage Explorer, but let’s create queue via code, using the following

var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
      "StorageConnectionString"));

var client = storageAccount.CreateCloudQueueClient();
var queue = client.GetQueueReference("my-queue");
queue.CreateIfNotExists();

Sending a message to our Queue

Adding a message to the queue is as simple as, the following

var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
      "StorageConnectionString"));

var client = storageAccount.CreateCloudQueueClient();
var queue = client.GetQueueReference("my-queue");

var message = new CloudQueueMessage("Hello World");
queue.AddMessage(message);

Peeking at a Queue

We can peek at a message on the queue, which basically means we can look at the message without affect it, using

var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
      "StorageConnectionString"));

var client = storageAccount.CreateCloudQueueClient();
var queue = client.GetQueueReference("my-queue");

var message = queue.PeekMessage();
Console.WriteLine(message.AsString);

As the name suggests, in this instance we’re peeking at the current or at least, most recent message. But we can also use the PeekMessages method to enumerate over a number of messages on the queue.

Here’s an example of peeking at 32 messages (it appears 32 is the maximum number of messages we’re allowed to peek, currently anything above this causes a bad request exception)

foreach (var message in queue.PeekMessages(32))
{
   Console.WriteLine(message.AsString);
}

Getting messages

Unlike, for example TIBCO RV, Azure Queue’s do not have subscribers and therefore do not push messages to subscribers (like and event might). Once a message is de-queued it will be marked as invisible (see CloudQueue.GetMessage Method.

To de-queue a message we use GetMessage on a Queue. As one might expect, once the message is marked as invisible, subsequent calls to GetMessage will not return the hidden message until the visibility timeout is reached and the message will then reappear and be available again from subsequent GetMessage calls.

var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
      "StorageConnectionString"));

var client = storageAccount.CreateCloudQueueClient();
var queue = client.GetQueueReference("my-queue");

var message = queue.GetMessage();

Console.WriteLine(message.AsString);

Now if you change the var message = queue.GetMessage(); line to the following

var message = queue.GetMessage(TimeSpan.FromSeconds(10));

and then within the Azure Portal or Azure Storage explorer refresh immediately after it’s de-queued the message will disappear but then refreshing again after 10 seconds and the message will reappear in the queue with it’s dequeue count incremented.

Like PeekMessages, we can call GetMessages to get a batch of messages (between 1 and 32 messages).

Deleting a message

To remove a message altogether use

queue.DeleteMessage(message);

This would usually be called after GetMessage is called, but obviously this is dependent upon your requirements. It might be called after a certain dequeuer count or simply after every GetMessage call, but remember if you do not delete the message it will reappear on your queue until an it’s maximum time to live ends, as supplied via the AddMessage method.

Using Azure File Storage

File storage is pretty much what is says in the tin. It’s a shared access file system using the SMB protocol. You can create directories, subdirectories and store files in those directories – yes, it’s a file system.

Reading a file

Using the Azure Portal either locate or create a storage account, within this create a File Storage, then create a share. Within the share upload a file, mine’s the Hello World.txt file with those immortal words Hello World within it.

Let’s read this file from our client. In many ways the client API is very similar to that used for Blob storage (as one might expect).

var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
      "StorageConnectionString"));

var fileClient = storageAccount.CreateCloudFileClient();

var share = fileClient.GetShareReference("myfiles");
var root = share.GetRootDirectoryReference();
var file = root.GetFileReference("Hello World.txt");

var contents = file.DownloadText();

Uploading a file

We can upload a file using the UploadFromFile. In this example we’ll just upload to the root folder of the myfiles share

var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
      "StorageConnectionString"));

var fileClient = storageAccount.CreateCloudFileClient();

var share = fileClient.GetShareReference("myfiles");
var root = share.GetRootDirectoryReference();
var file = root.GetFileReference("Hello World.txt");
file.UploadFromFile("Hello World.txt");

Deleting a file

Deleting files is as simple as, the following

var storageAccount = CloudStorageAccount.Parse(
   CloudConfigurationManager.GetSetting(
      "StorageConnectionString"));

var fileClient = storageAccount.CreateCloudFileClient();

var share = fileClient.GetShareReference("myfiles");
var root = share.GetRootDirectoryReference();
var file = root.GetFileReference("Hello World.txt");
file.Delete();

References

Introduction to Azure File storage
File Service REST API

Serilog revisited (now version 2.5)

About a year ago, I wrote the post Structured logging with Serilog which covered the basics of using Serilog 2.0. I’ve just revisited this post and found Serilog 2.5 has changed things a little.

I’m not going to go over what Serilog does etc. but instead just list the same code from my original post, but working with the latest NuGet packages.

So install the following packages into your application using NuGet

  • Serilog
  • Serilog.Sinks.RollingFile
  • Serilog.Sinks.Console

Serilog.Sinks.Console is used instead of Serilog.Sinks.Literate now, which has been deprecated.

Here’s the code. The only real change is around the JsonFormatter

Log.Logger = new LoggerConfiguration()
   .WriteTo.Console()
   .WriteTo.RollingFile(
      new JsonFormatter(renderMessage: true), 
      "logs\\sample-{Date}.txt")
   .MinimumLevel.Verbose()
   .CreateLogger();

Log.Logger.Information("Application Started");

for (var i = 0; i < 10; i++)
{
   Log.Logger.Information("Iteration {I}", i);
}

Log.Logger.Information("Exiting Application");

Now, let’s add something new to this post, add the following NuGet package

  • Serilog.Settings.AppSettings

In my original post I pointed out that Serilog seemed to be aimed towards configuration through code, but the Serilog.Settings.AppSettings package allows us to use the App.config for our configuration.

Change you Log.Logger code to the following

Log.Logger = new LoggerConfiguration()
   .ReadFrom.AppSettings()
   .CreateLogger();

and now in your App.config, within the configuration section, put the following

<appSettings>
   <add key="serilog:minimum-level" 
        value="Verbose" />
   <add key="serilog:using:RollingFile" 
        value="Serilog.Sinks.RollingFile" />
   <add key="serilog:write-to:RollingFile.pathFormat"
        value="logs\\sample-{Date}.txt" />
   <add key="serilog:write-to:RollingFile.formatter" 
        value="Serilog.Formatting.Json.JsonFormatter" />
   <add key="serilog:using:Console" 
        value="Serilog.Sinks.Console" />
   <add key="serilog:write-to:Console" />
</appSettings>

This recreates our original code by outputting to both a rolling file and console output, bad sadly does not allow us to set the renderMessage parameter of the JsonFormatter.

Note: At the time of writing it appears there’s no way to set this renderMessage, see How to set formatProvider property in Serilog from app.config file.