Category Archives: Programming

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.

Eureka Server (using Steeltoe) revisited

The Eureka server is used as a registry for maintaining lists of services and their endpoints. It’s used a lot in the microservice world by way of a microservice registering it’s existence somewhere (in this case in Eureka). When a client (whether it’s another service or anything else for that matter) want to access a service it asks the registry for an instance, in this case we connect to the Eureka server and find the services instance(s) that are available for a specific application name and are ofcourse UP.

Installing and running Eureka Server

In a previous post Spring boot Eureka server we wrote a Java application to run a Eureka server. In this post we’re going to use Docker to host the server and then use Steeltoe with .NET to interact with the instance.

To get an image of Eureka server, let’s use the Steeltoe docker image (Spring and others exist, the Steeltoe image is not intended for production, but is fine for what we want to do)

docker pull steeltoeoss/eureka-server

Now run up the docker image using

docker run --publish 8761:8761 steeltoeoss/eureka-server

If all goes well, connect to the Spring Eureka dashboard using

http://locahost:8761

change localhost to the ip/host you’re running the Eureka server from, now you should now see the Spring Eureka web page.

Registering a .NET core client with Eureka

I’ve a post on this topic A .NET service registered with Eureka, but let’s go through the process again with the current version of Steeltoe (as the NuGet packages have changed somewhat).

  • Create an ASP.NET Core Web Application, as this will represent a REST service that we want to interact with. My project is called RegisterExample and is an API project.
  • Add the NuGet package Steeltoe.Discovery.ClientCore and Steeltoe.Discovery.Eureka
  • In Startup.cs within ConfigureServices add the following
    services.AddDiscoveryClient(Configuration);
    
  • Within the Configure method add the following
    app.UseDiscoveryClient();
    
  • Finally add the following to the appsettings.json file
    // Eureka info
      "eureka": {
        "client": {
          "serviceUrl": "http://localhost:8761/eureka/",
          "shouldFetchRegistry": "false",
          "shouldRegisterWithEureka": true,
          "validateCertificates": false
        },
        "instance": {
          "appName": "weatherapi",
          "port": "8080",
          "ipAddress": "localhost",
          "preferIpAddress": true
        }
      }
    

We’re going to leave the API with the default weatherapi, hence the appName in the appsettings.

Notice we set the value for “shouldFetchRegistry” to false as this service will not be acting as a client to any other services. Obviously change this is you also need to discovery other services. “shouldRegisterWithEureka” is set to true as we want this service to automatically register itself with Eureka.

Now navigate to the URL of your Eureka server again (or refresh) and you should see a new Application. In my case I have an application with the name weatherapi. This name comes from our appsettings.json configuration application/name.

Info and Health

If you click on the instance link within the Eureka server dashboard, you will navigate to

https://localhost:5001/info

(or whatever ip/hostname and port your service is running on) you get a 404, so let’s fix that in our project.

Info, by default will display some basic information about the application, product version etc. However you can also add further custom information if you want, but example the git build SHA1 hash or just some general info.

  • Add NuGet package Steeltoe.Management.EndpointCore
  • In Startup.cs ConfigureServices, add the following
    services.AddSingleton<IInfoContributor, MyInfoContributor>();
    services.AddInfoActuator(Configuration);
    services.AddHealthActuator(Configuration);
    

    The first of these lines will add our implementation of an IInfoContributor to allow for custom info.

  • Still in Startup.cs, but now the method Configure, add the following to the UseEndpoints endpoint routes

    endpoints.Map<InfoEndpoint>();
    endpoints.Map<HealthEndpoint>();
    
  • Now we’ll create a simple implementation of and IInfoContributor which allows us to add our own info, so add the following class

    public class MyInfoContributor : IInfoContributor
    {
       public void Contribute(IInfoBuilder builder)
       {
          builder.WithInfo("MyInfo", new { SomeName = "Scooby Doo" });
       }
    }
    

Now when we run our service we hope to see our info data, however by default Steeltoe seems to set the info and health endpoint to /actuator/info and /actuator/health respectively. Eureka seems to expect /info. So go to the appsettings.json and add the following to the Instance section

