Async entrypoint for C# .NET program

The entrypoint of our C# program is by default a static void Main function, it should look something like that:

internal class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");
    }
}

Modern day C# applications tend to use async…await, which requires to assign async to the function like following:

static async void Main(string[] args)

This would cause an error when running the application:

Important: We should avoid async void and only use it if we really know what we are doing.

The solution is quite straightforward, update void to Task:

internal class Program
{
    static async Task Main(string[] args)
    {
        Console.WriteLine("Hello World!");
    }
}

Happy coding!

AWS S3 read and write operations using C#

Goal of this exercise is to show how to read and write files on AWS S3 from a .NET console application.

Tool/prerequisites we are going to use: Visual Studio, .NET 6, AWSSDK.S3 as NuGet dependency.

GitRepo is available here.

AWS S3 is a versatile product, used for many different purposes from logs to big data storages. It’s basic operations are easy to implement using the AWS SDK.

For the first step we need to create an S3 bucket which will hold our files. Go to AWS console, search for S3 product in the search bar:

Search for S3 in the AWS search bar

Click on Create bucket button:

Create bucket button

Choose a name and region, then hit Create bucket at the bottom of the page:

Creating the bucket

Now we are all set on the AWS console, we are going to use it later on for testing.

We can get back to our code editor and add AWSSDK.S3 to our project, which has bunch of S3 related codes to help us out. Let’s write some code:

using Amazon.S3;
using Amazon.S3.Model;
using System.Text;

namespace BlogDavid_S3ReadWrite
{
    internal class Program
    {
        // Note: Running the program will use the local machine's credentials!

        // Store the bucket's name somewhere
        const string s3BucketName = "blogdavid-s3readwrite";
        // Set the client to our bucket's region
        static IAmazonS3 s3Client = new AmazonS3Client(Amazon.RegionEndpoint.EUWest1);

        static async Task Main(string[] args)
        {
            // Generating a random path for our text file
            string randomizedPath = $"{Guid.NewGuid()}/{Guid.NewGuid()}/{Guid.NewGuid()}.txt";

            // Adding some text to save
            string textToSave = $"I am a text generated at {DateTime.UtcNow}";

            // Converting the text into bytes
            byte[] textAsBytes = Encoding.UTF8.GetBytes(textToSave);

            using (MemoryStream ms = new MemoryStream(textAsBytes))
            {
                // Rewinding the stream
                ms.Position = 0;

                PutObjectRequest putRequest = new PutObjectRequest
                {
                    // Name of the bucket
                    BucketName = s3BucketName,
                    // Key as the path under the bucket
                    Key = randomizedPath,
                    // Data we are saving
                    InputStream = ms,
                };

                await s3Client.PutObjectAsync(putRequest);
            }

            // Let's read back the previous file

            // Construction the request object
            GetObjectRequest getRequest = new GetObjectRequest
            {
                BucketName = s3BucketName,
                Key = randomizedPath
            };

            // Sening the request
            using (var getResponse = await s3Client.GetObjectAsync(getRequest))
            using (var sr = new StreamReader(getResponse.ResponseStream))
            {
                // Reading back the response
                var getResponseString = await sr.ReadToEndAsync();
                // Showing the response, which is hopefully the text we just saved
                Console.WriteLine(getResponseString);
            }

            Console.ReadKey();
        }
    }
}

Starting our app, it should write and read a file on S3:

Happy coding!

Calling another process from C# console app

Goal of this post is to show how to call an exe from another program.

Tool/prerequisites we are going to use: Visual Studio, .NET 6.

GitRepo is available here.

Calling another process and capturing it’s output is really straightforward in .NET. We just need to create a new ProcessStartInfo with the needed parameters and pass it to the Process.Start method. That is all.

Here we have a solution with two projects. The first project is receiving two parameters and writes out a number in a loop, then the second project is depending on the first one, calls it and captures it’s output.

“Counter” project’s code:

public class Program
{
    public static void Main(string[] args)
    {
        int start = Convert.ToInt32(args[0]);
        int end = Convert.ToInt32(args[1]);

        for (int current = start; current < end; current++)
        {
            Console.WriteLine(current);
        }
    }
}

