sebnilsson.com | Liquid Development Is What I Do
Seb Nilsson

LINQ Distinct-Method using Lambda Expression

If you've ever wanted to filter a collection for a distinct result you probably know about the extension-method .Distinct in LINQ. It can be useful on simple data structures containing easily comparable objects, like I collection of strings or ints, but for more complex scenarios you need to pass in a IEqualityComparer. This is not very convenient.

More convenient would be to able to pass in a Lambda-expression, specifying what field you want to do the distinction by, like this:

var distinctItems = items.Distinct(x => x.Id);

To do this, you can add the following extension-methods to your projects:

public static IQueryable<TSource> Distinct<TSource>(
    this IQueryable<TSource> source, Expression<Func<TSource, object>> predicate)
{
    // TODO: Null-check arguments
    return from item in source.GroupBy(predicate) select item.First();
}

public static IEnumerable<TSource> Distinct<TSource>(
    this IEnumerable<TSource> source, Func<TSource, object> predicate)
{
    // TODO: Null-check arguments
    return from item in source.GroupBy(predicate) select item.First();
}

The extension-method using IQueryable<T> works with ORMs like Entity Framework, while IEnumerable<T> works with all types of collections, in-memory or otherwise, depending on implementation.

Warning: Avoid using this with EF Core version 1.x or 2.0, since the .GroupBy-execution is always made in-memory. So you might get the whole content of your database loaded into memory. Only use it with EF Core 2.1 and above in production-scenarios.

API-Versioning in ASP.NET

The ASP.NET Route Versions-library was created after I was inspired by a discussion with a colleague and reading the great article Your API versioning is wrong by Troy Hunt, where he concludes that you don't need a war of preferences between different ways of versioning your API, you can actually support multiple ways in the same API.

In his article, Troy lists 3 ways (to do it wrong), which I have implemented for ASP.NET Core, and added support for one more way, which is URL versioning. This library supports the following ways to version your API:

  • URL versioning
  • Query string versioning
  • Custom request header
  • Content type

URL versioning:

HTTP GET:
https://my-web-app.com/api/v2/customers

Query string versioning:

HTTP GET:
https://my-web-app.com/api/customers?api-version=2

Custom request header:

HTTP GET:
https://my-web-app.com/api/customers
api-version: 2

Content type:

HTTP GET:
https://my-web-app.com/api/customers
Accept: application/vnd.api-version.v2+json

[RouteVersion]-attribute

All you need to do is use the [RouteVersion]-attribute on the Controller-Actions you want to version and provide the route-version as an argument:

[Route("api/v{api-version}/[controller]")]
[Route("api/[controller]")]
[ApiController]
public class CustomersController : ControllerBase
{
    [HttpGet]
    [RouteVersion(1)]
    public ActionResult<string> GetV1()
    {
        return "Get Customers Version 1";
    }

    [HttpGet]
    [RouteVersion(2)]
    public ActionResult<string> GetV2()
    {
        return "Get Customers Version 2";
    }

    [HttpPost]
    [RouteVersion(1)]
    public ActionResult<string> PostV1()
    {
        return "Post Customers Version 1";
    }

    [HttpPost]
    [RouteVersion(2)]
    public ActionResult<string> PostV2()
    {
        return "Post Customers Version 2";
    }
}

The attribute will only resolve versioning between Controller-Actions, everything else is handled by the regular ASP.NET Core routing, and behave as you're used to.

Configuration

In your Startup.cs you can configure what ways of API-versioning you want to support (all activated by default). You can also change the keys of the routing, query string, custom header and content type.

public void ConfigureServices(IServiceCollection services)
{
    services.ConfigureRouteVersions(options =>
    {        
        options.UseRoute = true;
        options.UseQuery = true;
        options.UseCustomHeader = true;
        options.UseAcceptHeader = true;

        // Set route-name in template used. For example: "api/v{version}/[controller]"
        // Default: "api-version"
        options.RouteKey = "version";

        // Set query string-key used. For example: "/api/customers?v=1"
        // Default: "api-version"
        options.QueryKey = "v"; // To use: '/api/customers?v=1'

        // Set custom version header used. For example: "my-app-api-version"
        // Default: "api-version"
        options.CustomHeaderKey = "my-app-api-version";

        // Set Accept-header vendor used. For example: "application/vnd.my-custom-api-header.v1+json"
        // Default: "application/vnd.api-version.v1+json"
        options.SetAcceptHeader("my-custom-api-header");

        // Set Accept-header regex-pattern. For example: "application/pre.my-custom-vendor-api.v1+json"
        options.AcceptRegexPattern = @"application\/pre\.my-custom-vendor-api\.v([\d]+)\+json";
    });

    services.AddMvc();
}

Default version

If you know that your new version of an API-endpoint is compatible with previous version, and if you want to support it, you can use the IsDefault-parameter with the [RouteVersion]-attribute. For example, if you've just added new fields to the next version and you find that is compatible enough to be the default version of the API-endpoint:

[Route("api/[controller]")]
[ApiController]
public class CustomersController : ControllerBase
{
    [HttpGet]
    [RouteVersion(1)]
    public ActionResult<string> GetV1()
    {
        return "Get Customers Version 1";
    }