"StatusPageUrlPath": "/actuator/info",
"HealthCheckUrlPath": "/actuator/health" 

Note: I’m not sure what I’m missing here and why the defaults don’t match up, but these configuration changes will tell the Eureka server (when we register our service with it) that it should use these paths for info and health.

Now, if you run the service again for /actuator/info you should see something like this

{"MyInfo":{"someName":"Scooby Doo"},
"applicationVersionInfo":{"ProductName":"RegisterExample",
"FileVersion":"1.0.0.0","ProductVersion":"1.0.0"},
"steeltoeVersionInfo":"ProductName":"Steeltoe.Management.Endpoint",
"FileVersion":"3.0.2.0","ProductVersion":
"3.0.2\u002B4089779c66d127f40325a3be9b613149b3b090f2"}}

and health something like

{"status":"UP","details":{"liveness":{},"diskSpace":{"total":4000769372160,
"free":3889734168576,"threshold":10485760,"status":"UP"},"eurekaServer":
{"remoteInstStatus":"UNKNOWN","fetchStatus":"Not fetching","heartbeat":
"Successful","heartbeatStatus":"UP","heartbeatTime":"2021-01-23T20:40:44",
"status":"UP","applications":"NONE"},"readiness":{}}}

with and record types in C# 9.0

In the previous post we looked at the new C# record type. Whilst we can make record types mutable, they’re particularly well suited to immutability. When used as immutable types we need a way to make new immutable types based upon previous ones.

Let’s use our Person record type from the previous post but sprinkled with C# 9 goodness in the form of init

public record Person
{
   public string FirstName { get; init; }
   public string LastName { get; init; }
}

The init syntax gives us the ability to create instances of a Person, assigning values during the construction phase of the record type using standard initialiser syntax.

For example

var p = new Person 
{ 
   FirstName = "Scooby", 
   LastName = "Doo" 
};

However, this post is mean’t to be about the with keyword with allows us to take a record type and create a new instance baed upon and existing record with changes. i.e. we want to take the Person p and create a new record with the FirstName = “Scrappy”, like this

var scrappy = p with { FirstName = "Scrappy" };

the result of outputting this to the console would be

Person { FirstName = Scrappy, LastName = Doo }

C# 9.0 record type

As of yesterday I got the updated version of Visual Studio 2019 (16.8.1) which includes .NET 5 and C# 9.0 – I don’t intend to post about every feature of C# 9.0 – for those interested simply go and take a look at What’s new in C# 9.0.

One feature that’s quite interesting is C# Records.

Record types “are a reference type that provides synthesized methods to provide value semantics for equality”. So basically we can create multiple instances of a record and compare them for equality. Here’s an example of a record type

public record Person
{
   public Person(string firstName, string lastName)
   {
      FirstName = firstName;
      LastName = lastName;
   }

   public string LastName { get; }
   public string FirstName { get; }
}

It’s that easy, we simply declare our type as a record instead of a class.

Now, if we create a couple of instances of a Person that look like this

Person person1 = new("Scooby", "Doo");
Person person2 = new("Scooby", "Doo");

and we compare them using ==/Equals, they will result in the two instances being the same (unlike with classes with would compare equality by reference).

Another feature of records, over classes is. If you use ToString() on an instance of a class you’ll see something like

TestApp.Person

For a record type you’ll instead get

Person { LastName = Doo, FirstName = Scooby }

this is facilitated by the compiler adding a PrintMembers protected method which generates this output.

ANTLR in C#

In the previous post Starting out with ANTLR we look at the basics of creating a grammar and generating code from it, now let’s take that very simple grammar and integrate it into a C# application.

Here’s the grammar again (from our grammar file queryLanguage.g4)

Note: We’re going to capitalize the grammar name as this will then by more in the style of C# class names.

grammar QueryLanguage;

query
    : expression
    ;

expression
    : STRING
    | NUMBER
    | expression 'AND' expression
    | expression 'OR' expression
    ;

