PowerShell LINQ with Short Aliases

Most modern applications or code today deal with some kind of filtering or querying. In C# and .NET, we have Language Integrated Query (LINQ), which we also have access to in PowerShell, because it's built on .NET.

To list the top 10 largest files in the Windows temporary folder, which is larger than 1 Mb and starts with the letter W, skipping the first 5, ordering by size, the C#-code with LINQ would look somewhat like this:

new System.IO.DirectoryInfo(@"C:\Windows\Temp")
    .GetFiles()
    .Where(x => x.Length > 1024 && x.Name.StartsWith("W"))
    .OrderByDescending(x => x.Length)
    .Select(x => new { x.Name, x.Length })
    .Skip(5)
    .Take(10)
    .ToList()
    .ForEach(x => Console.WriteLine($"{x.Name} ({x.Length})"));

The equivalent logic in PowerShell has a bit of a more daunting syntax, especially if you're not used to it:

Get-ChildItem "C:\Windows\Temp" `
| Where-Object {$_.Length -gt 1024 -and $_.Name.StartsWith("W")} `
| Sort-Object {$_.Length} -Descending `
| Select-Object -Property Name, Length -First 10 -Skip 5 `
| ForEach-Object {Write-Host "$($_.Name) ($($_.Length))"}

That's a bit explicit and verbose, but if you use the command Get-Alias in PowerShell, you will see a lot of useful aliases, which make the syntax a bit terser and easier to get an overview of:

gci "C:\Windows\Temp" `
| ?{$_.Length -gt 1024 -and $_.Name.StartsWith("W")} `
| sort{$_.Length} -Descending `
| select Name, Length -First 10 -Skip 5 `
| %{write "$($_.Name) ($($_.Length))"}

In a real scenario, you probably wouldn't write each result to the console, but let PowerShell present the result in its default grid format.

HTML Encode TagHelper in ASP.NET Core

For a specific scenario recently, I wanted to display the HTML-encoded output of a TagHelper in ASP.NET Core. So I wanted to use the TagHelper, but not output its actual result, but see the raw HTML which would have been included in my template.

HTML

So I created another TagHelper, which allows me to wrap any HTML, inline code in ASP.NET Core and other TagHelpers, and get all the content inside the TagHelper's tag to be HTML-encoded, like this:

<html-encode>
    <a href="@Url.Action("Index")">Read More</a>
    @Html.TextBox("No_Longer_Recommended-TagHelpers_Preferred")
    <my-other-tag-helper />
</html-encode>

From this, I will get the raw HTML of the link with an UrlHelper-result, the result of the HTML-helper and the result of my other TagHelper.

The source-code for the html-encode-TagHelper is as follows:

[HtmlTargetElement("html-encode", TagStructure = TagStructure.NormalOrSelfClosing)]
public class HtmlEncodeTagHelper : TagHelper
{
    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        var childContent = output.Content.IsModified
            ? output.Content.GetContent()
            : (await output.GetChildContentAsync()).GetContent();

        string encodedChildContent = WebUtility.HtmlEncode(childContent ?? string.Empty);

        output.TagName = null;
        output.Content.SetHtmlContent(encodedChildContent);
    }
}

API Rate Limit HTTP Handler with HttpClientFactory

Most APIs have a Rate Limit of some sort. For example, GitHub has a limit of 5000 requests per hour. This can partly be handled by limiting your use by timing your requests to the API or through caching of the results.

What about when an API limits your requests per second? This is probably something you would want to handle somewhere central in your code and not spread out everywhere where you make an HTTP call to the API.

Funnel

For me, the solution was to add a Outgoing request middleware to the setup of the HttpClientFactory.

With this, I can just configure the startup services to use this RateLimitHttpMessageHandler-class with the HttpClientFactory:

services
    .AddHttpClient<IApi, Api>()
    .AddHttpMessageHandler(() =>
        new RateLimitHttpMessageHandler(
            limitCount: 5,
            limitTime: TimeSpan.FromSeconds(1)))
    .AddDefaultTransientHttpErrorPolicy();

This ensures that wherever I use the class IApi, through dependency injection, it will limit the calls to the API to only 5 calls per second.

The simplified version of code for the RateLimitHttpMessageHandler:

public class RateLimitHttpMessageHandler : DelegatingHandler
{
    private readonly List<DateTimeOffset> _callLog =
        new List<DateTimeOffset>();
    private readonly TimeSpan _limitTime;
    private readonly int _limitCount;

    public RateLimitHttpMessageHandler(int limitCount, TimeSpan limitTime)
    {
        _limitCount = limitCount;
        _limitTime = limitTime;
    }

    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        var now = DateTimeOffset.UtcNow;

        lock (_callLog)
        {
            _callLog.Add(now);

            while (_callLog.Count > _limitCount)
                _callLog.RemoveAt(0);
        }

        await LimitDelay(now);

        return await base.SendAsync(request, cancellationToken);
    }

    private async Task LimitDelay(DateTimeOffset now)
    {
        if (_callLog.Count < _limitCount)
            return;

        var limit = now.Add(-_limitTime);

        var lastCall = DateTimeOffset.MinValue;
        var shouldLock = false;

        lock (_callLog)
        {
            lastCall = _callLog.FirstOrDefault();
            shouldLock = _callLog.Count(x => x >= limit) >= _limitCount;
        }

        var delayTime = shouldLock && (lastCall > DateTimeOffset.MinValue)
            ? (limit - lastCall)
            : TimeSpan.Zero;

        if (delayTime > TimeSpan.Zero)
            await Task.Delay(delayTime);
    }
}

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

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.