Category Archives: C#

Alignment using string interpolation in C#

C#’s string interpolation supports alignment and format options

Alignment

Using the following syntax, {expression, alignment}, we can use negative alignment numbers for left-aligned expressions and positive numbers for right-aligned alignment. For example

var number = 123;
Console.WriteLine($"|{number,-10}|");
Console.WriteLine($"|{number,10}|");

This would produce the following

|123       |
|       123|

See also Alignment component.

Format String

We can use the following {expression, alignment:format} or {expression:format} syntax to add formatting, i.e.

var number = 123;
Console.WriteLine($"|{number, -10:F3}|");
Console.WriteLine($"|{number, 10:F3}|");

would produce the following

|123.000   |
|   123.000|

and

var number = 123;
Console.WriteLine($"|{number:F3}|");
Console.WriteLine($"|{number:F3}|");

would produce

|123.000|
|123.000|

See also Format string component.

The with expression in C# 9.0 and later

I’ve not had cause to use this so far. Which seems strange (in a way) as I used similar language features with the spread operator and changing values in TypeScript a lot.

The with expression basically copies a record, struct or anonymous type and allows us to change some values, hence creating a new instance but overriding certain properties or fields.

Note: In C# 9.0 the with expression only worked with record types, in C# 10 it can be used, additionally, with struct and anonymous types.

Let’s look at a simple example. We have my old favourite demo type, a Person record which looks like this

public record Person(string Name, int Age);

Now we’ll create a Person like this

Person scooby = new("Scooby Doo", 7);

Console.WriteLine(scooby);

This would ofcouse output something like Person { Name = Scooby Doo, Age = 7 }.

Note: Obviously this is a rather simplistic record but it should give you the idea how things work. For more complex cases imagine there’s a first name, last name, lines of an address, national insurance/social security number etc. just to make it a little more work to create copies of a Person record instance.

Now let’s say we wanted to create a new record which has all the same properties/fields bar some properties/fields that will be changed. Ofcourse we could just create a new Person in the same way as above and change the properties. However, the with expression allows us to copy and change in a more elegant way (well more elegant if we had lots more properties/fields on the record than this example).

We can write

Person scooby = new("Scooby Doo", 7);
Person scrappy = scooby with { Name = "Scrappy Doo" };

Console.WriteLine(scooby);
Console.WriteLine(scrappy);

The scooby instance remains unchanged as we’ve essentially created a new instance of the Person record, copying the Age and explicitly changing the Name.

Now, what if we wanted to simply create a new instance of a Person with the same properties and fields as the scooby instance? We can just use the { } syntax like this

Person scoobyCopy = scooby with { };

Assert.IsFalse(Object.ReferenceEquals(scooby, scoobyCopy));

As the Assert states, scooby and scoobyCopy instances are not the same instance, they are copies.

Note: If using a struct (or if you’re explicitly supplying properties for a record) as the left-hand of the with expression and change values within the curly braces, you will need properties to have setters.

.NET HttpClient – the correct way to use it

For ages I’ve been using the HttpClient in .NET without any issues, but usually I have a single endpoint and a singleton (or equivalent) of a class that uses the HttpClient. A while back I needed to call multiple different endpoints using the HttpClient and so I happily wrapped an instantiation of an HttpClient in a using statement, I mean after all it has an IDisposable interface so that makes sense right?

Well NO this is not the case. So don’t do this!

// Don't do this
using(var httpClient = new HttpClient())
{
   // do something with httpClient
}

This post stems from a work colleague pointing me to the post YOU’RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE.

What does the documentation say?

If you take a look at the HttpClient Class documentation states

HttpClient is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors.

If we take a look at some C# templates in Visual Studio, for example the ASP.NET core Blazor application template, it will give you a scoped instance of the HttpClient, for example

builder.Services.AddScoped(sp => 
  new HttpClient 
    { 
       BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) 
    }
);

So how are we supposed to use a single instance of HttpClient