WS  : (' '|'\t'|'\r'|'\n')+ -> skip;

STRING : '"' .*? '"';
SIGN
   : ('+' | '-')
   ;
NUMBER  
    : SIGN? ( [0-9]* '.' )? [0-9]+;

The ANTLR4 JAR is not compatible with the ANTRL4 Nuget package, so instead for our Example application we’ll use an alternative, the Antlr4 CodeGenerator, so follow these steps to create an application

  • Create a .NET Core Console application
  • Editor the SDK project file and change netcoreapp3.1 to net472
  • Add the ANTLR4.Runtime and Antlr4.CodeGenerator Nuget packages
  • Add your QueryLanguage.g4 grammar to the project

If you select the .g4 file you can now view the properties for that file within Visual Studio 2019 and (if you wish to) change what’s generated by ANTLR. Let’s just ensure Generate Visitor is Yes.

For some reason a .NET framework 4.7.2 project does not include the properties and whilst we can edit the .csproj file to get things working, I’ve found the above steps the simplest to get ANTLR running in a .NET application at the time of writing.

I’ve found I do still need to edit the .csproj file to add the following

<ItemGroup>
  <Antlr4 Update="QueryLanguage.g4">
    <Listener>false</Listener>
    <CustomToolNamespace>Example.Generated</CustomToolNamespace>
  </Antlr4>
</ItemGroup>

<PropertyGroup>
  <Antlr4UseCSharpGenerator>True</Antlr4UseCSharpGenerator>
</PropertyGroup>

Change Example.Generated to the preferred namespace for the generated files.

Now build the project and if all goes well there should be no errors and the ANTLR code should be generated in obj/Debug/net472 (or whatever configuration you’re using).

Let’s now make some changes to our grammar to make writing Visitor code simpler by adding labels to our expression code, the changes are listed below

expression
    : STRING #String
    | NUMBER #Number
    | expression 'AND' expression #And
    | expression 'OR' expression  #Or
    ;

We use # to create a label and this will turn into a Visit function with the label, i.e. VisitAnd, VisitoOr etc.

All we’re going to do with this grammar is use the Visitor pattern/class to generate code where strings are all lowercase, AND becomes & and OR becomes |, ofcourse you could produce byte code or do all sorts of things with your input.

Create a new file name QueryLanguageVisitor.cs and it should look like this

using Example.Generated;

namespace Example
{
  public class QueryLanguageVisitor : QueryLanguageBaseVisitor<string>
  {
    public override string VisitString(QueryLanguageParser.StringContext context)
    {
      return context.GetText().ToLower();
    }

    public override string VisitNumber(QueryLanguageParser.NumberContext context)
    {
      return context.GetText();
    }

    public override string VisitAnd(QueryLanguageParser.AndContext context)
    {
      return Visit(context.expression(0)) + "&" + Visit(context.expression(1));
    }

    public override string VisitOr(QueryLanguageParser.OrContext context)
    {
      return Visit(context.expression(0)) + "|" + Visit(context.expression(1));
    }
  }
}

As you can see from the above code, we subclass QueryLanguageBaseVisitor (a generated file) and the generic parameter is set as a string as our result of running through the QueryLanguageVisitor will simply be another string.

In the case of the AND and OR which ofcourse are binary expressions, i.e. require two parameters either side of the AND or OR and these may themselves be expression, hence we call Visit those expressions.

At this point, we have nothing to actually run the QueryLanguageVisitor so in the Main method place the following code

// add these using clauses
// using Antlr4.Runtime;
// using Example.Generated;

// example expression
var expression = "\"HELLO\" AND 123";

var inputStream = new AntlrInputStream(expression);
var lexer = new QueryLanguageLexer(inputStream);
var tokenStream = new CommonTokenStream(lexer);
var parser = new QueryLanguageParser(tokenStream);

var visitor = new QueryLanguageVisitor();
var query = parser.query();
var result = visitor.Visit(query);

In the code above, we create an ANTLR input stream (you can ofcource use an AntlrFileStream if you’re taking input from a file). Next we use our generated lexer which is passed into the CommonTokenStream and this in turn is passed into our generated QueryLanguageParser.