Note: there is no safeguarding on the parameters, as it is just an example project.

“Caller” project’s code:

using System.Diagnostics;


var proc = Process.Start(new ProcessStartInfo
{
    // Arguments to pass to another process, separated by a space
    Arguments = "10 20",
    // Name of the exe file. Note: it will be copied by the builder (Visual Studio) to the projects bin folder, as it is dependent on the project "BlodDavid_CounterProject"
    FileName = "BlodDavid_CounterProject.exe",
    // To redirect the output stream, we need to set it to false
    UseShellExecute = false,
    // We want to capture the data written to the output of the other process
    RedirectStandardOutput = true,
    // We don't want to show the users another windows, just the output in our main app
    CreateNoWindow = true
});

// Starting the another process
proc.Start();

// Listening to the output stream
while (!proc.StandardOutput.EndOfStream)
{
    Console.WriteLine($"Message from the other process: {proc.StandardOutput.ReadLine()}");
}
// Don't let our program to exit until the other process finishes
proc.WaitForExit();

// Notifying our user that the other project ended
Console.WriteLine("To exit press enter!");
Console.ReadLine();

Happy coding!

Serve static website using ASP .NET MVC

Goal of this post is to show how to serve static content using ASP .NET MVC.

Tool/prerequisites we are going to use: Visual Studio, .NET 6.

GitRepo is available here.

ASP .NET MVC is mainly used to serve dynamically generated content, but it is perfectly capable to send html files to the client. To achieve this we only need to turn on two functions in our Startup.cs, Configure method:

// Let Asp Mvc default '/' to index.html
app.UseDefaultFiles();
// Tell Asp Mvc to serve files from under wwwroot folder
app.UseStaticFiles();

That is all. From now on, the content in wwwroot will be served.

Note: Files path will be without the wwwroot! Eg.:

Project’s wwwroot file structure

The files above will be available on the following path:

index.html -> domain.com or domain.com/index.html

sub_content.html -> domain.com/path/sub_content.html

Re-apply gitignore on already pushed files

Imagine the scenario you pushed your whole project to git and you realise there is no .gitignore, all the miscellaneous, unnecessary files are already in your repo. You quickly add the .gitignore file, but nothing happens!

This is because you first need to clear your cache:

git rm -r --cached .

Now you can re-add (better to say clean) you files with the following command:

git add .

You should see many files to be removed from you instance. Now you can do your commit-push as usual.

Happy coding!

Basic testing in Python using Pytest

Goal of this post is to write a short test using Pytest.

Tool/prerequisites we are going to use: Visual Studio Code (or any file editor), python 3+.

GitRepo is available here.

Testing is and will be essential when coding. To make our life easier, developers created many frameworks and helpers to quickly achieve high code coverage. One well know library for Python is Pytest, which we are going to use in this article.

Start a new project/folder using your favorite editor and create a requirements file for PIP, we are going to use test_requirements.txt. Add our only dependency:

pytest

Install our dependency with the following command (running from the folder where the test_requirements.txt is):

pip install -r test_requirements.txt

Now we can add our code file with the name basic_calculator.py:

def sum(numberOne: int, numberTwo: int):
    return numberOne + numberTwo

It doesn’t do anything fancy, just sums two number.

Now comes the test. Add a new file called test_basic_calculator.py:

import basic_calculator

class TestHandler:
    def test_sum_one(self):
        sum = basic_calculator.sum(2,3)
        assert sum == 5

    def test_sum_two(self):
        sum = basic_calculator.sum(66,55)
        assert sum == 121

We are done with coding, let’s run our test (give the following command from folder):

python -m pytest

The result should be something similar to this:

What happened here? We asked python to run the module called pytest in the working folder. Pytest then searches for filenames which start with “test_” and looks for classes starting with “Test” containing function names starting with “test”.

Narrowing down the test scope

We might want to run test only on one file, to do so we can specify it by name:

python -m pytest ./test_basic_calculator.py

There are further options as well

Run only one class in a file:

python -m pytest ./test_basic_calculator.py -k "Test_Handler"