Let’s now assume that we create an HttpClient per application. Then how do we call multiple service endpoints without having changes affect different endpoint calls and is it threadsafe?

Is the HttpClient thread-safe? Well the answer is both yes and no.

Changing properties on the HttpClient are not threadsafe or at least cannot/should not be modified whilst there are outstanding requests. However methods such as GetAsync, PostAsync, SendAsync etc. are threadsafe which then leads us to how to se set-up different calls, i.e. maybe different headers etc. on all our calls. The answer here is to use SendAsync and create an HttpRequestMessage. This allows us to specify many of the properties we’ll need per HTTP call.

References

YOU’RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE
HTTPCLIENT CREATION AND DISPOSAL INTERNALS: SHOULD I DISPOSE OF HTTPCLIENT?

Nullable reference types in C#

Nullable reference types are now enabled by default on projects created with Visual Studio 2022, so let’s have a quick look at them…

Enabling nullable reference type checking

By default nullable reference types were disabled prior to Visual Studio 2022, so would need enable them at a project or file level. Enabling in the project means adding the following to the .csproj file

<PropertyGroup>
  <OutputType>Exe</OutputType>
  <TargetFramework>net5.0</TargetFramework>
  <Nullable>enable</Nullable>
</PropertyGroup>

If you prefer to enable at a file level then simply add the following to the top of each file

#nullable enable

Now what?

So once you’ve enabled nullable reference type checking, especially if on a legacy code base, be prepared for warnings such as

  • warning CS8625: Cannot convert null literal to non-nullable reference type.
  • warning CS8604: Possible null reference argument for parameter ‘s’ in ‘void Program.SetName(string s)’.
  • warning CS8602: Dereference of a possibly null reference.

I’m sure there are plenty of other warnings, but you get the idea.

Basically what we’re ask the compiler to do is tell us when we might have the potential to be passing a null into a function etc. and highlight the potential issue with a warning. In other words we now need to mark reference types as nullable so the compiler knows we’re expecting a null and in situations where we’re not using a nullable reference type we’ll be warned.

Let’s see some example code. First off, if you default arguments to null, for example

public void SetName(string name = null)
{
   // do something
}

This will result in the warning >warning CS8625: Cannot convert null literal to non-nullable reference type.. All we need to do is tell the compiler we’re expecting a null, by making the argument nullable, i.e. add the ? to the type just like nullable value types.

public void SetName(string? name = null)
{
   // do something
}

Any usage of the variable name will now expect a null check, so if we compile the following

public void SetName(string? name = null)
{
   Console.WriteLine(name.Length);
}

as you’d imagine, the compiler will issue a warning here, in this case warning CS8602: Dereference of a possibly null reference., so obviously we have the potential of a null reference exception here, so adding a conditional check like this will fix that

static void SetName(string? name = null)
{
   if (name != null)
   {
      Console.WriteLine(name.Length);
   }
}

In some situations you may in fact know a variable/argument is not null – in TypeScript it’s not unusual to find the transpiler getting a little confused in which case we use the null-forgiving operator simply an !, so whilst the SetName method, as it stands ofcourse can be null, what if we remove the optional argument and expect all callers of the SetName method – for example maybe it’s a private method and we know each caller must check for null first anyway, then we use the following

static void SetName(string? name)
{
   Console.WriteLine(name!.Length);
}

We’re basically saying, by using name!. we know the value is not null. This example is a little contrived because we could just change the string? to string and not require the !, but you get the idea.

Struct, Class and now Record types in C#

I thought it’d be interesting to compare the three C# types, struct, class and record. I’m sure we all know that a struct is a value type and a class a reference type, but what’s a record.

