Why Use .NET For Lambda?
Also, AWS supports the entire .NET runtime, which means you can use other languages besides C# that also compile to .NET binaries. C# is overwhelmingly the most popular, but you could also write Lambda Functions in F# or VB.NET.
How Does It Perform?
Languages like Java and C# are generally much nicer, but there is a downside to using them. They both are compiled to bytecode that must be compiled at startup, so they have higher startup times, especially when starting cold. “Cold starts” are when AWS hasn’t run the function in the last few minutes, so it won’t have it cached, and will need to perform the just-in-time compilation again to get it up and running. This process can cause your functions to take a second or more to respond, which isn’t good for web applications.
However, this problem is largely mitigated if you’re using Lambda very often. You can also reduce cold start times entirely with provisioned concurrency. The regular response times for .NET are very high, and the performance is on par with the fully compiled languages like Go and Rust.
If you’re currently using Java for Lambda functions, C# can be a viable replacement, as the modern .NET 6 runtime uses less memory and starts up quicker than the JVM in most cases.
Setting Up C# Lambda Functions
First, you will need .NET installed. AWS supports .NET Core 3.1 and .NET 6, so either of those two runtimes will work, but most importantly you will need the
dotnet CLI installed so that you can install the Lambda templates. You can get .NET from Microsoft’s documentation portal.
You’ll need to install the Lambda templates, and the global Lambda tools.
dotnet new -i Amazon.Lambda.Templates dotnet tool install -g Amazon.Lambda.Tools
There are a lot of options this installs; you can list them all with:
dotnet new --list
This tooling is quite nice, as it comes with many packaged templates preconfigured for different use cases. You will generally want one function per project to keep build sizes small, but you can have multiple functions in one DLL if you use AWS’s Serverless templates, which deploy using CloudFormation templates. These are a lot more complicated to manage, so only use them if you’re benefiting from it.
With .NET’s Solution files though, you can have multiple projects side-by-side referencing common assemblies, so this isn’t much of an issue.
For now, we’ll go with the simple Empty Function template, which generates a project using .NET 6. You can create this from the command line, or from your editor’s new project screen.
dotnet new lambda.EmptyFunction --name SimpleLambdaFunction --profile default --region us-east-1
This generates a very simple function—it takes a string as input, and is also passed an
ILambdaContext. This is the
Main() entry-point function for your Lambda and will be called by the runtime whenever the Lambda Function is invoked. This particular function returns a
string, but you can also make it
async and return a
At the top you’ll see an assembly attribute configuring a JSON Serializer. Internally, Lambda will handle deserializing the input content for you, and then will call your function. Afterwards, if it returns something, it will be written to the response stream. The Lambda libraries handle this boilerplate for you, and the code that wraps your function is in
Essentially, it will handle all kinds of method signatures, and if your function takes an input, it will deserialize that input for you. If your function returns an output, it will serialize that output for you. You actually don’t have to do any of this, as you can write functions that operate on raw
Stream classes, but this is a nice wrapper class to make things easier.
What this means is that you are free to define your own models for inputs and outputs passed to and from the function, one of the nice perks of handling JSON with C#.
In this function, it deserializes the
InputModel class, waits asynchronously for a second, and then returns an
OutputModel class. This class is serialized back into the output stream so Lambda can handle it.
Running Lambda Functions
Running the function once you’ve made it is quite simple, as the Lambda .NET CLI provides a method for deploying it. Simply run
dotnet lambda deploy-function SimpleNETFunction
You will need to select an IAM role or create a new one, and you may need to add permissions to this new role. You should now see the function in your console:
Lambda provides a built-in tester which you can pass JSON to.
This will execute and show you all the details about the execution. In this case, with a very small minimal function, the cold startup time was less than 500ms, which is pretty decent for .NET and for Lambda in general. Once it’s warm, the billed duration goes down to only a few milliseconds.
In this case, this function didn’t use much memory at all, and bumping the function down to 128MB caused no problems.