Working with JSON in .NET, Infrastructure as Code with Pulumi

Full source code available here.

This is a follow up to my previous post where I used dynamic and JSON files to make querying ElasticSearch with a HttpClient much easier.

To deploy my ElasticSearch domain on AWS I used Pulumi. ElasticSearch requires a JSON policy to define the permissions. In the post linked above, I have a heavily escaped that This policy can be complex and needs values substituted into it. In the example below I need to pass in the region, account id, domain name and allowed IP address.

Here is a very simple policy with four substitutions –

"{{
""Version"": ""2012-10-17"",
""Statement"": [
    {{
        ""Action"": ""es:*"",
        ""Principal"": {{
            ""AWS"": ""*""
        }},
        ""Effect"": ""Allow"",
        ""Resource"": ""arn:aws:es:{currentRegion.Name}:{currentCallerIdentity.AccountId}:domain/{esDomainName}/*"",
        ""Condition"": {{
            ""IpAddress"": {{""aws:SourceIp"": [""{myIPAddress}""]}}
        }}
    }}
]
}}"

Just escaping this is not easy, and very prone to error. A more realistic policy would be significantly longer and would need more substitutions.

Using a JSON file
Here is what I think is an easier way. As in the previous post, the JSON file becomes part of my source code. It is deserialized into a dynamic object and the required values are set.

Here is the AWS policy as it appears in my JSON file. The resource (made up of region, account, and domain name) and IpAddress are left blank, but the structure of the policy is the same as you would paste into the AWS console.

{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "AWS": "*"
        },
        "Action": "es:*",
        "Resource": "",
        "Condition": {
          "IpAddress": {
            "aws:SourceIp": ""
          }
        }
      }
    ]
}

In my C# I read the file, deserialize, and set the values with simple C#.

Here is an example –

private string GetAWSElasticSearchPolicy(string region, string account, string elasticSearchDomainName, string allowedIPAddress)
{
    string blankPolicy = File.ReadAllText("AWSPolicy.json");
    dynamic awsElasticSearchPolicy = JsonConvert.DeserializeObject(blankPolicy);

    awsElasticSearchPolicy.Statement[0].Resource = $"arn:aws:es:{region}:{account}:domain/{elasticSearchDomainName}/*";
    awsElasticSearchPolicy.Statement[0].Condition.IpAddress = new JObject(new JProperty("aws:SourceIp", allowedIPAddress));

    return awsElasticSearchPolicy.ToString(); // this is correctly formatted JSON that can be used with Pulumi.
}

Line 3 reads the JSON file into a string.
Line 4 turns the string into a dynamic object.
Lines 6 & 7 set the values I want.
Line 9 returns a nice JSON string that can be used with Pulumi.

This is much cleaner than the heavily escaped version in this post.

Full source code available here.

Working with JSON in .NET, a better way?

Full source code available here.

Two recent experiences with C# and JSON frustrated me with how difficult it is to work JSON inside an application. I have also been learning Node.js and contrasting the ease of use there with C# is, shocking. In C# the developer is generally expected to create class structures that represent the JSON they want to produce or consume and for most of my career that has been fine, I usually had to work on quite fixed JSON, with quite fixed classes.

An example might be JSON that represents customers, orders and order items. Easy enough to make C# classes that represent them, and it having classes means its is easy to work with the customer, order or order item inside your code.

But more recently I have been working with ElasticSearch and Pulumi.

In the case of ElasticSearch, querying it is done through HTTP requests with complex JSON that can change significantly between requests. The JSON can be many layers deep and combine searching across multiple fields, sorting, paging, specifying fields to return, and other functionality.

Here is a simple query, I built this using Visual Studio Rest Client. To use this inside a C# application I have to escape all the “, {, and } characters and I have do it such a way that allows me substitute in the values I want.

This is the raw JSON –

{
    "query": {
        "match_phrase_prefix": {
            "fullName" : "Joe"
        }
    },
    "from": 0,
    "size": 2
}

Escaping and getting it to work with a request from HttpClient took a while, and to my mind it looks awful –

string query = @"
                {{
                    ""query"": {{
                        ""match_phrase_prefix"": {{
                            ""fullName"" : ""{0}""
                        }}
                    }},
                    ""from"": {1},
                    ""size"": {2}
                }}";

Here is a more realistic and not so complicated query with ElasticSearch, now try to escape that support substitutions for each value!

{
    "query":{
        "bool": {
            "must": [
                { "match": { "address.city": "New York" } }
               ,{ "match_phrase_prefix": { "lastName": "Sanders" } }
            ]
            ,"must_not": [
                {"range": {"dateOfBirth" : {"gte": "1980-01-01", "lte": "2000-01-01" }}}
            ]
        }
    }
    ,"sort": { "customerId" : {"order": "asc"} }
    ,"size": 100
    ,"from": 0 
    ,"_source": ["firstName", "lastName"]
}

You might rightly ask why I don’t use the provided libraries from the Elastic company. Well, I am working on a system that uses multiple languages, I do my experiments and testing with a HTTP client, and the last thing I want to do is convert everything from JSON to a significantly different formats for each programming language. JSON is also the first class citizen of ElasticSearch, I don’t want to find out later that the .NET client has not kept up with features provided by ElasticSearch. JSON is also very easy to share with colleagues.

What To Do
I am going to store my JSON in a file that becomes part of my source code, deserialize it into a dynamic object, set the values on the fields I want to change, serialize it back to a string and use that string in my requests. It is not as complicated as that might sound and way better than escaping the JSON.

Let’s take the first ElasticSearch query, here again is the raw JSON, I save it to file named ElasticSearchQuery.json.

{
  "query": {
      "match_phrase_prefix": {
          "fullName" : ""
      }
  },
  "from": 0,
  "size": 0
}

And here is how I read, set values and serialize it again –

private string GetElasticSearchQuery(string fullName, int from, int size)
{
    string elasticSearchQuery = File.ReadAllText("ElasticSearchQuery.json");
    dynamic workableElasticSearchQuery = JsonConvert.DeserializeObject(elasticSearchQuery);

    workableElasticSearchQuery.query.match_phrase_prefix.fullName = fullName;
    workableElasticSearchQuery.from = from;
    workableElasticSearchQuery.size = size;

    return workableElasticSearchQuery.ToString();
}

Line 3 reads the JSON file into a string.
Line 4 turns the string into a dynamic object.
Lines 6,7,8 set the values I want.
Line 10 returns a nice JSON string that can be used with a HttpClient to make request to ElasticSearch.

But some ElasticSearch queries are a little harder to work with because a query can include a bool. This example is in the file ElasticSearchQuery.json.

{
    "query": {
        "bool": {
            "must": [
                {"match_phrase_prefix": { "lastName" : "" } }
                ,{"match": { "address.state" : ""} } 
            ]
        }
    }
}

The dynamic object will not allow us to use “bool” because it is reserved word in C#, but you can put an “@” in front of it, and now it will work –

private string GetElasticSearchQuery2(string lastName, string state)
{
    string elasticSearchQuery2 = File.ReadAllText("ElasticSearchQuery2.json");
    dynamic workableElasticSearchQuery2 = JsonConvert.DeserializeObject(elasticSearchQuery2);

    workableElasticSearchQuery2.query.@bool.must[0].match_phrase_prefix.lastName = lastName;
    workableElasticSearchQuery2.query.@bool.must[1].match = new JObject(new JProperty("address.state", state));

    return workableElasticSearchQuery2.ToString();
}

And again the string produced can be used with a HttpClient.

Full source code available here.

Getting Started with ElasticSearch, Part 3 – Deploying to AWS with Pulumi

Full source code available here.

This is part 3 of my short introduction to ElasticSearch. In the first part I showed how to create an ElasticSearch index, mapping, and seeded it with data. In the second I used HttpClientFactory and a typed client to query the index. In this part I going to show you how to setup ElasticSearch in AWS using infrastructure as code. Be careful, AWS charges for these things.

A few months ago Pulumi added C# to their list of supported languages. If you haven’t heard of them, they are building a tool that lets you create the IaC in a familiar programming language, at the time of writing they support TypeScript, JavaScript, Python, Go and C#. Writing in a programming language makes it easy to work with things like loops and conditionals, if you are unfamiliar with IaC, those two simple things can be extremely challenging or impossible with other tools.

I’m going to write my IaC in C#.

I’m not going to walk you through installing Pulumi, their site has all the info you need for that.

The IaC Project
Once you have installed Pulimi and tested that the command works, create a new directory called ElasticSearchDeploy.

Change to that directory and run –

pulumi new aws-csharp

Follow the instructions and open the project in VS Code or Visual Studio.

Delete the MyStack.cs file.
Create a file named MyElasticSearchStack.cs.

Paste in the below code –

using Pulumi;
using ElasticSearch = Pulumi.Aws.ElasticSearch;
using Aws = Pulumi.Aws;
using Pulumi.Aws.ElasticSearch.Inputs;

class MyElasticSearchStack : Stack
{
    public MyElasticSearchStack()
    {
        string myIPAddress = "x.x.x.x" you need to put your IP address here;
        string esDomainName = "myelasticesearch";
        var config = new Config();
        var currentRegion = Output.Create(Aws.GetRegion.InvokeAsync());
        var currentCallerIdentity = Output.Create(Aws.GetCallerIdentity.InvokeAsync());
        var esDomain = new ElasticSearch.Domain(esDomainName, new ElasticSearch.DomainArgs
        {
            DomainName = esDomainName,
            ClusterConfig = new ElasticSearch.Inputs.DomainClusterConfigArgs
            {
                InstanceType = "t2.small.elasticsearch",
            },
            EbsOptions = new DomainEbsOptionsArgs()
            {
                EbsEnabled = true,
                VolumeSize = 10,
                VolumeType = "gp2"
            },
            ElasticsearchVersion = "7.7",
            AccessPolicies = Output.Tuple(currentRegion, currentCallerIdentity).Apply(values =>
            {
                var currentRegion = values.Item1;
                var currentCallerIdentity = values.Item2;
                return @$"
                {{
                    ""Version"": ""2012-10-17"",
                    ""Statement"": [
                        {{
                            ""Action"": ""es:*"",
                            ""Principal"": {{
                                ""AWS"": ""*""
                            }},
                            ""Effect"": ""Allow"",
                            ""Resource"": ""arn:aws:es:{currentRegion.Name}:{currentCallerIdentity.AccountId}:domain/{esDomainName}/*"",
                            ""Condition"": {{
                                ""IpAddress"": {{""aws:SourceIp"": [""{myIPAddress}""]}}
                            }}
                        }}
                    ]
                    }}
                ";
            }),
        });
        this.ESDomainEndpoint =  esDomain.Endpoint;
    }
    [Output]
    public Output<string> ESDomainEndpoint { get; set; }
}

Note on line 10, you need to put in the IP address you are using. Checking this with a site like https://ipstack.com/.

In Program.cs change the reference my MyStack to MyElasticSearchStack.

That’s it.

Deploying
Go to the command line, run –

pulumi up

Select ‘yes’ and then wait about 10 to 15 minutes as AWS gets your ElasticSearch domain up and running. In the output of the command you willsee the url of the ElasticSearch domain you just created, use that in the scripts from part 1 of this series.

You can also go to the AWS console, you should see something like –

There you go – ElasticSearch index creating, seeding, querying, and infrastructure as code.

In a follow up post I’ll show you how to deploy ElasticSearch with Terraform.

The JSON Problem
For those of you that dislike horribly escaped blocks of JSON inside C#, as I do, I am working on a post that will make this much nicer to look at, and to work with.

Full source code available here.

The Simplest Hello World in Node.js

Full source code available here.

I am learning Node.js and have found it a bit of a struggle to locate good, simple documentation. It feels like most people writing in the space assume a lot of existing knowledge, like you know how plenty of JavaScript, or how to effectively use web browser debug tools, have a good understanding of HTML and CSS. If you go down the rabbit hole of asynchronous programming in Node.js the writers often assume you know about the previous Node.js approach of asynchronous, try reading about async/await and you’ll be pointed to examples with promises, promisify, microtasks and callbacks, again assuming a lot of knowledge on the part of the reader.

I even asked a friend who was using Node.js to build a complex web site to write a simple Hello World program for me that I could run from the command line, but he didn’t know. This was a very experienced developer working in the industry for almost thirty years, but he started with Node.js inside a an existing web site.

If you search for how to write a “Hello world” in Node.js, you’ll find examples that setup web servers, take requests, return response, and probably use callbacks.

Because of what I perceive as a lack of documentation, I’m going write some simple blog posts about things I learn but could not find a simple example of.

To kick off, here is Hello World in Node.js.

I’m going assume you have Node and the Node Package Manager installed, and that you can run node and npm from the command line. If you do not have them installed there is plenty of help out there for that!

Hello World
Create a new directory called SimpleHelloWorld.

Open a terminal/console inside that directory.

Run –

npm init -y

This creates a package.json file, you will edit this in a moment.

Create a new file called helloworld.js.

Add a single line to the file –

console.log("Hello World");

At this point you can run –

node helloworld.js

And you will see “Hello World” printed. There you a simple Hello World program.

Let’s do one more thing, open package.json.

Replace the scripts section with –

 "scripts": {
    "start": "node helloworld.js"
  },

Now you can run –

npm start

Full source code available here.

Getting Started with ElasticSearch, Part 2 – Searching with a HttpClient

Full source code available here.

In the previous blog post I showed how to setup ElasticSearch, create and index and seed the index with some sample documents. That is not a lot of use without the ability to search it.

In this post I will show how to use a typed HttpClient to perform searches. I’ve chosen not to use the two libraries provided by the Elasticsearch company because I want to stick with JSON requests that I can write and test with any tool like Fiddler, Postman or Rest Client for Visual Studio Code.

If you haven’t worked with HttpClientFactory you can check out my posts on it or the Microsoft docs page.

The Typed HttpClient
A typed HttpClient lets you, in a sense, hide away that you are using a HttpClient at all. The methods the typed client exposes are more business related than technical – the the type of request, the body of the request, how the response is handled are all hidden away from the consumer. Using a typed client feels like using any other class with exposed methods.

This typed client will expose three methods, one to search by company name, one to search my customer name and state, and one to return all results in a paged manner.

Start with an interface that specifies the methods to expose –

public interface ISearchService
{
    Task<string> CompanyName(string companyName);
    Task<string> NameAndState(string name, string state);
    Task<string> All(int skip, int take, string orderBy, string direction);
}

Create the class that implements that interface and takes a HttpClient as a constructor parameter –

public class SearchService : ISearchService
{
    private readonly HttpClient _httpClient;
    public SearchService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }
    //snip...

Implement the search functionality (and yes, I don’t like the amount of escaping I need to send a simple request with a JSON body) –

public async Task<string> CompanyName(string companyName)
{
    string query = @"{{
                        ""query"": {{
                            ""match_phrase_prefix"": {{ ""companyName"" : ""{0}"" }} 
                        }}
                    }}";
    string queryWithParams = string.Format(query, companyName);
    return await SendRequest(queryWithParams);
}

public async Task<string> NameAndState(string name, string state)
{
    string query = @"{{
                        ""query"": {{
                            ""bool"": {{
                                ""must"": [
                                    {{""match_phrase_prefix"": {{ ""fullName"" : ""{0}"" }} }}
                                    ,{{""match"": {{ ""address.state"" : ""{1}""}}}} 
                                ]
                            }}
                        }}
                    }}";
    string queryWithParams = string.Format(query, name, state);
    return await SendRequest(queryWithParams);
}

public async Task<string> All(int skip, int take, string orderBy, string direction)
{
    string query = @"{{
                    ""sort"":{{""{2}"": {{""order"":""{3}""}}}},
                    ""from"": {0},
                    ""size"": {1}
                    }}";

    string queryWithParams = string.Format(query, skip, take, orderBy, direction);
    return await SendRequest(queryWithParams);
}

And finally send the requests to the ElasticSearch server –

private async Task<string> SendRequest(string queryWithParams)
{
    var request = new HttpRequestMessage()
    {
        Method = HttpMethod.Get,
        Content = new StringContent(queryWithParams, Encoding.UTF8, "application/json")
    };
    var response = await _httpClient.SendAsync(request);
    var content = await response.Content.ReadAsStringAsync();
    return content;
}

The Setup
That’s the typed client taken care of, but it has be added to the HttpClientFactory, that is done in Startup.cs.

In the ConfigureServices(..) method add this –

services.AddHttpClient<ISearchService, SearchService>(client =>
{
    client.BaseAddress = new Uri("http://localhost:9200/customers/_search");
});

I am running ElasticSearch on localhost:9200.

That’s all there is to registering the HttpClient with the factory. Now all that is left is to use the typed client in the controller.

Searching
The typed client is passed to the controller via constructor injection –

[ApiController]
[Route("[controller]")]
public class SearchController : ControllerBase
{
    private readonly ISearchService _searchService;
    public SearchController(ISearchService searchService)
    {
        _searchService = searchService;
    }
    //snip...

Add some action methods to respond to API requests and call the methods on the typed client.

[HttpGet("company/{companyName}")]
public async Task<ActionResult> GetCompany(string companyName)
{
    var result = await _searchService.CompanyName(companyName);
    return Ok(result);
}

[HttpGet("nameAndState/")]
public async Task<ActionResult> GetNameAndState(string name, string state)
{
    var result = await _searchService.NameAndState(name, state);
    return Ok(result);
}

[HttpGet("all/")]
public async Task<ActionResult> GetAll(int skip = 0, int take = 10, string orderBy = "dateOfBirth", string direction = "asc")
{
    var result = await _searchService.All(skip, take, orderBy, direction);
    return Ok(result);
}

That’s it. You are up and running with ElasticSearch, a seeded index, and an API to perform searches.

In the next post I’ll show you how to deploy ElasticSearch to AWS with some nice infrastructure as code.

Full source code available here.

Getting Started with ElasticSearch, Part 1 – Seeding

Full source code available here.

This is the first in a short series of blog posts that will get you started with ElasticSearch. In this you will deploy seed and query ElasticSearch from your own computer. The next will add a .NET Core API into the mix as a ‘frontend’ for ElasticSearch. And the last will show how to deploy an ElasticSearch domain on AWS using an infrastructure as code tool.

This post will show you how to create a simple document mapping, seed the ElasticSearch index and perform some simple queries. It is not a substitute for reading the docs, it is more of step up to help you get going.

Getting Started
Download and install the latest version of ElasticSearch, this post was written when 7.7 was the up to date version, I mention this because if you are reading this when a version > 7 is available the steps may not work – ElasticSearch is known for making major breaking changes in major releases.

Start up ElasticSearch by changing to its directory and running –

bin/elasticsearch

It will start on localhost:9200 by default.

If you are using Visual Studio Code I suggest installing the Rest Client extension. The elasticSearch.http file in attached zip contains examples of how to create and delete indexes, add mappings, and perform queries.

At the top of my elasticSearch.http I have two variables that will be used throughout the rest of the file, these define the host where ElasticSearch is running and the name of the index I’m working with

@elasticSearchHost = http://localhost:9200
@index = customers

To see the indexes that are already in place run this –

GET {{elasticSearchHost}}/_cat/indices?v&pretty

Adding an Index with Rest Client

Let’s add the customer index with Visual Studio Rest Client, of course you can use Postman, Fiddler or any tool of your choosing –

PUT {{elasticSearchHost}}/{{index}}
Content-Type: application/json

{
  "mappings": {
    "properties": {
      "companyName": {
        "type": "text"
      },
      "customerId": {
        "type": "integer"
      },
      "dateOfBirth": {
        "type": "date"
      },
      "email": {
        "type": "text"
      },
      "firstName": {
        "type": "text",
        "copy_to": "fullName"
      },
      "middleName": {
        "type": "text",
        "copy_to": "fullName"
      },
      "lastName": {
        "type": "text",
        "copy_to": "fullName"
      },
      "fullName": {
        "type": "text"
      },      
      "mobileNumber": {
        "type": "text"
      },
      "officeNumber": {
        "type": "text"
      },
      "address": {
        "properties": {
          "line1": {
            "type": "text",
            "copy_to": "fullAddress"
          },
          "line2": {
            "type": "text",
            "copy_to": "fullAddress"
          },
          "city": {
            "type": "text",
            "copy_to": "fullAddress"
          },
          "state": {
            "type": "text",
            "copy_to": "fullAddress"
          },
          "zip": {
            "type": "text",
            "copy_to": "fullAddress"
          }
        }
      },
      "fullAddress": {
          "type": "text"
      }
    }
  }
}

Run the request to list indexes again and you will see the customers index.

GET {{elasticSearchHost}}/_cat/indices?v&pretty

Now you have an index full of nothing, docs.count is 0 –

health status index     uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   customers RDWD3Q75TVqf7VkvO932mA   1   1          0            0       208b           208b

Seeding
Time to switch to the seeder. This is Node.js program that checks if the customers index exists, creates it if it does not, and seeds the index with 5000 customer documents.

I’m not going to go into how it works as I am learning Node.js now and I’m sure it is not as good as it should be. You can execute it by running –

npm install
node seed.js

Now you should see a different result when you look at the indexes on the ElasticSearch server.

GET {{elasticSearchHost}}/_cat/indices?v&pretty

health status index     uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   customers wsdQcJ1IQNOXQ-QQIX_a_Q   1   1       5000            0      2.4mb          2.4mb

Here are a few examples of requests you can make to ElasticSearch.

###
# delete an index, BE CAREFUL WITH THIS ONE
DELETE {{elasticSearchHost}}/{{index}}?pretty

###
# retrieve a document from the index by its id
GET {{elasticSearchHost}}/{{index}}/_doc/1

###
# search the index with no query, this will match all documents, but return only the first few
GET {{elasticSearchHost}}/{{index}}/_search

###
# retrieve a page of results with no query
GET {{elasticSearchHost}}/{{index}}/_search
Content-Type: application/json

{
  "sort":{"dateOfBirth": {"order":"asc"}},
  "from": 0,
  "size": 10
}

###
# search for company names that match the word 'Turcotte', you might need to change this name
GET {{elasticSearchHost}}/{{index}}/_search
Content-Type: application/json

{
    "query": {
        "match_phrase_prefix": {
            "companyName" : "Turcotte" 
        } 
    }
}

###
# search for people in Utah with the name Keith (first, middle or last), you might need to change these parmas.
GET {{elasticSearchHost}}/{{index}}/_search
Content-Type: application/json

{
    "query": {
        "bool": {
            "must": [
                {"match_phrase_prefix": { "fullName" : "Keith" } }
               ,{"match": { "address.state" : "Utah"}} 
            ]
        }
    }
}

That’s it for now.

In the next blog post I will show you how to use `HttpClientFactory` and typed clients to perform searches in .NET Core.

Full source code available here.

Dependency Inject a Service from Startup back to Program in .Net Core 3.1

Full source code available here.

Over the past couple of years I wrote a few posts about Dependency Injection in .Net Core 2.1, and this week I received comments from a reader telling me that some of the changes in .Net Core 3.1 mean that some of the approaches no longer work. There have been breaking changes https://docs.microsoft.com/en-us/dotnet/core/compatibility/2.2-3.1#hosting-generic-host-restricts-startup-constructor-injection

I wanted to see what would still work so I tried a few things.

You can no longer DI from Program in Startup.

But you can add a transient service and/or a singleton service to the ServiceCollection and use it within Program, and the rest of the application. You can also add a scoped service to the ServiceCollection and use it within the Program, it’s a little different from using transient and singleton so I’ll cover it in the next post.
Here’s how to use transient and singletons inside Program.
Create two services, creatively named ServiceOne and ServiceTwo and have them implement interfaces.

public class ServiceOne : IServiceOne
{
	public static int staticCounter;

	public ServiceOne()
	{
		staticCounter++;
	}
   //snip…

public class ServiceTwo : IServiceTwo
{
	public static int staticCounter;

	public ServiceTwo()
	{
		staticCounter++;
	}
	//snip…

I added a static counter to make it easy to see how many times the constructor is called.
For the transient one I expect it to increment every time the service is injected, for the singleton I expect it to remain at 1 for the lifetime of the application.

In Program.cs I split up CreateHostBuilder(args).Build call from the subsequent .Run().

public static void Main(string[] args)
{
	IHost host = CreateHostBuilder(args).Build();
	DoSomethingWithTheTransientService(host.Services);
	DoSomethingWithTheSingletonService(host.Services);
	host.Run();
}

The CreateHostBuilder() method is not changed –

public static IHostBuilder CreateHostBuilder(string[] args) =>
	Host.CreateDefaultBuilder(args)
	.ConfigureWebHostDefaults(webBuilder =>
	{
		webBuilder.UseStartup<Startup>();
	});

And I have two methods that use the ServiceCollection to access the registered services –

private static void DoSomethingWithTheTransientService(IServiceProvider serviceProvider)
{
	Console.WriteLine("Calling the transient service");

	var serviceOne = serviceProvider.GetService<IServiceOne>();
	Console.WriteLine(serviceOne.StaticCounter());
	Console.WriteLine(serviceOne.GetHashCode());
}

private static void DoSomethingWithTheSingletonService(IServiceProvider serviceProvider)
{
	Console.WriteLine("Calling the singleton service");

	var serviceTwo = serviceProvider.GetService<IServiceTwo>();
	Console.WriteLine(serviceTwo.StaticCounter());
	Console.WriteLine(serviceTwo.GetHashCode());
}

To make what’s happening more obvious I added the services to the constructor call of the WeatherForecastController() and added the counter and hash codes to the data returned by the action method.

public class WeatherForecastController : ControllerBase
{
	private IServiceOne _serviceOne;
	private IServiceTwo _serviceTwo;
	public WeatherForecastController(IServiceOne serviceOne, IServiceTwo serviceTwo)
	{
		_serviceOne = serviceOne;
		_serviceTwo = serviceTwo;
	}

For completeness, here is the ConfigureServices method in Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IServiceOne, ServiceOne>();
    services.AddSingleton<IServiceTwo, ServiceTwo>();
    services.AddControllers();
}

Put some breakpoints in Program Main(), DoSomethingWithTheTransientService(), DoSomethingWithTheSingletonService() and in the WeatherForecastController.Get().

Start the application and browse to http://localhost:5000/weatherforecast to see what happens.

I’m going to follow up on this post with a version that shows how to use scoped dependencies in Startup .NET 5.

Full source code available here.

Executing a Method on All Implementations of an Interface

Full source code available here.

Have you ever wanted to execute a method on all implementations of an interface? Say you have an interface called IGenerator and four implementations of it – GeneratorA, GeneratorB, GeneratorC, and GeneratorD.
IGenerator is very simple, it has a single Generate(string details) method.

What I don’t want to do is –

var generatorA = new GeneratorA();
var generatorB = new GeneratorB();
var generatorC = new GeneratorC();
var generatorD = new GeneratorD();
generatorA.Generate(someDetails);
generatorB.Generate(someDetails);
generatorC.Generate(someDetails);
generatorD.Generate(someDetails);

I also don’t want to do this –

List<IGenerator> generators = new List<IGenerator>() { new GeneratorA(), new GeneratorB(), new GeneratorC(), new GeneratorD() } ;
foreach (var generator in generators) 
{
    generator.Generate(someDetails)
}

What I want to do is find all the implementations of the interface without explicitly naming them, create instances of the implementations and call the Generate(..) method. This way I can add a GeneratorE and GeneratorF and I’ll be sure their Generate(..) methods will be called.

Finding the Implementations

This code will find all the implementations of the IGenerator interface –

var iGenerator = typeof(IGenerator);
var generatorsTypes = AppDomain.CurrentDomain.GetAssemblies() 
    .SelectMany(assembly => assembly.GetTypes())
    .Where(type => iGenerator.IsAssignableFrom(type) && type.IsClass);

On line 1 I specify the type I am going to look for.

Line 2 gets all the assemblies, line 3 gets all the types in those assemblies. Line 4 limits the type to those that implement IGenerator and are a class, checking that the type is a class is important, because without it the interface itself would be included in the result.

Activating the Implementations

At this point all we have done is find the types, we have not instantiated them, so we cannot yet call the Generate(..) methods.
But of course there is a way to do just that.

List<IGenerator> generators = new List<IGenerator>();

foreach (var generator in generatorsTypes)
{
    generators.Add(Activator.CreateInstance(generator) as IGenerator);
}

Now I have a list with instances of all the implementations of IGeneraotor.

Calling the methods

Easy –

generators.ForEach(g => g.Generate("Hi from a generator."));

That’s it, find the implementations, activate them, call the method. The end.

Full source code available here.

Simple Func<T> and Func<T1, T2, TResult> Examples

Full source code available here.

About a month ago I wrote a post with a simple explanation of how to use methods that take Action or Action<T> as parameters. Actions themselves take 0 to 16 parameters, and return nothing.

This is a follow up with some examples of how to call methods that take Func<TResult> or Func<T1, T2, TResult> as parameters. Funcs return a value and take 0 to 16 parameters.

As with Actions, there are a variety of ways to call methods that take Funcs.

Example 1

It’s not easy to come up with a non contrived examples for this post, and especially difficult for Func, a function that takes no parameters and returns something.

public static string DateCalculator(Func<double> daysSinceSomeDate)
{
   return $"{daysSinceSomeDate()} days in your date calculation";
}

This method expects a Func<double>, that is, a Func<TResult> that returns a double and takes no parameters.

Here are two examples calling this –

Console.WriteLine(DateCalculator(() => Math.Round(DateTime.Now.Subtract(new DateTime(2000,1,1)).TotalDays, 2)));
	
Console.WriteLine(DateCalculator(() => Math.Round(new DateTime(2020,12,31).Subtract(DateTime.Now).TotalDays , 2)));

The calls pass in a Func that performs a calculation with date subtraction, not very useful, as I said, it’s not easy to come up with a simple and useful example of using a Func<TResult>.

Example 2

This method expects a Func<int, int, int>, that is, a Func<T1, T2, TResult>. It takes two ints and returns an int.

public static void SomeMath(Func<int, int, int> mathFunction)
{
	int result = mathFunction(7, 5);
	Console.WriteLine($"The mathFunction returned {result}");
}

There are a variety of ways to call this. Here are some examples.

Create a method that takes two ints and returns an int.

public static int Subtraction(int number1, int number2)
{
	return number1 - number2;
}

You can now call SomeMath(..) like this –

SomeMath(Subtraction);

Or this –

Func<int, int, int> myFuncAsMethod = Subtraction;
SomeMath(myFuncAsMethod);

I like showing how you to pass a method into a method that takes a Func, because if you are ever struggling writing a lambda to satisfy a Func parameter, stop, and write a simple method instead to work out what you need to do. It can be easier to read, and is usually easier to debug.

Speaking of lambdas, here’s how to call the SomeMath(..) method with lambdas.

This is the simplest approach –

SomeMath((a, b) => a + b);

This lambda will take two parameters, named a and b, then performs addition on them. You can think of (a, b) as parameters to the “method body” to the right of =>. That “method body” adds a and b. There is also an implied “return” statement, but it can be left out of single line lambdas like that. For multi-line lambdas it is necessary.

Here is such an example –

SomeMath((x, y) =>
{
	int temp = x * y; // you can make your lambdas multi-line
	int calculatedNumber = temp * temp; 
	return calculatedNumber;
});

You can also assign a lambda to a declared Func, but I don’t see that done very often. You might do this if you are going to use the same lambda logic in multiple places.

Func<int, int, int> myFuncAsLambda = (a, b) => a * b;
SomeMath(myFuncAsLambda);

There are other ways to satisfy the method SomeMath(..), but these should be enough for most scenarios you will come across.

Full source code available here.

Simple Action and Action<string> Examples

Full source code available here.

A junior engineer colleague of mine recently asked me “how the f*** do I call this method?”. Seemed like a reasonable question. It was a method that took a complicated Func, and an Action with a series of parameters. We broke it down into each part and I explained them to him separately.

I think this is a problem that more people have so I going to try to give some simple examples that hopefully you can learn from and build on yourself.

This post will talk only about Actions, a later post will talk about Funcs. I’m not going to go into the details of delegates, anonymous methods, how they get called, when they get executed, etc, there are plenty of blog posts and articles out there on those topics already. Instead I’m going to focus on a few examples and explain them.

Example 1

This method takes an Action as a parameter, the Action itself has no parameters.

public static void Printer(Action myPrinterAction)
{
    myPrinterAction();
}

How do we call this? Here are few examples.
Create a second method –

public static void PrinterMethod()
{
    Console.WriteLine("Hello from PrinterMethod");
}

Now you can call Printer(..) like this –

Printer(PrinterMethod);

Or this

Printer(() => PrinterMethod());

To short cut this, you can use a lambda as the parameter to Printer(..). The lambda must take no arguments and return nothing, because that is how the Action the Printer method task is defined.

Printer(() => Console.WriteLine("Hello from Console.WriteLine"));

Generally, you will want to go for the lambda approach as it many people consider it the easiest to read.

There are more ways of calling Printer(..).
You can explicitly define an Action and assign the PrinterMethod to it.

Action printerActionAsMethod = PrinterMethod;
Printer(printerActionAsMethod);

You can explicitly define an Action and assign lambda to it.

Action printerActionAsLambda = () => Console.WriteLine("Explicitly defined printer action");
Printer(printerActionAsLambda);

There are other ways, but I think these are going to cover most of your needs.

Example 2

In this example the Printer method takes an Action as a parameter, this Action method takes a string as a parameter. The Printer(..) method also takes a string, this string is what the Action method will print.

public static void Printer(Action<string> myPrinterAction, string value)
{
    myPrinterAction(value);
}

How we call this is a little different than above. Here are few examples.
Create a second method –

public static void PrinterMethod(string text)
{
    Console.WriteLine($"Hello from PrinterMethod - {text}");
}

Then use it like this –

Printer(PrinterMethod, "Hi there");

Or this –

Printer((text) => PrinterMethod(text), "Calling print method");

Again, a lambda is probably the better approach. The lambda takes a single string as the argument and returns nothing, because that is how the Action the Printer(..) method task is defined.

Printer(someMessage => Console.WriteLine($"Hello from Console.WriteLine - {someMessage}"), "this is a lambda");

The included source code shows a few other ways of making the calls, but I think the ones discussed here will cover most of the scenarios you will hit.

Full source code available here.