Finally we create our newly added QueryLanguageVisitor which will have functions based upon our grammar, in our case the startRule is query hence we call this method and pass the result into the Visit method of our QueryLanguageVisitor. The result (assuming no errors) will be

"hello" & 123

A more fully featured (i.e. includes error handling) implementation would be as follows (concepts and code snippets taken from various existing samples)

public class ParserResult
{
  public bool IsValid { get; internal set; }
  public int ErrorPosition { get; internal set; } = -1;
  public string ErrorMessage { get; internal set; }
  public string Result { get; internal set; }
}

public static class Query
{
  public static ParserResult Parse(string expression, bool secondRun = false)
  {
    if (String.IsNullOrWhiteSpace(expression))
    {
      return new ParserResult
      {
        IsValid = true,
        Result = null
      };
    }

    var inputStream = new AntlrInputStream(expression);
    var lexer = new QueryLanguageLexer(inputStream);
    var tokenStream = new CommonTokenStream(lexer);
    var parser = new QueryLanguageParser(tokenStream);

    lexer.RemoveErrorListeners();
    parser.RemoveErrorListeners();
    var customErrorListener = new QueryLanguageErrorListener();
    parser.AddErrorListener(customErrorListener);
    var visitor = new QueryLanguageVisitor();

    var queryExpression = parser.query();
    var result = visitor.Visit(queryExpression);
    var isValid = customErrorListener.IsValid;
    var errorLocation = customErrorListener.ErrorLocation;
    var errorMessage = customErrorListener.ErrorMessage;
    if (result != null)
    {
      isValid = false;
    }

    if (!isValid && !secondRun)
    {
      var cleanedFormula = string.Empty;
      var tokenList = tokenStream.GetTokens().ToList();
      for (var i = 0; i < tokenList.Count - 1; i++)
      {
        cleanedFormula += tokenList[i].Text;
      }
      var originalErrorLocation = errorLocation;
      var retriedResult = Parse(cleanedFormula, true);
      if (!retriedResult.IsValid)
      {
        retriedResult.ErrorPosition = originalErrorLocation;
        retriedResult.ErrorMessage = errorMessage;
      }
      return retriedResult;
    }
    return new ParserResult
    {
      IsValid = isValid,
      Result = isValid || result != null 
        ? result
        : null,
      ErrorPosition = errorLocation,
      ErrorMessage = isValid ? null : errorMessage
    };
  }
}

public class QueryLanguageErrorListener : BaseErrorListener
{
  public bool IsValid { get; private set; } = true;
  public int ErrorLocation { get; private set; } = -1;
  public string ErrorMessage { get; private set; }

  public override void ReportAmbiguity(
    Parser recognizer, DFA dfa, 
    int startIndex, int stopIndex, 
    bool exact, BitSet ambigAlts, 
    ATNConfigSet configs)
  {
    IsValid = false;
  }

  public override void ReportAttemptingFullContext(
    Parser recognizer, DFA dfa, 
    int startIndex, int stopIndex, 
    BitSet conflictingAlts, SimulatorState conflictState)
  {
    IsValid = false;
  }

  public override void ReportContextSensitivity(
    Parser recognizer, DFA dfa, 
    int startIndex, int stopIndex, 
    int prediction, SimulatorState acceptState)
  {
    IsValid = false;
  }

  public override void SyntaxError(
    IRecognizer recognizer, IToken offendingSymbol, 
    int line, int charPositionInLine, 
   string msg, RecognitionException e)
 {
   IsValid = false;
   ErrorLocation = ErrorLocation == -1 ? charPositionInLine : ErrorLocation;
   ErrorMessage = msg;
 }
}

Now the code that uses our parser simply looks like this (and includes error handling)

var expression = "\"HELLO\" AND 123";
var result = Query.Parse(expression);

Generating IL using C#

Note: This is an old post I had sitting around for a couple of years, I’m not sure how complete or useful it is, but better being published than hidden away and it might be of use at some point.