    [HttpGet]
    [RouteVersion(2, IsDefault = true)]
    public ActionResult<string> GetV2()
    {
        return "Get Customers Version 2";
    }
}

Then you can make a call to the URL for the API-endpoint without specifying the version and get the default version, which in this example is v2:

HTTP GET:
https://my-web-app.com/api/customers/
> "Get Customers Version 2"

Contributing

You can find the source code on GitHub, the newest unstable build on MyGet and the latest version of the library on NuGet

Azure Deployment of Mono-Repos in GitHub

With the increasing adoption of Microservices, one of the questions sometimes discussed is mono-repo vs. multi-repo, which tries to answer if you structure your code to have one large, single repository for all/multiple microservices or individual, small repositories for each microservice.

If you're in the scenario of using a mono-repo, where you have multiple projects in a single repo, and you want to deploy this single code-base to multiple applications and services in Azure, there is a simple way to do this.

Kudu, the engine behind git-deployments in Azure App Service, has documentation on how to customize deployments, but only one of these ways works well with the mono-repo-scenario. The approach of using a .deployment-file unfortunately locks the configuration of the specified project into your source-code, so it won't work for mono-repos.

Instead, using the Application settings available in all Azure App Services, you use the key PROJECT to set the value for the path of the project you want to be deployed to your different Azure App Services. So this solution works for Function Apps, Web Apps, API Apps and much more.

Example

If you have the following code-structure in your single repository:

  • /src/ExampleProject/ExampleProject.csproj
  • /src/ExampleProject.FunctionsApp/ExampleProject.FunctionsApp.csproj
  • /src/ExampleProject.WebApiApp/ExampleProject.WebApiApp.csproj
  • /src/ExampleProject.WebApp/ExampleProject.WebApp.csproj
  • /test/ExampleProject.Tests/ExampleProject.Tests.csproj

Then you'd set up your PROJECT-path in Application settings for your different Application Services like this:

  • Function App: "src/ExampleProject.FunctionsApp"
  • Web App: "src/ExampleProject.WebApp"
  • API App: "src/ExampleProject.WebApiApp"

With the paths relative to the Git-repository's root-path. You can also point directly to the .csproj-file, if there is any risk of ambiguity in the same file-folder.

When this is done you can configure your Azure App Services for Continuous Deployment using the same repository, in GitHub, Bitbucket, local Git or any other source supported in Azure.

Convert C# Anonymous (or Any) Types Into Dynamic ExpandoObject

There are scenarios when you need to convert an anonymous type in C#, or any other type, into a dynamic, or more specifically to the underlying ExpandoObject-type.

My specific need was to be able to move the data from an anonymous type from the current assembly into a dynamically executed external assembly. So I created an extension-method for this, which moves all the properties from any object into a new ExpandoObject.

public static ExpandoObject ToExpandoObject(this object obj)
{
    // Null-check

    IDictionary expando = new ExpandoObject();

    foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(obj.GetType()))
    {
        expando.Add(property.Name, property.GetValue(obj));
    }

    return (ExpandoObject) expando;
}

You can use the extension-method on any type of object and choose to reference the resulting type by it's actual type ExpandoObject or as a dynamic.

var anonymous = new {Id = 123, Text = "Abc123", Test = true};

dynamic dynamicObject = anonymous.ToExpandoObject();
ExpandoObject expandoObject = anonymous.ToExpandoObject();

Since the code uses the type System.ComponentModel.TypeDescriptor, if you use .NET Core or .NET Standard, you might need to reference the Nuget-package named System.ComponentModel.TypeConverter.

KeyLocks: Lock .NET/C#-code on Specific Values

If you've ever needed to ensure that multiple threads are not running the same code, you've probably used a lock-statement in your .NET/C#-code.

Sometimes a regular lock can be too aggressive and lock too much running code for too long. You can solve this by cleverly locking on unique objects, but that handling is complex, error-prone and can become tedious.

Many times you know that you have a specific value or ID which is the key you want to lock on. For instance, you might want your code to not write to the database from multiple actions performed in parallel on your web-application. Using the Nuget-package KeyLocks will give you easy to write syntax to handle this:

private static KeyLock<string> _keyLock = new KeyLock<string>();

public void Main()
{
    Parallel.Invoke(
        () => { UpdateData("entity-123", "First new value"); },
        () => { UpdateData("entity-123", "Second new value"); }, // This will await line above
        () => { UpdateData("another-entity-456", "Another new value"); },        
        () => { UpdateData("yet-another-entity-789", "Yet another new value"); });
}

private void UpdateData(string id, string value)
{
    _keyLock.RunWithLock(id, () =>
    {
        // Execute locked code
    });
}

Make sure the instance of KeyLock<T> is shared between threads executing the code you want to lock on. In this case, I solved it by making the instance static and therefore shared across all instances of the code using it.

The package also contains the type NameLock is a short-hand term for KeyLock<string>. It defaults to being case-sensitive, but that can be changed by passing the correct IEqualityComparer<T> as a constructor-argument like this:

var nameLock = new NameLock(StringComparer.InvariantCultureIgnoreCase)

See the README-file on GitHub for the latest detailed information about KeyLocks. Or try it out through Nuget by running Install-Package KeyLocks in your project. You can even follow the absolutely latest build on MyGet.