# Fixing C# reflection which was broken by upgrade to .NET 6

# The code that was broken by .NET 6 upgrade

This code was in a .NET Core 3.1 app and was working fine:

```csharp
public void GetTranslation(Type translationType)
{
    // lots of code skipped

    var firstOrDefaultMethod = typeof(Enumerable).GetMethods().First(m =>
        m.Name == nameof(Enumerable.FirstOrDefault) && 
        m.GetParameters().Length == 2);
    var typedFirstOrDefaultMethod = firstOrDefaultMethod.MakeGenericMethod(translationType);
    var firstOrDefaultExpression = Expression.Call(
        typedFirstOrDefaultMethod,
        translationsObjectExpression, // created earlier in method
        translationFilterLambda); // created earlier in method

    // more code skipped
}
```

I updated the app to .NET 6 and it compiled fine, but at runtime an exception was thrown by `Expression.Call`.

> System.ArgumentException: Expression of type 'System.Func\`2[MyProject.Translation,System.Boolean]' cannot be used for parameter of type 'MyProject.Translation' of method 'MyProject.Translation FirstOrDefault\[Translation](System.Collections.Generic.IEnumerable\`1[MyProject.Translation], MyProject.Translation)' (Parameter 'arg1')

This error baffled me for a while. How could the code have broken? It was already working in production, and I hadn't changed any of the code. I eventually tracked the root cause down to [this breaking change](https://docs.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/additional-linq-queryable-method-overloads) in .NET 6.

# Why this code doesn't work in .NET 6

The key problem is in this line:

```csharp
var firstOrDefaultMethod = typeof(Enumerable).GetMethods().First(m =>
    m.Name == nameof(Enumerable.FirstOrDefault) && 
    m.GetParameters().Length == 2);
```

This code makes an assumption about the overloads for `Enumerable.FirstOrDefault`, namely that there would only be a single overload with two parameters. That assumption was correct up to .NET 5, but .NET 6 introduced some new overloads. You can see this by comparing the [.NET 5 overloads](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.firstordefault?view=net-5.0#overloads) with the [.NET 6 overloads](https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.firstordefault?view=net-6.0#overloads). Significantly, there are now 2 overloads which have 2 parameters.

# Solution

The breaking change documentation tells us the solution to this problem:

> ensure that your reflection code is tolerant of method overload additions. For example, use a Type.GetMethod overload that explicitly accepts the method's parameter types.

> Source: https://docs.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/additional-linq-queryable-method-overloads#recommended-action

Remember that the fundamental problem was that the original code assumed there would only be a single overload with two parameters. The solution was therefore to more precisely get the correct `FirstOrDefault` overload.

This is the updated code to get `firstOrDefaultMethod`, which works in .NET 6 (as well as earlier versions):

```csharp
var genericParameter = Type.MakeGenericMethodParameter(0);
var enumerableType = typeof(IEnumerable<>).MakeGenericType(genericParameter);
var predicateType = typeof(Func<,>).MakeGenericType(genericParameter, typeof(bool));
var firstOrDefaultMethod = typeof(Enumerable).GetMethod(
    nameof(Enumerable.FirstOrDefault),
    new[] { enumerableType, predicateType });
```

Note the change to using `GetMethod` rather than `GetMethods`. This allows us to specify an array of specific types which the method takes as parameters. Knowing the exact parameter types allows the code to uniquely identify the correct overload.

# Further reading

I have written about the role of unit testing in this bug fix, you can read about that in the following article:

%[https://tjhilton.hashnode.dev/unit-testing-triumphs-verifying-experimental-bug-fix]
