Alexa.Tip – Build Intent Handlers in .NET

In this Alexa.Tip series, we explore some little bits of code that can make your life easier in developing Alexa Skills in many languages including C# + .NET, node.jS + TypeScript, Kotlin, etc. We look at concepts that developers might not be aware of, design patterns that and how they can be applied to voice application development, best practices, and more!

In this post, we explore some more best practices in developing Alexa Skills in C# whether you are using an ASP.NET Core API or an AWS Lambda. This time, we talk about abstracting our business logic out of either our Function or Controller and into separate Handler classes.

If you’ve done skill development in node.js or in .NET, you’ve probably noticed that a lot of docs, example apps, and real world apps are written with one BIG class or file. That’s pretty gross.

You may some C# examples look like this:

UglyFunction.cs

public class Function
{
    public async Task<SkillResponse> HandleRequest(SkillRequest input)
    {
        if(input.GetRequestType() == typeof(LaunchRequest))
        {
            return ResponseBuilder.Ask("Welcome to my skill written in one big function class with all my business logic in one place and no real testability! Ask me anything!", null);
        }

        if(input.GetRequestType() == typeof(IntentRequest))
        {
            var intentRequest = input.Request as IntentRequest;

            switch(intentRequest.Intent.Name)
            {
                case "MyIntent1":
                    // get slot values
                    // search database
                    // validate data
                    // build response 
                    // add card if it supports a screen
                    // return
                    break;
                case "Intent2":
                    // get slot values
                    // search third party api
                    // validate data
                    // build response 
                    // add card if it supports a screen
                    // return
                    break;
                case "Intent3":
                    // get slot values
                    // search database
                    // validate data
                    // build response 
                    // add card if it supports a screen
                    // return
                    break;
                case "Intent4":
                    // get slot values
                    // search third party api
                    // validate data
                    // build response 
                    // add card if it supports a screen
                    // return
                    break;
                case "Intent5":
                    // get slot values
                    // search database
                    // validate data
                    // build response 
                    // add card if it supports a screen
                    // return
                    break;

                // I think you get my point here...
            }
        }

        return ResponseBuilder.Tell("Something went wrong. Please try again later");
    }
}

So that’s pretty gross right? It’s untestable, difficult to update, and hard to read! But hey, we’re C# developers. So let’s build Alexa Skills like C# developers.

The proposal here is to abstract your intent logic into Handler classes. Then you can inject those either into your Function or your Controller if you’re using a RESTful API.

Here’s something that I hope can get you started:
– A generic IIntentHandler
– An inherited specific IIntentWhateverHandler
– An implementation of the IIntentWhateverHandler
– A testable and injectable version to be used in the function.

Let’s start.

IIntentHandler.cs

public interface IIntentHandler
{
    Task<SkillResponse> HandleIntent(IntentRequest input);
}

Now let’s get specific. In this simple case, this interface is empty but exists for registration – although you can add specific methods here as needed.

IMyIntentHandler.cs

public interface IMyIntentHandler : IIntentHandler
{
}

Now let’s create an example implementation of an intent handler that houses the business logic of the request.

MyIntentHandler.cs

public class MyIntentHandler : IMyIntentHandler
{
    private readonly MyDbContext _context;
    public MyIntentHandler(MyDbContext context)
    {
        // oh snap, we can inject db context
        _context = context;
    }

    public async Task<SkillResponse> HandleIntent(IntentRequest input)
    {
        var mySlot = input.Slots["MySlot"].Value; // assumes the slot is there from it being required in the interaction model
        var myMessage = await _context.Messages.FirstOrDefaultAsync(m => m.SomeValue == mySlot)

        if(myMessage == null)
        {
            return ResponseBuilder.Ask("I don't know that one. Try something else.");
        }

        return ResponseBuilder.Tell(myMessage.Content);
    }
}