or

python -m pytest ./test_basic_calculator.py::Test_Handler

If you only want to run one test in a class:

python -m pytest ./test_basic_calculator.py -k "Test_Handler and test_sum_one"

or

python -m pytest ./test_basic_calculator.py::Test_Handler::test_sum_one

Happy coding!

Work with SQLite using C#

Goal of this post is to show some of the capabilities of SQLite with C#.

Tool/prerequisites we are going to use: Visual Studio, .NET 6, Microsoft.Data.Sqlite NuGet package.

GitRepo is available here.

SQLite is a powerful tool to handle data locally. It’s file based solution helps us to quickly test our application with real world data, but without setting up a full SQL server. We are going to create a console application with some basic functionality.

After the project is created, first we need to install our prerequisite called Microsoft.Data.Sqlite using NuGet package manager. From ‘Solution Explorer’ open ‘Dependencies’, right click on ‘Packages’ and choose ‘Manage NuGet Packages…’

Choose the ‘Browse’ tab and copy in the name: Microsoft.Data.Sqlite, select it and click ‘Install’ on the right panel.

New we have all the necessary packages and we can start coding. Reference the just added library at the top of the Program.cs:

using Microsoft.Data.Sqlite;

To use the same connection string, we will have a general provider method:

        private static string CreateSqliteConnectionString(string dbName)
        {
            SqliteConnectionStringBuilder builder = new SqliteConnectionStringBuilder();
            builder.Add("Data Source", dbName);
            return builder.ConnectionString;
        }

Initialize the DB with a ‘person’ table:

        private static void CreatePersonTable(string connectionString)
        {
            using (SqliteConnection connection = new SqliteConnection(connectionString))
            using(SqliteCommand command = connection.CreateCommand())
            {
                connection.Open();
                command.CommandText = "CREATE TABLE person(id INTEGER PRIMARY KEY, full_name TEXT, age INT)";
                command.ExecuteNonQuery();
            }
        }

Now add some basic functionality methods for CRUD (create, read, update, delete):

 private static void InsertPerson(string connectionString, string fullName, int age)
        {
            using (SqliteConnection connection = new SqliteConnection(connectionString))
            using (SqliteCommand command = connection.CreateCommand())
            {
                connection.Open();
                command.CommandText = $"INSERT INTO person (full_name, age) VALUES ('{fullName}', {age})";
                command.ExecuteNonQuery();
                connection.Close();
            }
        }

        private static void DeletePerson(string connectionString, int id)
        {
            using (SqliteConnection connection = new SqliteConnection(connectionString))
            using (SqliteCommand command = connection.CreateCommand())
            {
                connection.Open();
                command.CommandText = $"DELETE FROM person WHERE id = {id}";
                command.ExecuteNonQuery();
                connection.Close();
            }
        }

        private static void UpdatePerson(string connectionString, int id, string fullName, int age)
        {
            using (SqliteConnection connection = new SqliteConnection(connectionString))
            using (SqliteCommand command = connection.CreateCommand())
            {
                connection.Open();
                command.CommandText = $"UPDATE person SET full_name = '{fullName}', age = {age} WHERE id = {id}";
                command.ExecuteNonQuery();
                connection.Close();
            }
        }

        private static void SelectPerson(string connectionString, int id)
        {
            using (SqliteConnection connection = new SqliteConnection(connectionString))
            using (SqliteCommand command = connection.CreateCommand())
            {
                connection.Open();
                command.CommandText = $"SELECT id, full_name, age FROM person WHERE id = {id}";
                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        Console.WriteLine($"Id: {reader.GetInt32(0)}, full_name: {reader.GetString(1)}, age: {reader.GetInt32(2)}");
                    }
                }
                connection.Close();
            }
        }

        private static void ListPersons(string connectionString)
        {
            using (SqliteConnection connection = new SqliteConnection(connectionString))
            using (SqliteCommand command = connection.CreateCommand())
            {
                connection.Open();
                command.CommandText = $"SELECT id, full_name, age FROM person ORDER BY id DESC";
                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        Console.WriteLine($"Id: {reader.GetInt32(0)}, full_name: {reader.GetString(1)}, age: {reader.GetInt32(2)}");
                    }
                }
                connection.Close();
            }
        }

