The issue when creating projects using frameworks and templates is that it often guides developers into thinking that the only way too can implement solutions is by following the guidelines provided by the project template.
The truth is when it comes to the Serverless Framework the only thing that is of any real importance is the YAML file, which is primarily used to create Cloud Formation script to create and manage the AWS resources to be used by your Lambda and the deployment script. When it comes to the actual code and structure of your lambda project and the code it contains, it really doesn’t care.
When generating a new AWS Lambda project, the Serverless Framework generates only the simplest project possible. A project which has hardly any features that most .net developers are typically used too and it falls on to them to implement all the features they need.
In this post, I will walk you through how to implement full-blown Dependency Injection, Configuration and even make use of IHost functionality in your AWS Lambda projects. The only restriction you’ll face in AWS Lambda Projects is that at the time of writing you are limited to using .net core 2.1
If you haven’t used or unfamiliar with the Serverless Framework then I suggest you read Getting started with .NET Core and the Serverless Framework.it will guide you through installing, provide a basic understanding of Serverless Framework and generate a typical project. I will assume you already have this knowledge for the remainder of this tutorial.
The reality is we could just delete the .net project created and start again, because the only things of use for us in the generated project is the serverless.yml , build.sh but we’ll work with what we’ve got!
We’re going to do a fit of refactoring here to tidy up the generated project to better suit our conventions etc. In a future post, I will provide details on how you can create your own custom Project Templates with the Serverless Framework so you don’t have to do this Boilerplate grunt work on every project you start.
In the first instance, we will want to rename our assembly to more closely match its purpose. When you’re engaging in Micro-services Development and developing many Lambdas you don’t really want 100’s of lambdas all named aws-csharp and a default namespace of AwsDotnetCsharp, it really doesn’t describe the component you’re developing and is counter-intuitive to the Philosophy of Software Design
The process to renaming your Assembly and setting the default namespace is as normal in whichever IDE or Text Editor you’re using. I predominantly use Jet Brains Rider – A fast & powerful cross-platform .NET IDE , so my process will be slightly different to most. However, there are few important edits to make to your serverless.yml to ensure you don’t implement breaking changes.
You’ll notice I simply edit line 12 to represent the fact I have renamed my Assembly to HelloConfiguration and my Namespace for my project is now Threenine.ConfigTest. For the purpose of this Demo code, this will suffice, I just wanted to highlight that you can edit these project files to better suit your needs.
Add a StartUp class
In the sample code for Simple Dependency Injection In AWS Lambda we followed some pretty bad coding practices and created one class that did everything, which is not SOLID and could cause all sorts of maintenance nightmares so we do some refactoring on that class and split core functional components into their own classes. I always have the principles of Adaptive Code in mind when developing!
So lets add a new class and by convention we’ll call this StartUp.cs
We are going to slightly refactor our ILambdaConfiguration and LambdaConfiguration to make use of IConfigurationRoot which represents the root of an IConfiguration hierarchy
Our new startUp class is going to be the location where we wire up our dependency and configuration data that our application is going to use.
So before we actually do that, we’ll have to create our services and do some more refactoring of our application. In my example I deleted the Handler.cs file created a new directory named Functions and created a new class in that directory named Speak.cs
For the sake of demo, I created an additional Directory named Services and created an interface of ISpeakService and a class called SpeakService
You’ll notice that the SpeakService with make use of the Options patternprovide a mechanism to validate configuration data . We also seem to using a class called Greeting, let's go ahead and create that class
We can now wire up our dependencies in our StartUp class
publicclassStartUp{publicstaticIServiceCollectionContainer=>ConfigureServices(LambdaConfiguration.Configuration);privatestaticIServiceCollectionConfigureServices(IConfigurationRootroot){varservices=newServiceCollection();//Wire up all your dependencies hereservices.Configure<Greeting>(options=>root.GetSection("greeting").Bind(options));services.AddTransient<ISpeakService,SpeakService>();returnservices;}}
In our Speak.cs class we’ll now make use of all our dependencies. You’ll notice that we will create 2 class constructors one of which will take in an IServiceProvider and the other will instantiate this class by initialising out StartUp class.
Our greet method will then Get a service we from our IOC container and use it. We make use of the ServiceProvider.GetService method to get the service object of the specified type.
We will also modify our AppSettings file slightly to create a Greeting section
{"greeting":{"message":"This is from the configuration"}}
Build, Deploy and Invoke
We can now build our lambda
sudo ./build.sh
We can then deploy our Lambda
sls deploy
Then we can invoke it to see if everything works as expected
sls invoke -f hello
Summary
In this sample, we have enabled our lambda to make use of some great .net core features of Dependency Injection and Configuration. This will enable developers to de-clutter their lambda functions and help to organise their code and also enable the use of additional libraries.