There are different ways to dynamically generate code for .NET, using tools such as T4, custom code generators run via target builds etc. Then there’s creating your assembly, modules, types etc. via IL. I don’t mean literally write IL files but instead generating your IL via C# using the ILGenerator class and Emit methods.

I wanted to write a factory class that worked a little like Refit in that you define the interface and Refit “magically” creates an implementation to the interface and calls boilerplate code to inject and/or do the work required to make the code work.

Refit actually uses build targets and code generation via GenerateStubsTask and InterfaceStubGenerator not IL generation.

IL is not really a simple way to achieve these things (source generators, currently in previous, would be far preferable) but maybe in some situations IL generation suits your requirements and I thought it’d be an interesting thing to try anyway.

Use case

What I want to do is allow the developer to create an interface which contains methods (we’re only going to support “standard” methods at this point). The methods may take multiple arguments/parameters and must return Task (for void) or Task of T (for return values). Just like Refit, the idea would be that the developer marks methods in the interface with attributes which then tell the factory class what code to generate for the implementation.

All very much along the lines of Refit.

Starting things off by creating our Assembly

We’re going to need to create an Assembly, at runtime, to host our new types, so the first thing we do is, using the domain of the current thread we’ll use the method DefineDynamicAssembly, pass in both an AssemblyName and AssemblyBuilderAccess parameter which creates an AssemblyBuilder. This becomes the starting point for the rest of our builders and eventually our IL code.

Note: If you want to save the assembly to disk, which is very useful for debugging by inspecting the generated code using ILSpy or the likes, then you should set the AssemblyBuilderAccess to AssemblyBuilderAccess.RunAndSave and supply the file path (not the filename) as the fourth argument to DefineDynamicAssembly.

Before we get into this code further, let’s look at a simple interface which will be our starting point.

public interface IService
{
   Task<string> GetName();
}

Whilst the aim, eventually, is to include attributes on our interface and return different generic types, for this post we’ll not get into this, but instead simply generate an implementation which ignores the arguments passed and expects either a return of Task or Task<string>.

Let’s create our assembly – here’s the code for the TypeGenerator class.

public class TypeGenerator
{
   private AssemblyBuilder _assemblyBuilder;
   private bool _save;
   private string _assemblyName;

   public TypeGenerator WithAssembly(string assemblyName, string filePath = null)
   {
      var currentDomain = Thread.GetDomain();
      _assemblyName = assemblyName;
      _save = !String.IsNullOrEmpty(filePath);

      if (_save)
      {
         _assemblyBuilder = currentDomain.DefineDynamicAssembly(
            new AssemblyName(_assemblyName),
               AssemblyBuilderAccess.RunAndSave,
                  filePath);
      }
      else
      {
         _assemblyBuilder = currentDomain.DefineDynamicAssembly(
            new AssemblyName(_assemblyName),
               AssemblyBuilderAccess.Run);
      }
      return this;
   }

   public static TypeGenerator Create()
   {
      return new TypeGenerator();
   }
}

The code above will not actually save the assembly but is part of the process we need to go through to actually save it. Let’s add a save method which will actually save the assembly to disk.

public TypeGenerator Save()
{
   if (!String.IsNullOrEmpty(_assemblyName))
   {
      _assemblyBuilder.Save(_assemblyName);
   }
   return this;
}

Note: we’ll also need to assign the assembly name to the Module which we’re about to create.

Now we need a Module

Creating the module is simply a case of calling DefineDynamicModule on the AssemblyBuilder that we created, this will give us a ModuleBuilder which is where we’ll start generating our type code.

As noted, if we are saving the module then we also need to assign it the assembly name, so here’s the code for creating the ModuleBuilder

public TypeGenerator WithModule(string moduleName)
{
   if (_save)
   {
      _moduleBuilder = _assemblyBuilder.DefineDynamicModule(
         moduleName, _assemblyName);
   }
   else
   {
      _moduleBuilder = _assemblyBuilder.DefineDynamicModule(
         moduleName);
   }
   return this;
}