If we run our code multiple times, the ‘CreatePersonTable’ can run into problem, that the DB already exists, lets delete it if exists. It is easy to do as it is only a file in our file system:

        private static void RemoveDBIfExists(string dbName)
        {
            FileInfo fi = new FileInfo(dbName);
            if (fi.Exists)
            {
                fi.Delete();
            }
        }

Now call our methods one-by-one from ‘Main’:

static void Main(string[] args)
        {
            Console.WriteLine("Initializing db");
            string dbName = "blog.db";
            RemoveDBIfExists(dbName);
            string connectionString = CreateSqliteConnectionString(dbName);
            CreatePersonTable(connectionString);
            Console.WriteLine("Db created with person table");

            Console.WriteLine("Adding initial data");
            InsertPerson(connectionString, "Peter", 35);
            InsertPerson(connectionString, "Anna", 35);
            InsertPerson(connectionString, "David", 38);
            InsertPerson(connectionString, "Lilla", 27);

            Console.WriteLine("Listing persons");
            ListPersons(connectionString);

            Console.WriteLine("Deleting a person");
            DeletePerson(connectionString, 2);

            Console.WriteLine("Listing persons");
            ListPersons(connectionString);

            Console.WriteLine("Selecting person id = 1");
            SelectPerson(connectionString, 1);

            Console.ReadLine();
        }

Running the whole code should output something similar to this:

Putting every piece together we should have the following Program.cs (or see on GitHub):


using Microsoft.Data.Sqlite;

namespace BlogDavid_BasicSqlLite
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Initializing db");
            string dbName = "blog.db";
            RemoveDBIfExists(dbName);
            string connectionString = CreateSqliteConnectionString(dbName);
            CreatePersonTable(connectionString);
            Console.WriteLine("Db created with person table");

            Console.WriteLine("Adding initial data");
            InsertPerson(connectionString, "Peter", 35);
            InsertPerson(connectionString, "Anna", 35);
            InsertPerson(connectionString, "David", 38);
            InsertPerson(connectionString, "Lilla", 27);

            Console.WriteLine("Listing persons");
            ListPersons(connectionString);

            Console.WriteLine("Deleting a person");
            DeletePerson(connectionString, 2);

            Console.WriteLine("Listing persons");
            ListPersons(connectionString);

            Console.WriteLine("Selecting person id = 1");
            SelectPerson(connectionString, 1);

            Console.ReadLine();
        }
        private static void RemoveDBIfExists(string dbName)
        {
            FileInfo fi = new FileInfo(dbName);
            if (fi.Exists)
            {
                fi.Delete();
            }
        }
        private static string CreateSqliteConnectionString(string dbName)
        {
            SqliteConnectionStringBuilder builder = new SqliteConnectionStringBuilder();
            builder.Add("Data Source", dbName);
            return builder.ConnectionString;
        }
        private static void CreatePersonTable(string connectionString)
        {
            using (SqliteConnection connection = new SqliteConnection(connectionString))
            using(SqliteCommand command = connection.CreateCommand())
            {
                connection.Open();
                command.CommandText = "CREATE TABLE person(id INTEGER PRIMARY KEY, full_name TEXT, age INT)";
                command.ExecuteNonQuery();
            }
        }

        private static void InsertPerson(string connectionString, string fullName, int age)
        {
            using (SqliteConnection connection = new SqliteConnection(connectionString))
            using (SqliteCommand command = connection.CreateCommand())
            {
                connection.Open();
                command.CommandText = $"INSERT INTO person (full_name, age) VALUES ('{fullName}', {age})";
                command.ExecuteNonQuery();
                connection.Close();
            }
        }

        private static void DeletePerson(string connectionString, int id)
        {
            using (SqliteConnection connection = new SqliteConnection(connectionString))
            using (SqliteCommand command = connection.CreateCommand())
            {
                connection.Open();
                command.CommandText = $"DELETE FROM person WHERE id = {id}";
                command.ExecuteNonQuery();
                connection.Close();
            }
        }

        private static void UpdatePerson(string connectionString, int id, string fullName, int age)
        {
            using (SqliteConnection connection = new SqliteConnection(connectionString))
            using (SqliteCommand command = connection.CreateCommand())
            {
                connection.Open();
                command.CommandText = $"UPDATE person SET full_name = '{fullName}', age = {age} WHERE id = {id}";
                command.ExecuteNonQuery();
                connection.Close();
            }
        }

        private static void SelectPerson(string connectionString, int id)
        {
            using (SqliteConnection connection = new SqliteConnection(connectionString))
            using (SqliteCommand command = connection.CreateCommand())
            {
                connection.Open();
                command.CommandText = $"SELECT id, full_name, age FROM person WHERE id = {id}";
                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        Console.WriteLine($"Id: {reader.GetInt32(0)}, full_name: {reader.GetString(1)}, age: {reader.GetInt32(2)}");
                    }
                }
                connection.Close();
            }
        }

        private static void ListPersons(string connectionString)
        {
            using (SqliteConnection connection = new SqliteConnection(connectionString))
            using (SqliteCommand command = connection.CreateCommand())
            {
                connection.Open();
                command.CommandText = $"SELECT id, full_name, age FROM person ORDER BY id DESC";
                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        Console.WriteLine($"Id: {reader.GetInt32(0)}, full_name: {reader.GetString(1)}, age: {reader.GetInt32(2)}");
                    }
                }
                connection.Close();
            }
        }
    }
}