The record “keyword defines a reference type that has built-in functionality for encapsulating data” (see Records (C# reference)).

Before we get into record. let’s review what struct and classes give us using the example of a Person type which has a FirstName property.

struct

struct Person
{
  public string FirstName { get; set; }
}
  • A struct extends System.ValueType and structs are “allocated either on the stack or inline in containing types and deallocated when the stack unwinds or when their containing type gets deallocated. Therefore, allocations and deallocations of value types are in general cheaper than allocations and deallocations of reference types.” See Choosing Between Class and Struct.
  • ToString() will output YouNameSpace.Person by default.
  • Structs cannot be derived from anything else (although they can implement interfaces).
  • Structs should be used instead of classes where instances are small and short-lived or are commonly embedded in other objects.
  • As structs are ValueTypes they are boxed or cast to a reference type or one of the interfaces then implement.
  • As a function of being a ValueType, structs are passed by value

class

class Person
{
  public string FirstName { get; set; }
}
  • A class extends System.Object and classes are reference types and allocated on the heap.
  • ToString() will output YouNameSpace.Person by default.
  • Obviously classes may extend other class but cannot extend a record and vice versa, records cannot extend classes.
  • Reference types are passed by reference.

record

Records can have properties supplied via the constructor and the compiler turns these into readonly properties. The syntax is similar to TypeScript in that we can declare the properties in a terse syntax, such as

record Person(string FirstName);

Whilst records are primarily aimed at being immutable we can still declare them with mutability, i.e.

class Person
{
  public string FirstName { get; set; }
}
  • Records can only inherit for other records, for example
    record Scooby() : PersonRecord("Scooby");
    
  • Records use value comparison (not reference comparison) via IEquatable
  • ToString formats output to show the property values
  • Records support the with keyword which allows us to create a copy of a record and mutate at the point of copying. Obviously as records are generally aimed at being immutable this use of with allows us to copy an existing record with some changes, for example
    var p = somePerson with { FirstName = "Scrappy" };
    

    This is not a great example with our record which has a single property, but if we assume Person also had a LastName property set to “Doo” then we’d essentially have created a new record with the same LastName but now with the new FirstName of “Scrappy”.

    We can also copy whole records by not supplying any values within the { } i.e.

    var p = somePerson with { };
    

More Pattern Matching in C#

A while back I wrote a post C# 8.0 enhancements with pattern matching which was very light on the subject. Let’s look a little more in depth at options for pattern matching.

is/as in pattern matching syntax

Often (if you’ve been writing C# for a while) you’ll used to writing code like this

var person = o as Person;
if (person != null)
{
   Console.WriteLine(person.FirstName);
}

i.e. we have an object o which we don’t know the type of, so we use as to convert this to a Person type or null if it’s not of that type.

This can be replaced with a slightly more concise syntax (and what’s known as the declaration pattern).

if (o is Person person)
{
  Console.WriteLine(person.FirstName);
}

Null checks

This is a pattern known as a constant pattern

if (o is null)
{
  Console.WriteLine("Is null");
}

Along with a logical pattern using not we can also write

if (o is not null)
{
  Console.WriteLine("Is not null");
}

We can also use this pattern with types, for example

if (o is not (string or null))
{
  Console.WriteLine("Is NOT string or null");
}

<strong>Pattern matching when values match a criteria</strong>

If we extend the above Pattern Matching to not just check the type is a Person but also that the Age property is greater than 4, so we can now replace

[code language="csharp"]
if (o is Person p && p.Age > 4)
{
  Console.WriteLine($"Older than 4 {p.FirstName}");
}

with the following

if (o is Person { Age: > 4 } p)
{
   Console.WriteLine($"Older than 4 {p.FirstName}");
}

In the above we’re using a property pattern.

Switch patterns matching

Exhaustive switches can be used to match types using switch statements, for example

var result = o switch
{
  string s => $"String {s}",
  Person p => $"Person {p.FirstName}",
  _ => throw new ArgumentException("Unhandled type")
};

Note the use of the _ (discard) ensuring this is an exhaustive switch.

Better still we can also use other features of pattern matching in the switch like this

var result = o switch
{
  string s => $"String {s}",
  Person { Age: > 4} p => $"Person {p.FirstName} > 4",
  Person p => $"Person {p.FirstName}",
  null => "In Null",
  _ => throw new ArgumentException("Unhandled type")
};

In the above we’re switching based upon type and also matching values.

We can also use relational patterns, in this example we’ll assume o is an int

var result = o switch
{
  1 or 2 => "One or Two",
  > 2 and < 4 => "Mid",
  >= 4 and < 6 => "High",
  6 => "Six",
  _ => "Other"
};

Before we leave the switch statement we can also match against types using the “standard” switch syntax, i.e.

switch (o)
{
  case string s:
    Console.WriteLine(s);
    break;
  case Person p:
    Console.WriteLine(p.FirstName);
    break;
}

Tuples

Pattern matching also allows us to match against tuples and use the discard to ignore parts we’re not interested in, for example

var result = o switch
{
  (1, _) => "First One",
  (_, 0) => "Second Zero",
  _ => "Other"
};

Creating new variables from patterns

We can also use the var pattern to assign values from the patterns

var result = o switch
{
  var (a, b) when a < b => new Tuple<string, int, int>("First less than second", a, b),
  var (a, b) when a > b => new Tuple<string, int, int>("Second greater than first", a, b),
  var (a, b) => new Tuple<string, int, int>("Equals", a, b)
};

In this example we’re deconstructing a tuple and assigning to some new value (in this case an extended Tuple, just because I couldn’t think of a better example – check the documentation link above for a better example).

Index and Range in C#

In C# 8.0 two classes were added to C# to handle Index and Ranges (as well as some syntactic sugar in the form of .. to represent a Range). Both are structs, an Index being literally an int but with some static method and “helper” methods for using indicies. As you might expect, a Range, is made up of a start Index and an end Index. Let’s have a look at both types in a little more depth…

Index

An Index is a very simply type which is best understood in usage on a collection. Let’s therefore assume we have some collection type which, ofcourse, allows for you to get at item at a specific index.

This is hardly an exciting addition in itself but what it does is gives us some “standard” code for handling things like, get the nth item from the end of the collection, using the overloaded ^ operator. Let’s see this in action by creating a simply collection

public class MyCollection
{
  private readonly string[] _collection;

  public MyCollection()
  {
    _collection = new[]
    {
      "One",
      "Two",
      "Three",
      "Four",
      "Five"
    };
  }

  public string this[Index idx] => _collection[idx.GetOffset(_collection.Length)];
}

All we’ve really done here that’s slightly different to how we’d have written this without an Index is we use the Index GetOffset to give us the actual index number to get the value from the collection. What this really does is allow us to write code such as

var c = new MyCollection();
Console.WriteLine(c[^2]);

The ^2 on an Index simply means give me the 2nd item from the end which would result in “Four” being written to the console ^1 would be the equivalent of _collection[_collection.Length – 1] hence you can see that ^2 is the equivalent of _collection[_collection.Length – 2]. The Index helper method simply allows us to let it handle any conversion for us. Ofcourse we can write the following

var c = new MyCollection();
Console.WriteLine(c[2]);

Which, as you’ve expect returns “Three” this item at index 2.

Range

The Range uses Index types to create a Range, for example

var range = new Range(1, 10);

This create a range type with a start of 1 and end of the range 10. Where the Range becomes more useful is in the syntactic sugar, the range shown in the snippet above may be written as

var range = 1..10;

This create a new Range type just as the first snippet, but now if we start to use this sort of stuff in collection types or our own types we start to get some neat syntax to get slices of collections – it should be noted that the current implementation of collections in .NET sees arrays supporting Range and hence can return slices of arrays, whereas other collections do not explicitly support Index and Range but instead can be used with them due to the operator overloading etc.

ofcourse we can create our own types to do similar. Before we look at implementing such things let’s quickly review the basic syntax possibilities/expectations

// short cut for getting a slice [0..10]
var firstToN = array[..10]; 
// short cut for getting the item 2 from the end
var nthItemFromEnd = array[^2]; 
// short cut for getting all items into a copy of the array
var all = array[..]; 

Support for Index

We can write our own types to explicitly understand Index and Range types, however using standard methods existing types can support Index and Range. Let’s demonstrate this by changing our MyCollection type to replace the indexer to look like this (i.e. remove the Index support).

public string this[int idx] => _collection[idx];

If you try to compile with myCollection[^2] you’ll now get a compiler error, cannot convert from ‘System.Index’ to ‘int’. So how do existing types work with Index? This is due to “implicit Index support” which basically means the compiler expects certain named methods with certain expected parameters.

The first required is the type is Countable which basically means it has a property named Length or Count with an accessible getter which returns an int. So this covers arrays and collections such as the List type. Let’s therefore add this to our type

public int Count => _collection.Length;

Now the compiler error will go away. If you think about it, it quite obvious what’s happening, in our original code for the indexer

public string this[Index idx] => 
   _collection[idx.GetOffset(_collection.Length)];

We ofcourse need the length of the collection as well as the Index. Well the compiler using the “implicit Index support” needs the same. So once we add the Count or Length properties, it can now get that information at create the code we otherwise would have to write ourselves and thus an indexer [int idx] now gets the value from the Index implicitly supplied.

So to wrap up “implicit Index support”, your type needs the following

  • The type is Countable i.e. has Length or Count property accessible and returning an int
  • The type has an accessible indexer which takes an int
  • The type does not have an accessible indexer which takes an Index as the first parameter

Support for Range

Let’s change our calling code to try to get a slice of our collection, so now we have

var c = new MyCollection();
Console.WriteLine(c[1..^2]);

This will immediately show the compile time error “cannot convert from ‘System.Range’ to ‘int'”. Let’s therefore add implicit support for Range to our collection.

Well again the type needs to be Countable and also not have a accessible indexer which takes a Range (otherwise this would assumer the type is explicitly handling Range). Which currently have.

As you’d imaging to handle ranges we need a method that creates a slice or sub-collection from our collection and that’s exactly what’s required. Let’s add a Slice method which returns a collection (in our case we’re just pretty much copy the code from Ranges which is also the best place to look for information on Index and Range.

Add the following to MyCollection

public string[] Slice(int start, int length)
{
  var slice = new string[length];
  Array.Copy(_collection, start, slice, 0, length);
  return slice;
}

The compiler should now show our previously broken c[1..^2] code as valid and if you make a small change to output the items from the returned slice, like this

foreach (var item in c[1..^2])
{
  Console.WriteLine(item);
}

You’ll see items from the item at index 1 through to the one 2 from the end, i.e. “Two” and “Three”.

So to wrap up “implicit Range support”, your type needs the following

  • The type is Countable i.e. has Length or Count property accessible and returning an int
  • The type contains an accessible method named Slice which takes two int params, the first being the start index the second being the length
  • The type does not have an accessible indexer which takes an Range as the first parameter

With both the implicit Index and Range code you could ofcourse write explicit code (i.e. use Index and Range in your code). Both offer helper methods to make the experience of using them consistent. Ofcourse neither are restricted to collection types and let’s look at extending Range…

Extending Range

As already mentioned, Index and Range are fairly limited in functionality, but that’s fine, they do exactly what they’re intended to do, but if you’ve ever wanted a Range of numbers in C# you’ve probably looked at Enumerable.Range, so writing something like this

foreach (var i in Enumerable.Range(1, 10))
{
  Console.WriteLine(i);
}

For fun and now we have the syntactic sugar of .. to represent a Range type it’s be cool to write something like this instead

foreach (var i in 1..10)
{
  Console.WriteLine(i);
}

Okay so we know that this is really a Range instance like this

foreach(var r in new Range(1, 10))
{
  Console.WriteLine(i);
}

but ofcourse this will not work, as Range does not support IEnumerator. No worries we’ll create an extension method, something like this

public static class RangeExtensions
{
  public static IEnumerator<int> GetEnumerator(this Range range) =>
    Enumerable.Range(range.Start.Value, range.End.Value).GetEnumerator();
}

This now allows us to use a Range within a foreach and better still we can now use the .. operator syntax

foreach (var item in 1..10)
{
  Console.WriteLine(i);
}

Note: Whilst this offers syntax along the lines of Rust – I’m not necessarily saying using Range in this way within a foreach is a great idea because Range, via Index, supports ^ (from end) syntax and we’re definitely not attempting to support such syntax in our extension method but the terse nature of it’s usage is nevertheless interesting (or for those who prefer more explicit methods then it’s probably horrible).

We could extend our fun and our extension method to add support for IEnumerable for Linq queries, whilst not as nice as the IEnumerator in it’s implicit nature, we might add to RangeExtensions, the following

public static IEnumerable<int> ToEnumerable(this Range range)
{
  foreach (var item in range)
  {
    yield return item;
  }
}

Now we can use this like with Linq, maybe like this

foreach (var i in (1..10).ToEnumerable().Where(v => v % 2 == 0))
{
  Console.WriteLine(i);
}

Which will output the even numbers between 1 and 10.

References

Ranges

Named arguments in C#

I’ve actually never had or felt a need to use named arguments in C#. Am I missing anything?

Well first off I have actually used named arguments in other languages and my immediate feeling was – this makes my code so verbose and I felt I got no real benefit from them. Certainly they can (sort of) document your code a little better, but with most/all development tools including intellisense or other tools to tell the development what parameters they were editing/adding and in some cases even show the named of the argument in the editor – hence the benefit of “documenting” seemed to be of little real use.

This all said, let’s look at what we can do with named arguments.

What are named arguments?

When we write methods/functions we pass arguments/parameters using “positional arguments”, which simply means the order of arguments must match the method signature’s argument order. For example let’s look at a simple method to add a person to some application

void AddPerson(string name, string address, int age)
{
   // do something
}

So when we use this method with positional arguments we would write

AddPerson("Scooby Doo", "Mystery Machine", 11);

In C# we also get the ability to use named arguments instead (without any changes to the method signature) by including the argument name as part of the call, so for example

AddPerson(name: "Scooby Doo", address: "Mystery Machine", age: 11);

With tools like Visual Studio 2019, this doesn’t really add anything useful (if we’re mirroring the argument positions) because Visual Studio already tells us the name of each argument in the editor. Obviously outside of Visual Studio, for example is source control, maybe this is more useful.

Surely there’s more to it than that?

Positional arguments are just that, the calling code must supply each argument in the correct position and whilst we can do the same with named arguments, you can also rearrange the arguments and hence no longer need to call using the same positions, for example let’s switch around the named arguments from earlier to give us this

AddPerson(name: "Scooby Doo", age: 11, address: "Mystery Machine");

The C# compiler will simply rearrange the arguments into their positions producing the same IL as is generated for a callee using positional arguments. Here’s an example of such code generated via dotPeek – it’s exactly the same code as for the positional arguments as one would expect.

IL_0013: ldstr        "Scooby Doo"
IL_0018: ldstr        "Mystery Machine"
IL_001d: ldc.i4.s     11 // 0x0b
IL_001f: call         void NameParamsTests.Program::AddPerson(string, string, int32)
IL_0024: nop

One area where named arguments offer some extra goodness is when we’re using optional argument, so let’s assume our AddPerson signature changes to

static void AddPerson(string name, string address = null, int age = Int32.MinValue)
{
   // do something
}

If we’re using positional arguments and we don’t have an address then we must still supply a value for the address, for example

AddPerson("Scooby Doo", null, 11);

But as we’ve seen, with named arguments the order is no longer a limiting factor, therefore we can used named arguments instead and no even bother with the address, the compiler will figure it out for us, hence we can write

AddPerson(name: "Scooby", age: 11);

Note: We can ofcourse use positional and named arguments in a method call if we wish/need to but then the named arguments would need to be in the correct position limiting the usefulness of using named arguments.

Named arguments – when we have lots of arguments

The simple AddPerson method probably isn’t a great example for using named arguments, so lets instead look at a method that takes more arguments with lots of optional arguments. If we instead have a method which looks like this

void AddPerson(
   string firstName, string lastName, int age = Int32.MinValue,
   string addressLine1 = null, string addressLine2 = null, string addressLine3 = null,
   string city = null, string county = null, string postalCode = null)
{
   // do something
}

Now we can see that if we have partial details for the person we can call this method in a more succinct manner, for example

AddPerson(age: 11, firstName: "Scooby", lastName: "Doo", postalCode: "MM1");

// or with mixed positional and named arguments

AddPerson("Scooby", "Doo", 11, postalCode: "MM1");

As you’d imagine, the compiler simply handles the setting of the optional arguments etc. as before giving us IL such as

IL_0001: ldstr        "Scooby"
IL_0006: ldstr        "Doo"
IL_000b: ldc.i4.s     11 // 0x0b
IL_000d: ldnull
IL_000e: ldnull
IL_000f: ldnull
IL_0010: ldnull
IL_0011: ldnull
IL_0012: ldstr        "MM1"
IL_0017: call         void NameParamsTests.Program::AddPerson(string, string, int32, string, string, string, string, string, string)
IL_001c: nop

Once our methods start getting more arguments and especially if lots are defaulted, then named arguments start to make sense, although with a larger number of arguments, one might question whether in fact the method call itself might be in need of refactoring, with our example here we could ofcourse create separate objects for different parts of the data and with C#’s object initializer syntax we sort of get a similar way to create “named” arguments, for example

public struct Person
{
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public int Age { get; set; }
   public string Line1 { get; set; }
   public string Line2 { get; set; }
   public string Line3 { get; set; }
   public string City { get; set; }
   public string County { get; set; }
   public string PostalCode { get; set; }
}

void AddPerson(Person person)
{
   // do something
}

Now using object initializer syntax we could call this method like this

AddPerson(new Person
   {
      FirstName = "Scooby",
      LastName = "Doo",
      Age = 11,
      PostalCode = "MM1"
   });

Eureka Discovery with Steeltoe outside of ASP.NET

Whilst the previous post showed how we use Steeltoe in an ASP.NET application, sometimes we might not want to (or be able) to use all the nice methods for automatic service discovery etc.

In this post we’re going to create a simple .NET core console application and interact with the discovery service at a slightly lower level.

So…

  • Create yourself a .NET core console application
  • Add the NuGet package Steeltoe.Discovery.Eureka

We need to configure the discovery client. You can use your own configuration method but for this example we’ll hard code parameters into a EurekaClientConfig object.

var config = new EurekaClientConfig
{
   EurekaServerServiceUrls = "http://localhost:8761/eureka/",
   ShouldFetchRegistry = true,
   ShouldRegisterWithEureka = false
};

We’re just writing a client, hence we don’t want to register the application with Eureka, but we do want to fetch registry data.

Next we will create the discovery client, access the applications and get our weatherapi application…

var discoveryClient = new DiscoveryClient(config);
var applications = discoveryClient.Applications;
var service = applications?.GetRegisteredApplication("weatherapi");

Finally we want to find instances of the services associated with the application which are UP and get the URL associated with one of the instances.

var instance = service?.Instances.FirstOrDefault(info => info.Status == InstanceStatus.UP);
if(instance != null)                    
{
   var client = new HttpClient
   {
      BaseAddress = new Uri(instance.HomePageUrl)
   };
   var response = await client.GetAsync("weatherforecast");
   Console.WriteLine(await response.Content.ReadAsStringAsync());
}

If an instance is found we just simply use HttpClient to invoke a GET method on the service and output the response to the console, just to prove everything worked.

This code doesn’t use any load balancing strategies, I’ll leave that to the reader to look into as in a real world scenario we wouldn’t want all clients to use the first instance only.

Creating an ASP.NET client using Eureka and Steeltoe

In the previous post Eureka Server (using Steeltoe) revisited I went through the process of running a Eureka instance and creating a default template based ASP.NET Web API which registers itself with the Eureka server as the application weatherapi.

Let’s now create a basic ASP.NET MVC project to interact with the Eureka server, get an instance of the API service and use it.

Note: This post is almost 100% based on the Channel 9 video Service Discovery with Steeltoe, so credit should go to Tim Hess for the sample code.

  • Create an ASP.NET Core application and then select Web Application (Model View Controller)
  • Add the following NuGet packages, Steeltoe.Discovery.ClientCore, Steeltoe.Discovery.Eureka and System.Net.Http.Json
  • Update the appsettings.json with the following
      "eureka": {
        "client": {
          "serviceUrl": "http://locahost:8761/eureka/",
          "shouldFetchRegistry": "true",
          "shouldRegisterWithEureka": false,
          "validateCertificates": false
        }
      }
    

    Notice we set shouldFetchRegistry to true as we want to get the latest registry information, and we set shouldRegisterWidthEureka to false as, in this case, we don’t want to register this client. Ofcourse change this is your client also exposes services.

  • Within Startup.cs, ConfigureServices add the following
    services.AddDiscoveryClient(Configuration);
    services.AddHttpClient("weather", 
       client => client.BaseAddress = new Uri("http://weatherapi/"))
          .AddServiceDiscovery();
    

    The interesting thing here is that we associate the name “weather” with an address which looks like a URL but really is the name of the service within Eureka that we want to access. Then, by using the AddServiceDiscovery this will be converted to an instance URL representing the instance of the service associated with the app name.

    Note: we can also use load balancing strategies, such as round robin or random.

  • Finally within the Startup.cs, method Configure, add the following
    app.UseDiscoveryClient();
    
  • We’re going to simply copy the WeatherForecast class from the service and add to the client, here it is

    public class WeatherForecast
    {
       public DateTime Date { get; set; }
       public int TemperatureC { get; set; }
       public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
       public string Summary { get; set; }
    }
    
  • Within HomeController.cs we need to add a variable of type IHttpClientFactory which will be injected into the constructor and then uses to call defined HTTP client, this will then used service discovery to return the URL of an instance (as already discussed) and then we’re use that to call the weather API to get a list of values. Here’s the changes required to HomeController.cs
    private readonly IHttpClientFactory _httpClientFactory;
    
    public HomeController(IHttpClientFactory httpClientFactory, ILogger<HomeController> logger)
    {
       _httpClientFactory = httpClientFactory;
       _logger = logger;
    }
    
    public async Task<IActionResult> Index()
    {
       var client = _httpClientFactory.CreateClient("weather");
       var weather = await client.GetFromJsonAsync<IList<WeatherForecast>>("weatherforecast");
       return View(weather);
    }
    
  • Finally, let’s change the Index.cshtml to display the weather forecast data returned in the Index method. Firstly we declare the @model and then simply create a table to output the items from that model in columns and rows, so here’s the change to be added to the top of the file
    @model IList<EurekaWebClient.Controllers.WeatherForecast>
    

    and within the main div, just add the following

    <table class="table">
       <tr><th>Day of Week</th><th>Summary</th><th>Temp</th></tr>
       @foreach (var weather in Model)
       {
          <tr><td>@weather.Date.DayOfWeek</td><td>@weather.Summary</td><td>@weather.TemperatureF</td></tr>
       }
    </table>
    

That should be it. Ensure Eureka is running, your service is up and you should now see the weather service data in a nice little table.