Creating our types

Considering this post is about IL code generation, it’s taken a while to get to it, but we’re finally here. We’ve created the assembly and within that a module. Our current implementation for generating a type will take the interface as a generic parameter (only interfaces will be handled), here’s the method

public TypeGenerator WithType<T>()
{
   var type = typeof(T);

   if (type.IsInterface)
   {
      EmitTypeFromInterface(type);
   }

   return this;
}

The EmitTypeFromInterface will start by defining a new type using the ModuleBuilder. We’ll create a name based upon the interface type’s name. Obviously the name needs to be unique. To make things simple we’ll just prefix the text “AutoGenerated”, hence type IService will become implementation AutoGeneratedIService. We’ll also need to set up the TypeAttributes to define our new type as a public class and in our case ensure the new type extends the interface. Here’s the code to generate a TypeBuilder (and also create the constructor for the class)

private void EmitTypeFromInterface(Type type)
{
   _typeBuilder = _moduleBuilder.DefineType($"AutoGenerated{type.Name}",
      TypeAttributes.Public |
      TypeAttributes.Class |
      TypeAttributes.AutoClass |
      TypeAttributes.AnsiClass |
      TypeAttributes.BeforeFieldInit |
      TypeAttributes.AutoLayout,
      null, new[] { type });


   var constructorBuilder =
      _typeBuilder.DefineDefaultConstructor(
         MethodAttributes.Public |
         MethodAttributes.SpecialName |
         MethodAttributes.RTSpecialName);

   // insert the following code snippets here
}

Implementing our methods

Obviously an interface requires implementations of it’s methods – yes you can actually save the assembly without supplying the methods and will get a TypeLoadException stating that the new type does not have an implementation for the method.

In the code below we’ll look through the methods on the interface type and using the TypeBuilder we’ll create a MethodBuilder per method which will have the same name, return type and parameters and will be marked as public and virtual, from this we’ll finally get to emit some IL using the ILGenerator. Here’s the code

foreach (var method in type.GetMethods())
{
   var methodBuilder = _typeBuilder.DefineMethod(
      method.Name,
      MethodAttributes.Public |
      MethodAttributes.Virtual,
      method.ReturnType,
      method.GetParameters().Select(p => p.ParameterType).ToArray());

   var ilGenerator = methodBuilder.GetILGenerator();

   // IL Emit code goes here
}

A very basic overview or writing IL code

We can generate IL code using an ILGenerator and Emit methods from a C# application (for example). We can also write IL directly as source code files. For example, create a file test.il

Now add the following code

.assembly MyAssembly
{
}

.method void Test()
{
.entrypoint
ret
}

The text preceded by the . are directives for the IL compiler (ILASM which comes with Visual Studio). Within the file we’ve firstly declared an assembly named MyAssembly. Whilst this file would compile without the .assembly, it will not run and will fail with a BadImageFormatException.

Next we define a method (using the .method directive) named Test. The .entrypoint declares this is the entry point to our application (as this will compile to an EXE). Hence unlike C# where we use Main as the entry point, any method may be the entry point but only one method may be marked as the entry point.

To create a correctly formed method we also need the last line code to be a ret.

If you now compile this file using

ilasm test.il

You might notice that ilasm outputs the warning Non-static global method ‘Test’, made static. Obviously in C# our entry method would normally be a static method. Simply add the keyword static as below

.method static void Test()
{
.entrypoint
ret
}

Let’s now turn this little IL application into the classic Hello World by calling the Console.WriteLine method.

If you’ve ever written any assembly code you’ll know we pass arguments to subroutines by placing the arguments on the stack and then the callee will pop the expected number of arguments. So to output a string, we’ll need to push it onto the stack – in this case we use ldstr which specifically handles strings.

Console.WriteLine is available in the System namespace within mscorlib, and to invoke a method we’ll need to call it specifying the overload (if any) to use along with a fully qualified name, hence our Test method becomes

.method static void Test() 
{
.entrypoint

ldstr "Hello World"
call void [mscorlib]System.Console::WriteLine(class System.String)
ret
}