Now let’s go about adding these handlers to our Function (the same thing can apply to our Controllers.

PrettyFunction.cs

public class Function
{
    private readonly IMyIntentHandler _myIntentHandler;

    private void Setup()
    {
        _myIntentHandler = new MyIntentHandler(...);
        // create the others - optionally implement a ServiceCollection
        // to handle proper dependency injection
    }
    public async Task<SkillResponse> HandleRequest(SkillRequest input)
    {
        if(input.GetRequestType() == typeof(LaunchRequest))
        {
            return ResponseBuilder.Ask("Welcome to my skill written in one big function class with all my business logic in one place and no real testability! Ask me anything!", null);
        }

        if(input.GetRequestType() == typeof(IntentRequest))
        {
            var intentRequest = input.Request as IntentRequest;

            switch(intentRequest.Intent.Name)
            {
                case "MyIntent1":
                    return await _myIntentHandler.HandleIntent(intentRequest);
                    break;
            }
        }

        return ResponseBuilder.Tell("Something went wrong. Please try again later");
    }
}

So now you can separately test your Handler classes, your Data classes, and your Function as a whole.

SO much better 🙂

Conclusion

We don’t have to follow how Amazon writes their node.js skills when we write them in C# – let’s use some proper OO design and testability to build some scalable and awesome skills!

In the next post, we’ll talk about taking this one step closer to avoid that gross switch statement and simply register our Handler implementations for certain intents or RequestTypes which is more similar to how the actual ASK SDK works.

Stay tuned!


If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile and AI developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.


voicify_logo
I’m the Director and Principal Architect over at Voicify. Learn how you can use the Voice Experience Platform to bring your brand into the world of voice on Alexa, Google Assistant, Cortana, chat bots, and more: https://voicify.com/


Advertisements

Alexa.Tip – Using Entity Framework in Your C# Alexa Skill Lambda

In this Alexa.Tip series, we explore some little bits of code that can make your life easier in developing Alexa Skills in many languages including C# + .NET, node.jS + TypeScript, Kotlin, etc. We look at concepts that developers might not be aware of, design patterns that and how they can be applied to voice application development, best practices, and more!

In the previous Alexa.Tip, we explored Accessing Lambda Environment Variables in .NET in order to access a secure connection string and initialize a DbContext to use.

If you don’t know how to access Lambda Environment variables already, PLEASE read that post first as this post’s samples will NOT show the use of hard coded connection strings – let’s follow some best practices! 😀

Alright, let’s get into it. If you’re developing Alexa Skills in .NET, you probably need to access data somewhere! At least if you’re building a real-world scale voice application. Also, if you’re a .NET developer, you’re probably used to accessing data through Entity Framework at this point rather than using some of the AWS SDKs and data services such as DynamoDB.

It’s super easy…

Initial Skill

First off, this example uses a super basic skill where the only custom intent is to get facts about animals. Here’s the intent setup in the UI:
AnimalFactIntent_Full

You can also find the interactionModel.json here:
https://github.com/SuavePirate/Cross-Platform-Voice-NET/blob/master/src/End/AnimalFacts/AnimalFacts/InteractionModels/alexaInteractionModel.json

Let’s start by abstracting some handlers out so we aren’t writing everything in our Function class – this isn’t JavaScript after all:

WelcomeHandler.cs

public class WelcomeHandler : IResponseHandler
{
    public SkillResponse GetResponse(SkillRequest input)
    {
        return ResponseBuilder.Ask("Welcome to the animal facts voice app! You can ask me about all kinds of animals - try saying, tell me about dogs.");
    }
}

AnimalFactHandler.cs

public class AnimalFactHandler : IAsyncResponseHandler
{
    public async Task<SkillResponse> GetResponse(SkillRequest input)
    {
        // TODO: Do something with data to get the fact.
    }    
}

Then we can setup our Function:

Function.cs

public class Function
{
    // using these interfaces can help us use something like Moq to unit test this bad boy.
    private readonly IResponseHandler _welcomeHandler;
    private readonly IAsyncResponseHandler _animalFactHandler;

    public Function() 
    {
        _welcomeHandler = new WelcomeHandler();
        _animalFactHandler = new AnimalFactHandler();
    }  

    public async Task<SkillResponse> FunctionHandler(SkillRequest input, ILambdaContext context)
    {
        var requestType = input.GetRequestType();

        if (requestType == typeof(IntentRequest))
        {
            if ((input.Request as IntentRequest).Intent.Name == "AnimalFactIntent")
            { 
                return await _animalFactHandler.GetResponse(input);
            }
        }
        else if (requestType == typeof(Alexa.NET.Request.Type.LaunchRequest))
        {
            return _welcomeHandler.GetResponse(input);
        }    

        return ResponseBuilder.Ask("I'm not sure I know how to do that. Try asking me for an animal fact!");
    }
}

So we have a basic setup, but we need to actually implement something in the AnimalFactHandler. In this case, we want to do a SQL search for a fact about the animal that was asked about!

SO let’s start wiring up Entity Framework to this skill:

Add Models and Context

Let’s create a model for our AnimalFact entity to represent our table:

AnimalFact.cs

public class AnimalFact
{
    public int Id { get; set; }
    public string AnimalName { get; set; }
    public string Fact { get; set; }
}

And now let’s create a DbContext implementation with our AnimalFact:

AnimalContext

public class AnimalContext : DbContext
{
    public DbSet<AnimalFact> AnimalFacts { get; set; }
    public AnimalContext(DbContextOptions options)
        : base(options)
    {

    }

    public AnimalContext()
    {

    }
}

NOTE: If you are NOT also using this AnimalContext in a web app that handles the migrations, you’ll need to add either a web app or a console project with .NET Core to handle the migrations since it needs to be an executable project type. The assumption here is that you already have a project running these migrations so you can actually add data to it, but if you are starting here, simply create a new .NET Core web app project or Console app, reference your DbContext from a shared library between the Labmda project and the app project, then run your migrations from the app project.

From here on out, we assume you have the db created from your context and some data in it.

Updating the Skill Lambda

Now that we have a DbContext, let’s add it to our Function and AnimalFactHandler in order to search for the fact!

Let’s go to the AnimalFactHandler and setup the class to take the AnimalContext in the constructor so we can easily unit test and then use the slot value from the request to query for the animal:

AnimalFactHandler.cs

public class AnimalFactHandler : IAsyncResponseHandler
{
    private readonly AnimalContext _context;
    public AnimalFactHandler(AnimalContext context)
    {
        _context = context;
    }

    public async Task<SkillResponse> GetResponse(SkillRequest input)
    {
        // get the animal name from the slot
        var intent = (input.Request as IntentRequest).Intent;
        var animal = intent.Slots["Animal"].Value;

        // query for the fact
        var animalFact = _context.AnimalFacts
                            .Where(a => a.AnimalName.ToLower() == animal.ToLower())
                            .FirstOrDefaultAsync();

        // we found the animal!
        if (animalFact != null)
        {
            return ResponseBuilder.Tell(animalFact.Fact);
        }

        // couldn't find the animal - return a decent response
        return ResponseBuilder.Ask("I don't know about that animal. Try asking me about a different one!");
    }    
}

Now that we have the updated handler, let’s update the entry point to initialize and pass in the AnimalContext.

Function.cs

public class Function
{
    // using these interfaces can help us use something like Moq to unit test this bad boy.
    private readonly IResponseHandler _welcomeHandler;
    private readonly IResponseHandler _animalFactHandler;
    private readonly AnimalContext _context;

    public Function() 
    {
        var connectionString = Environment.GetEnvironmentVariable("DatabaseConnectionString");
        var timeoutSetting = Environment.GetEnvironmentVariable("DatabaseCommandTimeout");
        var optionsBuilder = new DbOptionsBuilder<AnimalFactContext>()
            .UseSqlServer(connectionString, providerOptions => providerOptions.CommandTimeout(int.Parse(timeoutSetting)));
        _context = new AnimalFactContext(optionsBuilder.Build());
        _welcomeHandler = new WelcomeHandler();
        _animalFactHandler = new AnimalFactHandler(_context);
    }  

    public SkillResponse FunctionHandler(SkillRequest input, ILambdaContext context)
    {
        var requestType = input.GetRequestType();

        if (requestType == typeof(IntentRequest))
        {
            if ((input.Request as IntentRequest).Intent.Name == "AnimalFactIntent")
            { 
                return _animalFactHandler.GetResponse(input);
            }
        }
        else if (requestType == typeof(Alexa.NET.Request.Type.LaunchRequest))
        {
            return _welcomeHandler.GetResponse(input);
        }    

        return ResponseBuilder.Ask("I'm not sure I know how to do that. Try asking me for an animal fact!");
    }
}

Now we can publish this and check out our results!

Results

For example, I’ll use Swagger to create an animal fact, and then ask for it!

Boom – created:
create_dog_fact.PNG

And now let’s ask for it!

dog_fact_response

Conclusion

Adding data-driven responses to your custom Alexa Skills is dead easy using techniques you may already be familiar with if you are a .NET web developer! Show me what you’ve built with .NET and Alexa in the comments!


If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile and AI developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.


voicify_logo
I’m the Director and Principal Architect over at Voicify. Learn how you can use the Voice Experience Platform to bring your brand into the world of voice on Alexa, Google Assistant, Cortana, chat bots, and more: https://voicify.com/


Alexa.Tip – Access Lambda Environment Variables in .NET

In this Alexa.Tip series, we explore some little bits of code that can make your life easier in developing Alexa Skills in many languages including C# + .NET, node.jS + TypeScript, Kotlin, etc. We look at concepts that developers might not be aware of, design patterns that and how they can be applied to voice application development, best practices, and more!

In this post, we’ll look at a simple tip to help secure your Alexa Skills when using an AWS Lambda. In a future post, we’ll look at a similar concept in .NET for developers using ASP.NET Core Web API instead of Lambdas.

So, you’ve taken the step to building proper data-driven Alexa Skills and have stepped out of the simple “todo” examples. Perhaps you need a database connection in your source code, need to change values depending on what environment you are running the skill in, or want to run your skill with options you can change without having to redeploy the codebase over and over again.

For all of these problems, your best bet is to use the Lambda Environment variables. Doing it is INCREDIBLY simple, yet I still see many developers unaware of how to use them and instead use runtime checks or don’t bother scaling their application at all to needing them.

In this example, we’ll look at how to create an instance of an Entity Framework DbContext and set it up for a testable injection based project.

Let’s look at a code snipet example of a piece of an Alexa Skill running in a .NET Lambda using a hard coded connection string:

Message.cs

// a simple table to represent some messages we can grab
public class Message
{
    public int Id { get; set; }
    public string Content { get; set; }
}

And here’s our DbContext we are going to grab data from:
MessageDbContext.cs

public class MessageDbContext : DbContext 
{
    public DbSet<Message> Messages { get; set; }

    public MessageDbContext(DbContextOptions options) : base(options) { }
}

Now check out a lambda I typically see with hard coded connection strings and settings:

BadLambda.cs

public class BadLambda
{
    // an EF DbContext with some message tables
    private readonly MessageDbContext _context;

    public BadLambda()
    {
        var optionsBuilder = new DbOptionsBuilder<MessageDbContext>()
            .UseSqlServer("my_connection_string", providerOptions => providerOptions.CommandTimeout(60));
        _context = new MessageDbContext(optionsBuilder.options);
    }

    public async Task<SkillResponse>(SkillRequest input)
    {
        var message = await _context.Messages.FirstOrDefaultAsync();
        return ResponseBuilder.Tell(message.Content);
    }
}

So breaking down the code… We have an EF DbContext with one table called “Messages” which has a column of Id and Content, and our Skill Lambda sets up the DbContext with a hard coded connection string and timeout setting, then returns the first messages as a simple response.

Let’s take this lambda and add environment variables so we can run mutliple environments for multiple dbs including a localhost db if we wanted to test locally before deploying to Lambda and so we can test updates before we make changes to our production skill!

In AWS

Head over to your lambda in AWS and scroll down to “Environment Variables”:
Labmda_env

Here you can add any key-value pairs you want. For this demo, we want to put our database connection string and our timeout setting here:

Lambda_env_filled

Now make sure you smash that save button and head back to Visual Studio to make our final updates!

Don’t forget you can now also create another lambda for a development or staging environment separate from your production lambda – be sure to use a different database connection in your environment variables, and then you can publish to both 😀

In the Code

Now we just have to use the

Environment.GetEnvironmentVariable(string key);

method to grab the values of these environment variables we set up and we’re off to the races!

Here’s what that looks like when updates:

GoodLambda.cs

public class GoodLambda
{
    // an EF DbContext with some message tables
    private readonly MessageDbContext _context;

    public GoodLambda()
    {
        var connectionString = Environment.GetEnvironmentVariable("DatabaseConnectionString");
        var timeoutSetting = Environment.GetEnvironmentVariable("DatabaseCommandTimeout");
        var optionsBuilder = new DbOptionsBuilder<MessageDbContext>()
            .UseSqlServer(connectionString, providerOptions => providerOptions.CommandTimeout(int.Parse(timeoutSetting)));
        _context = new MessageDbContext(optionsBuilder.options);
    }

    public async Task<SkillResponse>(SkillRequest input)
    {
        var message = await _context.Messages.FirstOrDefaultAsync();
        return ResponseBuilder.Tell(message.Content);
    }
}

Now publish it to your lambda and you’re done!

Recap

  • For the love of god, stop hard coding connection strings and settings in your lambda
  • Use environment variables
  • Setup multiple environments for scalability and testability
  • Abuse Environment.GetEnvironmentVariable()
  • Build more Alexa Skill 🙂

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile and AI developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.


voicify_logo
I’m the Director and Principal Architect over at Voicify. Learn how you can use the Voice Experience Platform to bring your brand into the world of voice on Alexa, Google Assistant, Cortana, chat bots, and more: https://voicify.com/