Happy coding!

Setting up GitHub’s Personal Access Token (PAT)

When we are using multiple GitHub accounts on the same machine, they can be easily mixed up and cause errors. One solution for that is to incorporate Personal Access Token (PAT) for our repos.

First of all login to your GitHub account and select ‘Settings’ option from under your avatar:

From the left menu’s bottom side search for ‘Developer settings’:

Select ‘Personal access tokens’ option:

Click on the button ‘Generate new token’:

You might be forced to input your password and/or your token.

Input a note for the PAT, give an Expiration and select scopes:

For basic source code usage, ‘repo‘ category is enough.

From the bottom click on ‘Generate token’. The page will be redirected back to the list but on the top you will see the newly created token:

The token shown on the picture is not a real one 🙂

Now, when you are setting up your GitHub repo’s remote url use the following template:

https://GitHubUserName:PAT@github.com/RepoOwnerGithubUserName/RepoName.git

Example given:

https://Szerpentin:ghp_mynewlycreatedpat@github.com/Szerpentin/BlogDavid_PythonBasicTesting.git

SourceTree

If you are using SourceTree you can set it up under the repo’s ‘Settings’ option or clone the repo already with the PAT included url:

Happy coding!

One good reason to use custom deployment buckets

When deploying serverless Lambda it will create a bucket with the code and CloudFormation template. It is totally fine, but the newly created bucket will be associated with the stack and will prevent us from purging the whole stack.

When do we want to delete a stack?

During development there could be many reasons to completely remove one, few ideas:

  • our Lambda’s type changed, thus it cannot be just updated
  • we had to experiment with roles and want to test them after clean serverless installation
  • none of the resources are required anymore

According to the default AWS policy an S3 bucket cannot be deleted if it is not empty, which exactly is preventing us from purging the stack.

What can one do?

It is really straightforward: create a bucket for serverless deployment. If we manually add a new S3 bucket and point our serverless installer to that, it won’t be interfering with our stack.

When the bucket is there we can add these two lines under provider to our serverless to make it use that for deployment:

  deploymentBucket:
    name: serverless-dep-bucket-dev

Updating the utc-time-teller’s serverless it looks like this:

service: BlogDavidPythonLambdaDeploy

provider:
  name: aws
  region: eu-west-1
  deploymentBucket:
    name: serverless-dep-bucket-dev

functions:
  utc-time-teller:
    name: utc-time-teller
    handler: src/lambda_handler.handler
    memorySize: 128
    timeout: 30
    runtime: python3.8
    events:
      - http:
          path: /{proxy+}
          method: any
      - http:
          path: /
          method: any