The easiest way to learn IL is to look at decompilation from tools such as ildasm, ILSpy, Reflector or dotPeek, write code you wish to generate IL for, compile then decompile with one of these tools to see what’s going on.

Targeting multiple frameworks in .NET

In this post we’re going to look at the process of targeting multiple .NET frameworks.

You may have seen this concept when installing nuget packages, viewing the package itself and noting that packages have lib folders which (in some cases) have multiple names such as net35, net40 etc. Hence having assemblies which are compatible with those versions of .NET.

For example, the packages/Castle.Core.4.2.1/lib folder contains

  • net35
  • net40
  • net45
  • netstandard1.3

The names are based upon, what’s called, the TargetFrameworkMoniker (or TFM for short), see Target frameworks in SDK-style projects for both a list of TFM’s as well as a Microsoft post of targeting multiple frameworks.

It looks like we need to be using the new SDK csproj files which we can create using either the dotnet CLI or by creating a .NET core or standard projects (or you can use conversion tools).

Let’s therefore walk through the steps for setting up a Class Library that can target a few different frameworks.

  • Create a .NET standard class library
  • Open the csproj file in Visual Studio or your preferred editor
  • Change the following
    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <TargetFramework>netstandard2.0</TargetFramework>
      </PropertyGroup>
    
    </Project>
    

    to use TargetFrameworks (plural) and add any frameworks you’re targeting delimited by a semi-colon, i.e.

    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <TargetFrameworks>netstandard2.0;net45</TargetFrameworks>
      </PropertyGroup>
    
    </Project>
    
  • Save and if necessary reload Visual Studio to see the effects of these changes.

If all went well you should see The Dependencies section of your project (within Visual Studio) lists both

  • .NETFramework 4.5
  • .NETStandard 2.0

If you now build your project you’ll get both net45 and netstandard2.0 folders/builds in the bin/Debug and bin/Release folders.

We can now either explicitly add assemblies or packages to our framework specific dependencies and ofcourse, by using preprocessor symbols such as NETSTANDARD2_0 and NETFRAMEWORK or NET45 we can begin to conditionally compiler code for the different target frameworks.

We can add nuget to install package and add references to our multiple targets, but in some circumstances we might need to be more specific about which package is referenced by which target.

In such cases we can add the reference to the application using Visual Studio and then edit the csproj ourselves to add a Condition around the reference, for example

<ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
  <PackageReference Include="Microsoft.Net.Http" Version="2.2.29" />
</ItemGroup>

In this example, only the .NET 3.4 target will get the reference added to it.

Now we’ve seen how to add conditions to our csproj we can also define constants for use also (or ofcourse using the project properties tab within Visual Studio to add these), for example

<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
   <DefineConstants>SOME_CONSTANT</DefineConstants>
</PropertyGroup>

References

Target frameworks in SDK-style projects
Support multiple .NET versions
.NET Standard

Converting old style csproj to new style sdk

This tool’s pretty good for converting the old style csproj files to the new SDK style.

Just install using

dotnet tool install --global Project2015To2017.Migrate2019.Tool

Now navigate to where your solution is (that you want to convert) and from the command prompt run

dotnet migrate-2019 wizard "D:\Path\To\My\TestProject.sln"

You’ll be asked if you want to backup things and whether you want to migrate, answer Y to the migrate question (backing up is up to you). This will then locate all the csproj’s in the solution and convert them.

Tests are failing with DateTime expected format

I’m creating some builds on appveyor for some of my GitHub projects and hit a small snag. All of the DateTime’s in my tests have test data in the en-GB format, i.e. dd/MM/yyyy.

Note: Whilst I haven’t checked, I suspect appveyor is hosted on en-US machines.

A simple way to resolve this is, just add a NUnit SetUp that converts the current culture to the expected culture, i.e.

SetUp]
public void SetUp()
{
   var culture = new CultureInfo("en-GB");
   Thread.CurrentThread.CurrentCulture = culture;
   Thread.CurrentThread.CurrentUICulture = culture;
}