<?xml version="1.0" encoding="utf-8"?><rss version="2.0"><channel><title>blog</title><link>https://robertwray.co.uk:443/blog</link><description>My blog of techy stuff, ranging around SQL (SQL Server, that is), C# and .net, JavaScript and a smattering of stuff about the Orchard CMS (that powers my site) as I get more familiar with it. There's the occasional non-technical post when something is of interest to me that I feel is worth sharing, but they should be few and far between</description><item><title>What can cause the trigger for a Power Automate Flow to stop firing?</title><link>https://robertwray.co.uk:443/blog/what-can-cause-the-trigger-for-a-power-automate-flow-to-stop-firing</link><description>&lt;p&gt;&lt;img src="/Media/Default/Blog/powerapps/dataverse_cloudflow_trigger_tophat.png" class="img-responsive" alt="Setting up a trigger for a Power Automate flow" /&gt;&lt;/p&gt;
&lt;p&gt;When the AAD account that owns a Power Automate flow (or Cloudflow, or whatever the name is by the time you read this....) is disabled it&amp;nbsp;&lt;em&gt;sometimes&lt;/em&gt; causes the trigger for the flow to stop working, even if you've changed the owner of the flow. Helpful, I know.&lt;/p&gt;
&lt;p&gt;It looks like this is caused by the trigger being still owned by the disabled user, which you can check by &lt;a href="https://learn.microsoft.com/en-us/power-apps/developer/data-platform/dataverse-sql-query"&gt;connecting via SQL to your Dataverse environment&lt;/a&gt; and running this query:&lt;/p&gt;
&lt;pre&gt;select * from callbackregistration where ownerid ='THE_ID_OF_THE_DISABLED_USER_HERE'&lt;/pre&gt;
&lt;p&gt;This will return a list of all the &lt;em&gt;callbackregistration's&lt;/em&gt; (aka: Flow triggers) that are still owned by the disabled user. The fix that we found for this is to, for each flow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Stop the flow - it has to be stopped to be able to delete the trigger&lt;/li&gt;
&lt;li&gt;Delete the trigger&lt;/li&gt;
&lt;li&gt;Restart the flow&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The trigger will get re-created when the flow is restarted.&lt;/p&gt;
&lt;p&gt;The easiest way to delete the trigger is to create a flow and plug each id in that you need to clear, like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="/Media/Default/Blog/powerapps/delete_callback_registration.png" class="img-responsive" alt="A simple instant flow that's used to delete a record from the 'Callback Registrations table" /&gt;&lt;/p&gt;</description><pubDate>Tue, 06 May 2025 09:58:02 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/what-can-cause-the-trigger-for-a-power-automate-flow-to-stop-firing</guid><category>powerapps</category><category>dynamics365</category><category>dataverse</category><category>powerautomate</category><category>cloudflows</category></item><item><title>"There is no Functions runtime available that matches the version specified in the project." when moving an Azure Functions project to a different machine</title><link>https://robertwray.co.uk:443/blog/there-is-no-functions-runtime-available-that-matches-the-version-specified-in-the-project-when-moving-an-azure-functions-project-to-a-different-machine</link><description>&lt;p&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/no_function_runtime.png" class="img-responsive" /&gt;&lt;/p&gt;
&lt;p&gt;If Visual Studio gives you the error "There is no Functions runtime available that matches the version specified in the project." then you'd, reasonably, think that the answer would be to either check for an updated version of Visual Studio or install an additional component. Neither of these are the solution.&lt;/p&gt;
&lt;p&gt;In this instance the Azure Functions project has a target framework of &lt;strong&gt;net9.0&lt;/strong&gt; which is the latest and greatest version. After checking the Visual Studio Installer for updates, and comparing what felt like the right components against the machine that the project came from I decided to try creating a new project and then diff them to find out what the issue was. This is what I saw:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/no_function_runtime_new_function_project.png" class="img-responsive" /&gt;&lt;/p&gt;
&lt;p&gt;No option for .NET 9.0...... I double checked on the original machine that the project was created on and the drop-down&amp;nbsp;&lt;strong&gt;does&lt;/strong&gt; contain ".NET 9.0 Isolated". This jogged my memory and the answer is.....&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open Visual Studio&lt;/li&gt;
&lt;li&gt;Go to Tools &amp;gt; Options&lt;/li&gt;
&lt;li&gt;In the Options window navigate to Project and Solutions &amp;gt; Azure Functions&lt;/li&gt;
&lt;li&gt;Click on the 'Check for updates' button:&lt;br /&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/no_function_runtime_check_for_updates.png" class="img-responsive" /&gt;&lt;/li&gt;
&lt;li&gt;Wait a few moments and a(nother) button should appear below it:&lt;br /&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/no_function_runtime_download_and_install.png" class="img-responsive" /&gt;&lt;/li&gt;
&lt;li&gt;Click on 'Download &amp;amp; Install' and wait&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The button should disappear, and the other two (original) buttons will be disabled for a time whilst Visual Studio downloads updates in the background. When the updates have finished downloading the buttons will be re-enabled:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/no_function_runtime_options_up_to_date.png" class="img-responsive" /&gt;&lt;/p&gt;
&lt;p&gt;You should be able to now both run your project&amp;nbsp;&lt;strong&gt;and&lt;/strong&gt; create new projects that are targeting the .NET 9.0 (or whatever version you're trying to use where you've seen this error!).&lt;/p&gt;</description><pubDate>Tue, 14 Jan 2025 15:20:45 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/there-is-no-functions-runtime-available-that-matches-the-version-specified-in-the-project-when-moving-an-azure-functions-project-to-a-different-machine</guid><category>azure</category><category>azure functions</category><category>visual studio</category><category>c#</category></item><item><title>"Changing" the secret for a Dataverse connection in Power Apps</title><link>https://robertwray.co.uk:443/blog/changing-the-secret-for-a-dataverse-connection-in-powerapps</link><description>&lt;p&gt;For reasons best known to Microsoft, you can't change the secret used to authenticate a Microsoft Dataverse connection using a service principal (If you're not familiar with setting these up there's a &lt;a href="https://www.matthewdevaney.com/a-visual-guide-to-power-platform-service-principal-setup/"&gt;good guide by Matthew Devaney&lt;/a&gt;). That's a bit of a pain when the secret expires, to say the least. There are some workaround for this, like having a &lt;a href="https://crmtipoftheday.com/1404/app-secrets-that-last-longer-than-2-years/"&gt;very long lived secret&lt;/a&gt;, but that doesn't resolve the underlying issue. There is a 'Switch account' button that makes you think "oh, I'll click that and be able to re-authenticate", go on, try it....&lt;/p&gt;
&lt;p&gt;&lt;img src="/Media/Default/Blog/powerapps/viewing_connection_in_powerapps.png" class="img-responsive" alt="The Power Apps UI showing a connection that's backed by a service principal" /&gt;&lt;/p&gt;
&lt;p&gt;Clicking on the button makes a pop-up window very briefly appear, and not a lot else.&lt;/p&gt;
&lt;p&gt;It turns out that there&amp;nbsp;&lt;strong&gt;is&lt;/strong&gt; a way to deal with this, albeit it's a bit of a hack and it does rely on you using &lt;a href="https://learn.microsoft.com/en-us/power-apps/maker/data-platform/create-connection-reference"&gt;connection references&lt;/a&gt; in your flows. The process consists of:&lt;/p&gt;
&lt;ol style="padding-left: 20px; list-style-type: decimal;"&gt;
&lt;li&gt;Creating a new secret&lt;/li&gt;
&lt;li&gt;Creating a new service principal backed connection&lt;/li&gt;
&lt;li&gt;Updating connection reference(s) that use the old connection&lt;/li&gt;
&lt;li&gt;Deleting the old connection&lt;/li&gt;
&lt;li&gt;Deleting the old secret&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I'm going to assume that steps 1, 4, and 5 are simple enough that there's no need to go into detail. Instead, I'll focus on steps 2 and 3.&lt;/p&gt;
&lt;p&gt;When you create the new secret, give it a name that'll let you tie it to the Dataverse connection you're going to create later. If the app registration is called "Dataverse Automation", you could call it "Dataverse Automation (May 2024)" to uniquely identify it.&lt;/p&gt;
&lt;h2&gt;Creating a new service principal backed connection&lt;/h2&gt;
&lt;p&gt;This is where the hack, workaround, bodge, call-it-what-you-will, comes into play. There genuinely doesn't appear to be&amp;nbsp;&lt;strong&gt;any&lt;/strong&gt; way to change the secret associated with a service principal backed connection, so the only thing for it is to create a new one. Now, what you&amp;nbsp;&lt;strong&gt;don't&lt;/strong&gt; want to do is click on the "+ New connection" button when you're viewing the list of connection:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/powerapps/new_connection_button_in_powerapps.png" class="img-responsive" alt="Where the button to click to create a new connection (just not one that's useful for a service principal) is located" /&gt;&lt;/p&gt;
&lt;p&gt;There's no option there to create a service principal backed connection. Thanks, Microsoft!&lt;/p&gt;
&lt;p&gt;Instead, you want to start creating a new flow (stick with it!):&lt;/p&gt;
&lt;p&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/powerapps/create_new_automated_cloud_flow.png" class="img-responsive" alt="The steps to navigate through to start the process of creating a new 'Automated cloud flow'" /&gt;&lt;/p&gt;
&lt;p&gt;In the "Build an automated cloud flow" window, give the flow any name you want as it's not going to stick around. Make sure you choose a Microsoft Dataverse trigger, I've opted for "When a row is added, modified or deleted":&lt;/p&gt;
&lt;p&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/powerapps/creating_a_new_flow_choosing_a_dataverse_trigger.png" class="img-responsive" alt="Naming your new flow and selecting the appropriate trigger by searching for it" /&gt;&lt;/p&gt;
&lt;p&gt;Once the flow designer has finished loading you should be presented with the basic starting point of a flow that has nothing other than a trigger. This is the point at which we'll be able to create a new connection, using the new secret, to refresh the connection reference. To do this, click on the "..." menu at the top-right of the trigger and choose "+ Add new connection" and you'll see this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/powerapps/sign_in_to_create_connection.png" class="img-responsive" alt="Creating a new connection as a service principal" /&gt;&lt;/p&gt;
&lt;p&gt;From here, click on "Connect with service principal" at the bottom of the box and you'll be prompted to give the connection a name, enter the Client ID, Client Secret and Tenant for the connection. Give the connection a name, which is where the name you gave the secret comes in handy. In this example I'm going to call the connection "Dataverse Automation (May 2025)":&lt;/p&gt;
&lt;p&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/powerapps/creating_new_connection.png" class="img-responsive" alt="Naming and configuring the service principal connection" /&gt;&lt;/p&gt;
&lt;p&gt;Click on 'Create' and, all being well, you'll be returned back to the previous step where you've got an empty flow with a trigger ready to be setup. Close the flow as you don't need to save it.&amp;nbsp; Now that you've got the new connection, with the updated secret, it's time to update the connection reference(s) that use it.&lt;/p&gt;
&lt;h2&gt;Updating connection reference(s) that use the old connection&lt;/h2&gt;
&lt;p&gt;Now you've got a new connection for your service principal, go into your solution, into "Connection references" then click on the one that you want to update, so you see this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/powerapps/changing_the_connection_in_the_connection_reference.png" class="img-responsive" alt="Updating the connection reference to use the new service principal connection with the fresh secret" /&gt;&lt;/p&gt;
&lt;p&gt;Change the selected connection to the newly created one (in my example I'm changing from "Dataverse Automation (January 2025)" to "Dataverse Automation (May 2025)") and then click Save. You'll be prompted with a "Save changes to this connection reference?" message, click the "Save changes" button to continue.&lt;/p&gt;
&lt;p&gt;After a few minutes (which the confirmation prompt mentions), everything will have been updated. You can load one of your flows and take a look at the "Connction References" box at the top-right to confirm:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/powerapps/flow_status_showing_connection_references.png" class="img-responsive" alt="A flow showing that the connection reference is now using the new connection" /&gt;&lt;/p&gt;
&lt;p&gt;The only thing left to do now is to delete the old connection (I said I wasn't going to mention this, but as there's a useful thing to mention, I'm going to), at which point you&amp;nbsp;&lt;em&gt;might&lt;/em&gt; see the Power Apps UI claim that it's still in use:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/powerapps/old_connection_still_in_use.png" class="img-responsive" alt="Power Apps claiming that the old connection is still in use, when it isn't" /&gt;&lt;/p&gt;
&lt;p&gt;This is a fib, which is particularly obvious here to me as I deleted "My basic flow" ten minutes before taking the screenshot above...&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;:::: some time passes ::::&lt;/p&gt;
&lt;p&gt;Even an hour later the Power Apps UI is still claming that the old connection is in use in both the flow that still exists (which is using the new connection via a connection reference) and the one that was deleted quite some time ago. It is safe to delete the connection, and that's exactly what I've done&lt;/p&gt;
&lt;h2&gt;In conclusion&lt;/h2&gt;
&lt;p&gt;Yes it's a work-around, but in the absence of a way to update an existing connection it's about as good as it gets. It's much better, at least in my opinion, than using a long lived secret. It also reinforces that having everything contained in a solution is the approach to take, as you can't use connection references outside of their solution. If Google, Bing, or your search engine of choice, has landed you here then hopefully this has helped!&lt;/p&gt;</description><pubDate>Sun, 05 May 2024 21:37:00 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/changing-the-secret-for-a-dataverse-connection-in-powerapps</guid><category>powerapps</category><category>dynamics365</category><category>azure</category><category>dataverse</category></item><item><title>Backing up backups from the HRMC 'Basic PAYE Tools' using NTFS junctions and OneDrive</title><link>https://robertwray.co.uk:443/blog/backing-up-backups-from-the-hrmc-basic-paye-tools-using-ntfs-junctions-and-onedrive</link><description>&lt;p&gt;&lt;img src="/Media/Default/Blog/hmrc_rti_backup_header.png" class="img-responsive" alt="The HMRC provided Basic PAYE Tools, showing where the backup files are setup" /&gt;&lt;/p&gt;
&lt;p&gt;I've been using the HMRC provided 'Basic PAYE Tools' to run payroll and it does the job adequately. The one gap is that, whilst it performs periodic backups, backups are to a location that cannot be changed. Ideally it would be possible to configure the backup location so it could be somewhere that One Drive covers automatically, but at the time of writing the configuration is static.&lt;/p&gt;
&lt;p&gt;The quickest and easiest way to achieve this, as OneDrive doesn't support backing up entirely arbitrary locations, is to use the mklink tool to create an &lt;a href="https://learn.microsoft.com/en-us/windows/win32/fileio/hard-links-and-junctions" title="'Hard links and junctions' at learn.microsoft.com"&gt;NTFS Junction&lt;/a&gt; between the folder that contains the Basic PAYE Tools backups and a folder&amp;nbsp;&lt;em&gt;inside&lt;/em&gt; OneDrive. The command to run is:&lt;/p&gt;
&lt;pre&gt;mklink /j "C:\users\robertwray\OneDrive\RTI Backup" "C:\Users\robertwray\AppData\Roaming\HMRC\payetools-rti\backups"&lt;/pre&gt;
&lt;p&gt;This creates a new folder called&amp;nbsp;&lt;em&gt;RTI Backup&lt;/em&gt; inside my OneDrive folder (this works equally well with "personal" and "business" OneDrive) which is a mirror of the folder where the Basic PAYE Tools backups are found.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Remember: Deleting a file in one folder will delete it in the other, it is not a copy of the backup folder, they're two views of the same files/folder.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Within a few minutes copies of all the backup files should be copied to OneDrive and I'm sure this would work equally well for any other application that's very prescriptive about where it stores its data.&lt;/p&gt;</description><pubDate>Mon, 29 Jan 2024 17:20:17 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/backing-up-backups-from-the-hrmc-basic-paye-tools-using-ntfs-junctions-and-onedrive</guid><category>onedrive</category><category>ntfs</category><category>bodging</category></item><item><title>Querying a SharePoint list via Microsoft.Graph after upgrading to .NET SDK v5</title><link>https://robertwray.co.uk:443/blog/querying-a-sharepoint-list-via-microsoft-graph-after-upgrading-to-net-sdk-v5</link><description>&lt;p&gt;I've just finished upgrading all the project references in a CRM solution I built a while ago, which was using the Microsoft.Graph v4 .NET SDK (via the &lt;a href="https://www.nuget.org/packages/Microsoft.Graph/4.35.0"&gt;Microsoft.Graph v4.35.0 NuGet package&lt;/a&gt;, to be precise), which meant I took an upgrade to the latest version (v5.37.0 at the time of writing) which comes with some breaking changes. The &lt;a href="https://github.com/microsoftgraph/msgraph-sdk-dotnet/blob/feature/5.0/docs/upgrade-to-v5.md"&gt;changelog and upgrade guide&lt;/a&gt; is a good starting point but the one bit that took a&amp;nbsp;&lt;em&gt;little&lt;/em&gt; bit of jiggling to get working again was retrieving items from a SharePoint list.&lt;/p&gt;
&lt;p&gt;The original code looked like this:&lt;/p&gt;
&lt;pre&gt;public async Task&amp;lt;IEnumerable&amp;lt;Order&amp;gt;&amp;gt; GetOrders()
{
    var client = await GetGraphApiClient();

    var queryOptions = new List&amp;lt;QueryOption&amp;gt;()
    {
        new QueryOption("expand", "fields(select=*)")
    };

    var list = await client.Sites[SiteId]
        .Lists[OrdersListId]
        .Items
        .Request(queryOptions)
        .GetAsync();

    var items = from item in list
                select GetFromListItemAdditionalData(item, item.Fields.AdditionalData);

    return items.ToList();
}

public Order GetFromListItemAdditionalData(ListItem item, IDictionary&amp;lt;string, object&amp;gt; additionalData)
{
    var title = additionalData["Title"].AsJsonElement().GetString();
&lt;/pre&gt;
&lt;p&gt;The revised code is:&lt;/p&gt;
&lt;pre&gt;public async Task&amp;lt;IEnumerable&amp;lt;Order&amp;gt;&amp;gt; GetOrders()
{
    var client = await GetGraphApiClient();&lt;br /&gt;&lt;br /&gt;    var list = await client.Sites[SiteId]
        .Lists[OrdersListId]
        .Items
        .GetAsync(requestConfiguration =&amp;gt;
        {
            requestConfiguration.QueryParameters.Expand = ["fields($select=*)"];
        });&lt;br /&gt;&lt;br /&gt;    var items = from item in list.Value
                select GetFromListItemAdditionalData(item, item.Fields.AdditionalData);

    return items.ToList();
}

public Order GetFromListItemAdditionalData(ListItem item, IDictionary&amp;lt;string, object&amp;gt; additionalData)
{
    var title = additionalData["Title"] as string;&lt;br /&gt;    ..&lt;br /&gt;    ..&lt;br /&gt;    return new Order(title, ... ...);&lt;br /&gt;}&lt;/pre&gt;
&lt;p&gt;The bit that took some digging to find the requried tweak was the addition of a "&lt;strong&gt;$&lt;/strong&gt;" prefixing "select" in the expand query definition. The other changes were:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Changing from using&amp;nbsp;&lt;em&gt;QueryOption&lt;/em&gt; to&amp;nbsp;&lt;em&gt;QueryParameters.Expand&lt;/em&gt; to pull all the additional list columns (i.e. the user columns in the list like title, order id, order date, etc,..) in the results returned&lt;/li&gt;
&lt;li&gt;Having the LINQ query operate on&amp;nbsp;&lt;em&gt;list.Value&lt;/em&gt; instead of list to process all the list items&lt;/li&gt;
&lt;li&gt;No longer needing the call to&amp;nbsp;&lt;em&gt;AsJsonElement&lt;/em&gt; to operate on the "AdditionalData" elements&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There's nothing particularly complicated, or ground-breaking, there... it did take more searches and digging around than i would like to come up with code that both compiled&amp;nbsp;&lt;strong&gt;and&lt;/strong&gt; worked after upgrading to v5 of the Microsoft.Graph .NET SDK though! Hopefully the note about changing &lt;strong&gt;"fields(select=*)"&lt;/strong&gt; to&amp;nbsp;&lt;strong&gt;"fields($select=*)"&lt;/strong&gt; will help someone out!&lt;/p&gt;</description><pubDate>Wed, 27 Dec 2023 22:59:17 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/querying-a-sharepoint-list-via-microsoft-graph-after-upgrading-to-net-sdk-v5</guid><category>.net</category><category>.net core</category><category>microsoft graph</category><category>sharepoint</category><category>c#</category></item><item><title>Feature flags in ASP.NET Core</title><link>https://robertwray.co.uk:443/blog/feature-flags-in-asp-net-core</link><description>&lt;p&gt;&lt;img src="/Media/Default/Blog/aspdotnet_core/feature_flagging_top_hat.png" class="img-responsive" alt="A screenshot of some ASP.NET code showing the tag helper for Feature Flagging being used" /&gt;&lt;/p&gt;
&lt;p&gt;I've been working on a few ASP.NET Core applications recently which has given me cause and opportunity to gate some functionality access behind &lt;a href="https://en.wikipedia.org/wiki/Feature_toggle"&gt;feature flags&lt;/a&gt;&amp;nbsp;whilst it's a work in progress, rather than having it gated behind &lt;code&gt;if (app.Environment.IsDevelopment())&lt;/code&gt; or similar where I then have to make code changes to enable the functionality. I've been doing this by using the &lt;a href="https://www.nuget.org/packages/Microsoft.FeatureManagement.AspNetCore"&gt;Microsoft.FeatureManagement.AspNetCore NuGet package&lt;/a&gt; which serves me with absolutely everything I need, at least for my relatively simple use cases. The &lt;a href="https://github.com/microsoft/FeatureManagement-Dotnet"&gt;readme on GitHub&lt;/a&gt; gives a pretty complete summary of how to use the package but for the sake of completness, here's a basic example excerpted from the implementation I'm using right now.&lt;/p&gt;
&lt;p&gt;Adding support to an ASP.NET Core 6 project is as simple as adding the library and then calling the AddFeatureManagement extension method:&lt;/p&gt;
&lt;pre&gt;using Microsoft.FeatureManagement;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddFeatureManagement();
&lt;/pre&gt;
&lt;p&gt;With that complete it's almost trivial to gate functionality in markup in a razor view behind a flag:&lt;/p&gt;
&lt;pre&gt;&amp;lt;feature name="SearchInTopHat"&amp;gt;
    &amp;lt;form class="d-flex float-md-end" asp-controller="home" asp-action="search"&amp;gt;
        &amp;lt;input class="form-control me-2" type="search" placeholder="Search" aria-label="Search" id="q" name="q"&amp;gt;
        &amp;lt;button class="btn btn-outline-light" type="submit"&amp;gt;Search&amp;gt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
&amp;lt;/feature&amp;gt;
&lt;/pre&gt;
&lt;p&gt;I've used strings for feature name (the examples in the documentation use an &lt;code&gt;enum&lt;/code&gt; which does make for stronger typing in your code but doesn't fully eliminate the risk of typos as it doesn't flow through to the configuration file. In the configuration file the absence of a feature flag implies that it's false which I use to leave all new/un-finished features switched off in production but then enable them in development by listing them in configuration explicitly:&lt;/p&gt;
&lt;pre&gt;&amp;nbsp; "FeatureManagement": {&lt;br /&gt;    "SearchInTopHat": true&lt;br /&gt;  }
&lt;/pre&gt;
&lt;p&gt;Documentation for the tag helper can be found at &lt;a href="https://docs.microsoft.com/en-gb/dotnet/api/microsoft.featuremanagement.mvc.taghelpers.featuretaghelper?view=azure-dotnet-preview"&gt;docs.microsoft.com&lt;/a&gt;&amp;nbsp;(code: &lt;a href="https://github.com/microsoft/FeatureManagement-Dotnet/blob/main/src/Microsoft.FeatureManagement.AspNetCore/TagHelpers/FeatureTagHelper.cs"&gt;on GitHub&lt;/a&gt;). Finding the documentation is mildly tricky as the it's structured by namespace whereas you'll never actually reference the namespace, for the most part - the &lt;a href="https://docs.microsoft.com/en-US/aspnet/core/mvc/views/tag-helpers/intro?view=aspnetcore-6.0#addtaghelper-makes-tag-helpers-available"&gt;@addTagHelper directive&lt;/a&gt; that needs to be added to &lt;em&gt;_ViewImports.cshtml&lt;/em&gt; is usually of the&amp;nbsp;&lt;em&gt;just name the assembly and pull in everything&lt;/em&gt; variety. However, once you've found the documentation you can see the different ways the tag helper can be wrangled. Of particular use is the 'Negate' property which allows you to show something when a feature is disabled.&lt;/p&gt;
&lt;p&gt;I've only made use of it in a .NET Core web application but it's equally usable in other .NET Core scenarios and has a lot more to it than the surface-level functionality I've described - but that's probably what 80% of people, like me, looking for this will need. It's definitely better than baking your own feature flag functionality and far, far, quicker!&lt;/p&gt;</description><pubDate>Sat, 23 Apr 2022 15:07:05 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/feature-flags-in-asp-net-core</guid><category>.net</category><category>.net core</category><category>.net framework</category><category>asp.net core</category><category>feature flags</category></item><item><title>Where have the last two years gone!?</title><link>https://robertwray.co.uk:443/blog/where-have-the-last-two-years-gone</link><description>&lt;p&gt;&lt;img src="/Media/Default/Blog/general_post_banner_image.jpg" class="img-responsive" alt="Just me, being whipped round the face by the tail of a cat balanced precariously on my left shoulder " /&gt;&lt;/p&gt;
&lt;p&gt;Well... Somehow it's ended up being two years since I last posted (and that was a brief one about &lt;a href="/blog/resetting-your-password-via-the-command-line"&gt;resetting your password via the command line&lt;/a&gt;) on the blog here - hopefully with the amount of&amp;nbsp;&lt;em&gt;stuff&lt;/em&gt; that's been going on from a techy perspective I'll be able to put pay to that for the forseeable future as there's plenty I have to write about in the .NET (Core), Dynamics 365 CRM and Azure space from both a work and non-work perspective.&lt;/p&gt;
&lt;p&gt;Other than that, over the last two years I've:&lt;/p&gt;
&lt;ul style="padding-left: 30px; list-style-type: disc;"&gt;
&lt;li&gt;Lost 2&amp;frac34; stone (or just under 18kg in new money) in weight, primarily over the first 6 to 9 months of the pandemic - more through calorie deficit from food than from exercise&lt;/li&gt;
&lt;li&gt;Got a lot better at reducing food waste - preparing three meals a day at home instead of 1 on most days has helped here, I think&lt;/li&gt;
&lt;li&gt;Started running&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It's the running that's the oddest one of the lot; I've always gone for a walk, however brief, at lunchtime to help me "reset" ready for the afternoon so I'm not someone tho whom exercise is an alien concept. I also spent a couple of years cycling to/from the train station before I finally bit the bullet and passed the driving test.&lt;/p&gt;
&lt;p&gt;The first run I did was on June 15th 2020 where I managed&amp;nbsp;&lt;span&gt;⅓ of a mile before deciding enough was enough and very nearly throwing up. Between then and now I've improved my pacing from about 10:20/mile to about 06:58/mile which I can sustain for a 10KM distance - I've even managed to push that as low as 06:28/mile over a 10KM distance. The biggest running achievement was probably managing a half-marathon (of distance, this was a solo endeavour) in late January this year in 01:38:14 even if my feet did hate me afterwards!&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;So lots of non-techy stuff which makes a refreshing change and is probably what's helped the last couple of years not seem quite as much of a shocker as they could've otherwise been.&lt;/span&gt;&lt;/p&gt;</description><pubDate>Sun, 17 Apr 2022 10:28:00 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/where-have-the-last-two-years-gone</guid><category>me</category><category>blog</category></item><item><title>Resetting your password via the command line</title><link>https://robertwray.co.uk:443/blog/resetting-your-password-via-the-command-line</link><description>&lt;p&gt;&lt;img src="/Media/Default/Blog/windows/password_reset_command_line.PNG" class="img-responsive" alt="Resetting a password from the command line using the 'net user' command" /&gt;&lt;/p&gt;
&lt;p&gt;If, like me, you're working remotely at the moment you might be spending a non-trivial amount of time using Remote Desktop to hop on and off various machines. It might be that, also like me,&amp;nbsp;you have to access machines that are in a DMZ using machine specific logins. If that's the case you can't just hit CTRL-ALT-END to change your password for the DMZ machine when it's multiple levels of remote desktop down (or if you can, lucky you - it doesn't work for me!). The tool I've been using is the&amp;nbsp;&lt;a href="https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/cc771865(v=ws.11)"&gt;NET&lt;/a&gt; command. The syntax for&amp;nbsp;this command to change your password is:&lt;/p&gt;
&lt;pre&gt;NET USER u&lt;em&gt;sername&lt;/em&gt; *&lt;/pre&gt;
&lt;p&gt;Substitute your username for&amp;nbsp;&lt;em&gt;username&lt;/em&gt; in the command and enter your new password when prompted (twice), and that's that done.&lt;/p&gt;
&lt;p&gt;You could also open Control Panel &amp;gt; Administrative Tools &amp;gt; Computer Management and navigate to the System Tools &amp;gt; Local Users and Groups &amp;gt; Users node to change your password but that would need the server to have the Desktop Experience available (i.e. not a 'Core' installation).&lt;/p&gt;</description><pubDate>Fri, 17 Apr 2020 05:59:00 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/resetting-your-password-via-the-command-line</guid><category>windows</category><category>windows server</category><category>windows 10</category><category>command line</category></item><item><title>Why my Pi-hole was logging tens of thousands of queries against my default gateway for domain SRV records</title><link>https://robertwray.co.uk:443/blog/why</link><description>&lt;p&gt;&lt;img src="/Media/Default/Blog/pihole_lots_of_queries_active_directory.PNG" class="img-responsive" alt="Pi-hole showing hundreds of thousands of queries from a small site running active directory" /&gt;&lt;/p&gt;
&lt;p&gt;If you're running&amp;nbsp;&lt;a href="https://pi-hole.net/"&gt;Pi-hole&lt;/a&gt; (which you should if you can, it's awesome - you can even &lt;a href="/blog/running-pi-hole-on-a-centos-virtual-machine"&gt;run it on a virtual machine&lt;/a&gt;) and also happen to be running it in an Active Directory&amp;nbsp;environment, there are various recommendations regarding how to configure it dotted around on the internet.&lt;/p&gt;
&lt;p&gt;Even having followed everything I've read,&amp;nbsp;I was seeing some very unusual traffic where there would be regular incredibly large batches of SRV queries against some of the domain DNS entries like &lt;em&gt;_ldap._tcp.dc._msdcs.mydomain.local&lt;/em&gt; which looked more than a little odd. These queries would tick-up at a rate of knots, 10s of them a second, and all were showing as originating from my UniFi USG device despite the fact that this would have&amp;nbsp;no cause to be making these requests. Because I run a lab environment with multiple domains, subnets and VLANs my immediate suspicion was that one of the secondary domains had got itself a little broken but the inter-domain trust appeared fine and there&amp;nbsp;was a distinct lack of errors or warnings in the Event Log&amp;nbsp;in any domains that may be involved.&lt;/p&gt;
&lt;p&gt;Eventually (by trial and error, sitting, watching and waiting) I narrowed it down to having the&amp;nbsp;&lt;em&gt;Use Conditional Forwarding&lt;/em&gt; setting in the Pi-hole configured to query the USG:&lt;/p&gt;
&lt;p&gt;&lt;img src="/Media/Default/Blog/pihole_conditional_fowarding.PNG" class="img-responsive" alt="The 'Use Conditional Forwarding' setting in Pi-hole' in Settings, DNS" /&gt;&lt;/p&gt;
&lt;p&gt;Changing this to the IP address of one of the primary domains controllers stopped this massive number of queries from being logged with no negative impact on domain behaviour. This also substantially reduced the rate at which&amp;nbsp;logs on the&amp;nbsp;Pi-hole grew, for obvious reasons.&lt;/p&gt;</description><pubDate>Sun, 22 Mar 2020 06:00:00 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/why</guid><category>pihole</category><category>windows</category><category>windows server</category><category>dns</category></item><item><title>SQL Server Reporting Services subscriptions fail to send email</title><link>https://robertwray.co.uk:443/blog/sql-server-reporting-services-subscriptions-fail-to-send-email</link><description>&lt;p&gt;&lt;img src="/Media/Default/Blog/sql_server/ssrs_failure_sending_mail.PNG" alt="The 'Subscriptions' tab in SSRS showing the status of a timed subscription that's failed to send by email" class="img-responsive" /&gt;&lt;/p&gt;
&lt;p&gt;We've recently had reports that some scheduled reports that are emailed out haven't been sent/received, looking at SSRS the error that was showing was:&lt;/p&gt;
&lt;pre&gt;Failure sending mail: The report server has encountered a configuration error. Mail will not be resent.&lt;/pre&gt;
&lt;p&gt;It turns out that the active directory account for the owner of the subscription was recently deleted. Re-creating the subscriptions with the owning account setup as an active user resolved the issue, though longer term we're going to move to getting users to manage their own subscriptions so that we don't have to keep track of, and refresh, subscriptions when the person who created them (invariably someone within IT) leaves the business.&lt;/p&gt;</description><pubDate>Wed, 18 Mar 2020 06:00:00 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/sql-server-reporting-services-subscriptions-fail-to-send-email</guid><category>windows server</category><category>sql server</category><category>ssrs</category><category>sql server reporting services</category></item><item><title>Recovering access to a SQL Server that you don't have 'sa' access to anymore</title><link>https://robertwray.co.uk:443/blog/recovering-access-to-a-sql-server-that-you-don-t-have-sa-access-to-anymore</link><description>&lt;p&gt;It's happened to most all of us at one time or another; an instance of SQL Server has been inherited where no one&amp;nbsp;knows the&amp;nbsp;&lt;em&gt;sa&lt;/em&gt; password, or is a member of the&amp;nbsp;&lt;em&gt;&lt;a href="https://docs.microsoft.com/en-us/sql/relational-databases/security/authentication-access/server-level-roles?view=sql-server-2017"&gt;sysadmin&lt;/a&gt;&lt;/em&gt; role, it's annoying when it's a SQL Server instance on a Development or Test server, but becomes far more problematic when it's on a production system. Fortunately, there's a way to solve this.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;</description><pubDate>Sat, 01 Feb 2020 23:58:00 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/recovering-access-to-a-sql-server-that-you-don-t-have-sa-access-to-anymore</guid></item><item><title>Generating the bootstrap carousel on my homepage</title><link>https://robertwray.co.uk:443/blog/generating-the-bootstrap-carousel-on-my-homepage</link><description>&lt;p&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/generating_carousel_markup.PNG" class="img-responsive" alt="Using a little bit of JavaScript to generate the carousel, rather than hand-crafting the marup" /&gt;&lt;/p&gt;
&lt;p&gt;Back in 2017 I wrote about&amp;nbsp;&lt;a href="/blog/bootstrapping-a-carousel-onto-my-homepage"&gt;bootstrapping a carousel onto my homepage&lt;/a&gt; (was it really that long ago!?) and ever since then I've been adding, removing and modifying items in the carousel by copying, pasting and editing chunks of HTML. Needless to say this is something that's been somewhat error prone, brittle and has made me quite reluctant to modify the carousel unless I&amp;nbsp;&lt;em&gt;absolutely had to&lt;/em&gt;. A little while ago I saw a post on&amp;nbsp;&lt;a href="https://meta.stackexchange.com/a/224922/7872"&gt;meta.stackexchange.com&lt;/a&gt; that has a listing of all the&amp;nbsp;data-dumps that are prepared from the Stack Exchange network, but what does this have to do with carousels? Well.... the list is generated from a JavaScript script which avoids the need to hand-crank the markup whenever a new&amp;nbsp;data-dump is generated. This was a "light-bulb" moment, which is a bit embarassing for someone who's generally a massive "automate when you can" kinda person.&lt;/p&gt;
&lt;p&gt;To do this I've created a simple JavaScript file in Visual Studio Code to which I do the following to generate the markup:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open it&lt;/li&gt;
&lt;li&gt;Hit F5 to start debugging&lt;/li&gt;
&lt;li&gt;Choose 'Node.js' when&amp;nbsp;asked which environment I want to run the script in&amp;nbsp;&lt;em&gt;(I'm not sure if this is something that's present in VS Code by&amp;nbsp;default or something that I've added at some point!)&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Copy the markup out of the Debug Console&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It's quick and dirty, but it's a lot less painful than editing the markup by hand.&lt;/p&gt;
&lt;h2&gt;Describing the carousel entries&lt;/h2&gt;
&lt;p&gt;First up is&amp;nbsp;the script that I use to describe the carousel entries. Each carousel entry consists of an image, a title and a description:&lt;/p&gt;
&lt;pre&gt;class CarouselItem
{
    constructor(imageSrc, postSrc, title, description, inverse)
    {
        this.imageSrc = imageSrc;
        this.postSrc = postSrc;
        this.title = title;
        this.description = description;
        this.inverse = inverse === undefined ? false : true;
    }
}
&lt;/pre&gt;
&lt;p&gt;They also contain an&amp;nbsp;&lt;em&gt;inverse&lt;/em&gt; flag which I use to flip the colour of the text because some of the images lend themselves to having white text instead of black, for readability. There's not a lot else to say about this class structure, so next up is&amp;nbsp;an example of a couple of populated items:&lt;/p&gt;
&lt;pre&gt;const carouselEntries = 
[
    new CarouselItem('/Media/Default/CarouselImages/bolognese_pasta_bake.jpg',
    '/recipes/lamb-hotpot', 'Bolognese Conchigli pasta bake',
    'The conchigli that I use for this pasta bake are quite thick, lending them an almost meaty texture that really gives the pasta bake some substance'),

    new CarouselItem('/Media/Default/CarouselImages/sea_bass_parcel_carousel_2.jpg',
    '/recipes/sea-bass-parcels-with-asian-flavours', 'Sea Bass parcels with asian flavours',
    'Sea Bass parcels with garlic, ginger, mirin and other goodies')
]
&lt;/pre&gt;
&lt;p&gt;For the purposes of this post I've not listed each and every entry in the carousel as that would be very long, very boring and very repetitive. The only part of the &lt;em&gt;CarouselItem&lt;/em&gt; class that's not used here is the&amp;nbsp;&lt;em&gt;inverse&lt;/em&gt; parameter to its constructor, because the majority of carousel items seem to look okay without inverting the colour I've opted to omit this when not required as it makes it more obvious when it is required.&lt;/p&gt;
&lt;h2&gt;Generating the carousel markup&lt;/h2&gt;
&lt;p&gt;Because I run this straight in Visual Studio Code and copy the output HTML, the execution harness that comes straight after the carousel entries is pretty simple:&lt;/p&gt;
&lt;pre&gt;const markup = generateMarkupForCarousel('carousel-example-generic');
console.log(markup);
&lt;/pre&gt;
&lt;p&gt;This runs the&amp;nbsp;&lt;em&gt;generateMarkupForCarousel&lt;/em&gt; method, passing in the ID I want to use for the carousel and then sends the generated markup to the console. The code for this method, actually two methods as I have another method I call out to that generates the carousel indicators / slides separately from the container and navigation buttons for the carousel, does a lot of concatenation and looks like this:&lt;/p&gt;
&lt;pre&gt;function generateMarkupForCarousel(carouselId)
{
    const markupForSlidesAndIndicators = generateMarkupForIndicatorsAndSlides(carouselId);
    const finalCarousel = [];
    finalCarousel.push('&amp;lt;div class="carousel slide" id="' + carouselId + '" data-ride="carousel"&amp;gt;');
    finalCarousel.push(markupForSlidesAndIndicators);
    // Controls: Previous
    finalCarousel.push('&amp;lt;a class="left carousel-control" role="button" href="#' + carouselId + '" data-slide="prev"&amp;gt;');
    finalCarousel.push('&amp;lt;span class="glyphicon glyphicon-chevron-left" aria-hidden="true"&amp;gt;&amp;lt;/span&amp;gt;');
    finalCarousel.push('&amp;lt;span class="sr-only"&amp;gt;Previous&amp;lt;/span&amp;gt;');
    finalCarousel.push('&amp;lt;/a&amp;gt;');
    // Controls: Next
    finalCarousel.push('&amp;lt;a class="right carousel-control" role="button" href="#' + carouselId + '" data-slide="next"&amp;gt;');
    finalCarousel.push('&amp;lt;span class="glyphicon glyphicon-chevron-right" aria-hidden="true"&amp;gt;&amp;lt;/span&amp;gt;');
    finalCarousel.push('&amp;lt;span class="sr-only"&amp;gt;Next&amp;lt;/span&amp;gt;');
    finalCarousel.push('&amp;lt;/a&amp;gt;');
    finalCarousel.push('&amp;lt;/div&amp;gt;'); // class="carousel slide"

    return finalCarousel.join('\r\n');
}

function generateMarkupForIndicatorsAndSlides(carouselId)
{
    const indicators = [];
    const slides = [];
    // indicators / slides pre-amble
    indicators.push('&amp;lt;ol class="carousel-indicators"&amp;gt;');
    slides.push('&amp;lt;div class="carousel-inner" role="listbox"&amp;gt;');
    let counter = 0;
    for (const entry of carouselEntries)
    {
        const indicatorCssClass = counter == 0 ? 'class="active" ' : '';
        const slideItemCssClass = counter == 0 ? 'class="item active"' : 'class="item"';
        const titleStyle = entry.inverse ? ' style="color: white;"' : '';
        indicators.push('&amp;lt;li ' + indicatorCssClass + 'data-target="#' + carouselId + '" data-slide-to="' + counter + '"&amp;gt;&amp;lt;/li&amp;gt;');
        slides.push('&amp;lt;div ' + slideItemCssClass + '&amp;gt;');
        slides.push('&amp;lt;img alt="' + entry.title + '" src="' + entry.imageSrc + '" /&amp;gt;');
        slides.push('&amp;lt;div class="carousel-caption"&amp;gt;');
        slides.push('&amp;lt;h3&amp;gt;&amp;lt;a href="' + entry.postSrc + '"&amp;gt;' + entry.title + '&amp;lt;/a&amp;gt;&amp;lt;/h3&amp;gt;');
        slides.push('&amp;lt;div' + titleStyle + '&amp;gt;' + entry.description + '&amp;lt;/div&amp;gt;');
        slides.push('&amp;lt;/div&amp;gt;'); // class="carousel-caption"
        slides.push('&amp;lt;/div&amp;gt;'); // class="item"
        counter++;
    }
    // indicators / slides closing
    indicators.push('&amp;lt;/ol&amp;gt;');
    slides.push('&amp;lt;/div&amp;gt;');

    const generatedMarkup = indicators.join('\r\n') + '\r\n' + slides.join('\r\n');

    return generatedMarkup;
}
&lt;/pre&gt;
&lt;p&gt;The code could probably be more concise, there's certainly some duplication in the generation of the &lt;em&gt;Previous&lt;/em&gt; and &lt;em&gt;Next&lt;/em&gt; controls that could be elided and probably a reduction in the amount of code overall.&lt;/p&gt;
&lt;h2&gt;What's next?&lt;/h2&gt;
&lt;p&gt;As this is "just" a chunk of JavaScript I could probably embed it in the page and have the carousel generated when the page loads. This would certainly help with page load time,&amp;nbsp;as it looks a little like this at the moment:&lt;/p&gt;
&lt;p&gt;&lt;img src="/Media/Default/Blog/homepage_carousel_loading.PNG" class="img-responsive" alt="Developer Tools showing the sequence of images that end up being loaded during page load for my homepage" /&gt;&lt;/p&gt;
&lt;p&gt;Whilst all the images are being loaded in parallel,&amp;nbsp;loading is being triggered before the rest of the page assets, including jQuery which sits a little bit further down. I should probably also look at optimising the images as that's 5.1MB of images being downloaded there which is a little sub-optimal.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;</description><pubDate>Tue, 21 Jan 2020 07:12:00 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/generating-the-bootstrap-carousel-on-my-homepage</guid><category>bootstrap</category><category>javascript</category><category>automation</category><category>scripting</category><category>blog</category></item><item><title>Reactive Extensions - Their NuGet packages were renamed</title><link>https://robertwray.co.uk:443/blog/reactive-extensions-their-nuget-packages-were-renamed</link><description>&lt;p&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/rx_main_unlisted_nuget_package.PNG" class="img-responsive" alt="A screenshot of the NuGet page for the Rx-Main package showing that it's been unlisted" /&gt;&lt;/p&gt;
&lt;p&gt;This is an aide-memoire which may be of use to others, though it's old news and almost certainly won't be relevant unless you've got some relatively old codebases that need to be maintained and supported..&lt;/p&gt;
&lt;p&gt;According to a post on the &lt;a href="https://groups.google.com/forum/#!topic/ravendb/epamtsm7Cf0"&gt;RavenDB google group&lt;/a&gt;, Reactive extensions announced that:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The NuGet packages have changed their package naming in the move from v2.x.x to v3.0.0&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;del&gt;&lt;code&gt;Rx-Main&lt;/code&gt;&lt;/del&gt;&amp;nbsp;is now&amp;nbsp;&lt;code&gt;System.Reactive&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;code&gt;Rx-Core&lt;/code&gt;&lt;/del&gt;&amp;nbsp;is now&amp;nbsp;&lt;code&gt;System.Reactive.Core&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;code&gt;Rx-Interfaces&lt;/code&gt;&lt;/del&gt;&amp;nbsp;is now&amp;nbsp;&lt;code&gt;System.Reactive.Interfaces&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;code&gt;Rx-Linq&lt;/code&gt;&lt;/del&gt;&amp;nbsp;is now&amp;nbsp;&lt;code&gt;System.Reactive.Linq&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;code&gt;Rx-PlatformServices&lt;/code&gt;&lt;/del&gt;&amp;nbsp;is now&amp;nbsp;&lt;code&gt;System.Reactive.&lt;wbr /&gt;PlatformServices&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;del&gt;&lt;code&gt;Rx-Testing&lt;/code&gt;&lt;/del&gt;&amp;nbsp;is now&amp;nbsp;&lt;code&gt;Microsoft.Reactive.Testing&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This brings the NuGet package naming in line with NuGet guidelines and also the dominant namespace in each package. The strong name key has also changed, which is considered a breaking change. However, there are no expected API changes, therefore, once you make the NuGet change, no code changes should be necessary.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Also mentioned in the announcement is&amp;nbsp;the fact that the&amp;nbsp;&lt;a href="https://www.nuget.org/packages/Rx-Main/"&gt;old packages&lt;/a&gt; have been unlisted, meaning that you can find them&amp;nbsp;directly if you construct&amp;nbsp;an appropriate URL containing their old name but a search won't return any results.&lt;/p&gt;</description><pubDate>Mon, 20 Jan 2020 08:24:12 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/reactive-extensions-their-nuget-packages-were-renamed</guid><category>reactive extensions</category><category>nuget</category><category>.net</category><category>c#</category></item><item><title>Converting a load of files from UTF-16 to UTF-8</title><link>https://robertwray.co.uk:443/blog/converting-a-load-of-files-from-utf-16-to-utf-8</link><description>&lt;p&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/convert_to_utf8.PNG" class="img-responsive" alt="A sure sign that you've scripted a load of stuff out as UTF-16 and are going to have to go through and do it all over again" /&gt;&lt;/p&gt;
&lt;p&gt;There's a database here that I've been meaning to get into source control for a&amp;nbsp;&lt;strong&gt;long&lt;/strong&gt; time. Luckily we have regular backups of&amp;nbsp;it (that have been restore proven) and it never&amp;nbsp;changes so it's been pretty low down the priority list. Along comes today and I need to make some changes so, time to get it all scripted. I do this using SQL Server Management Studio, scripting one object per file and dropping each object type into separate folders. I usually remember to script it out as 'ANSI Text' but&amp;nbsp;this time round I forgot, hence GitHub Desktop showing me this dreaded message.&lt;/p&gt;
&lt;p&gt;For various reasons scripting this database is a convoluted exercise, meaning that this one small mistake meant it would take me the best part of an hour to do again. Enter C# and a teeny tiny&amp;nbsp;.NET Core console app (though equally valid as a .NET Framework app as it's not using anything .NET Core specific) to re-save all the files. Without further ado, should the code be useful to you, here it is:&lt;/p&gt;
&lt;pre&gt;using System;
using System.IO;
using System.Text;

namespace ConvertToUTF8
{
    class Program
    {
        static void Main(string[] args)
        {
            var folder = args[0];

            var files = Directory.GetFiles(folder, "*.sql", SearchOption.AllDirectories);

            foreach (var file in files)
            {
                Console.WriteLine($"Processing {file}");
                var content = File.ReadAllText(file);

                File.WriteAllText(file, content, Encoding.UTF8);
            }
        }
    }
}

&lt;/pre&gt;
&lt;p&gt;This iterates over every&amp;nbsp;&lt;em&gt;.sql&lt;/em&gt; file in the folder passed in as the first argument to the app, and its sub-folders, and re-saves the file as UTF-8 instead of UTF-16. Five minutes to write the app, 10 seconds for it to run and that means the best part of an hour saved. Awesome!&lt;/p&gt;</description><pubDate>Thu, 05 Dec 2019 09:44:31 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/converting-a-load-of-files-from-utf-16-to-utf-8</guid><category>c#</category><category>.net</category><category>.net core</category><category>encoding</category></item><item><title>Changing a Git branch that's tracking a remote</title><link>https://robertwray.co.uk:443/blog/changing-a-git-branch-that-s-tracking-a-remote</link><description>&lt;p&gt;&lt;img src="https://robertwray.co.uk/Media/Default/Blog/git_header_image.PNG" class="img-responsive" alt="Some git commands and output that shows one of the commands in action and the result of running them" /&gt;&lt;/p&gt;
&lt;p&gt;Having done some work against a local branch that's associated with a remote for a certain piece of work I've decided I want to keep the work (it's given me some proof-of-concept ideas) but start again on the actual&amp;nbsp;task at hand. Keeping the current branch with all my commits but being able to "reset" back to the branch as it is on the server is the ideal outcome, which I've noted how to do below.&lt;/p&gt;
&lt;p&gt;As with almost anything else Git related, there's a lot of places where this answer already exists I'm sure, but for my own reference (and perhaps as someone else might find this as a result of&amp;nbsp;searching and find use for it) here are the steps to take a local branch that's tracking a remote one, rename it and stop it tracking the remote.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;With the branch in question being current (which will show in green with an asterisk next to it when running&amp;nbsp;&lt;em&gt;git branch&lt;/em&gt; from the console), run the following&amp;nbsp;commands:&lt;/p&gt;
&lt;pre&gt;git branch -m new_name_for_branch
git branch --unset-upstream
&lt;/pre&gt;
&lt;p&gt;The first command renames the branch and the second command, well, unsets the upstream branch associated with the local branch.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;</description><pubDate>Mon, 18 Nov 2019 09:59:45 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/changing-a-git-branch-that-s-tracking-a-remote</guid><category>git</category><category>team foundation server</category><category>command line</category></item><item><title>The anatomy of a FIX message</title><link>https://robertwray.co.uk:443/blog/the-anatomy-of-a-fix-message</link><description>&lt;p&gt;In my current role I've spend some time working with the &lt;a href="https://en.wikipedia.org/wiki/Financial_Information_eXchange"&gt;FIX protocol&lt;/a&gt;, which is:&lt;/p&gt;
&lt;blockquote&gt;an electronic communications protocol initiated in 1992 for international real-time exchange of information related to securities transactions and markets.&lt;/blockquote&gt;
&lt;p&gt;I'm pretty lucky in that there's an awesome library called&amp;nbsp;&lt;a href="https://github.com/connamara/quickfixn"&gt;QuickFix/n&lt;/a&gt; that takes care of doing the grunt work for me, but it's never a bad idea to have some understanding of what's happening "on the wire". To that end I took time to pick apart a FIX message using&amp;nbsp;&lt;a href="https://www.onixs.biz/fix-dictionary/4.2/fields_by_tag.html"&gt;this awesome repository of information at onixs.biz&lt;/a&gt; to do so.&lt;/p&gt;
&lt;p&gt;The message in question was a request for a quote for a foreign exchange transaction for the EUR/GBP pair, which looks like this:&lt;/p&gt;
&lt;pre&gt;8=FIX.4.29=17135=R34=325749=COMP-PRICES52=20180508-09:02:43.96856=BANK-PRICES&lt;br /&gt;131=Q-EURGBP-BUY-3357-636613669639680362146=155=EUR/GBP15=EUR38=335740=C&lt;br /&gt;54=164=20180508167=FOR10=150
&lt;/pre&gt;
&lt;p&gt;The message is constructed as follows:&lt;/p&gt;
&lt;div style="direction: ltr;"&gt;
&lt;table border="1" cellpadding="0" cellspacing="0" valign="top" style="direction: ltr; border-collapse: collapse; border: 1pt solid #A3A3A3;" title="" summary=""&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="background-color: #5b9bd5; vertical-align: top; width: 1.2937in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt; color: white;"&gt;&lt;span style="font-weight: bold;"&gt;Field&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #5b9bd5; vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt; color: white;"&gt;&lt;span style="font-weight: bold;"&gt;Value&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #5b9bd5; vertical-align: top; width: 3.9694in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt; color: white;"&gt;&lt;span style="font-weight: bold;"&gt;What it is&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 1.2937in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;8 &amp;ldquo;BeginString&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;FIX.4.2&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 3.9694in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;Identifies the start of a message and specifies the protocol version being used&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="vertical-align: top; width: 1.2937in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;9 &amp;ldquo;BodyLength&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;171&lt;/p&gt;
&lt;/td&gt;
&lt;td style="vertical-align: top; width: 4.0055in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;The length (bytes) of everything up until the field &amp;ldquo;CheckSum&amp;rdquo;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 1.2937in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;35 &amp;ldquo;MsgType&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;R&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 3.9694in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;The type of message: Quote Request&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="vertical-align: top; width: 1.2937in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;34 &amp;ldquo;MsgSeqNum&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;3257&lt;/p&gt;
&lt;/td&gt;
&lt;td style="vertical-align: top; width: 3.9972in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;The message sequence number. This increments as each message goes back and forth&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 1.3083in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;49 &amp;ldquo;SenderCompId&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;COMP-PRICES&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 3.9548in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;The identity of the sender of the message&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="vertical-align: top; width: 1.2937in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;52 &amp;ldquo;SendingTime&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;20180508-09:02:43.968&lt;/p&gt;
&lt;/td&gt;
&lt;td style="vertical-align: top; width: 3.9694in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;The time we sent the message&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 1.2937in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;56 &amp;ldquo;TargetCompId&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;BANK-PRICES&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 3.9694in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;The identity of the recipient of the message&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="vertical-align: top; width: 1.2937in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;131 &amp;ldquo;QuoteReqID&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;Q-EURGBP-BUY-3357-636613669639680362&lt;/p&gt;
&lt;/td&gt;
&lt;td style="vertical-align: top; width: 3.9694in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;Unique identifier for the quote&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 1.3111in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;146 &amp;ldquo;NoRelatedSym&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;1&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 3.952in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;The number of repeating symbols specified&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="vertical-align: top; width: 1.2937in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;55 &amp;ldquo;Symbol&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;EUR/GBP&lt;/p&gt;
&lt;/td&gt;
&lt;td style="vertical-align: top; width: 3.9694in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;The currency pair we&amp;rsquo;re asking for a quote in&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 1.2937in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;15 &amp;ldquo;Currency&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;EUR&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 3.9694in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;The currency used for price&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="vertical-align: top; width: 1.2937in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;38 &amp;ldquo;OrderQty&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;3357&lt;/p&gt;
&lt;/td&gt;
&lt;td style="vertical-align: top; width: 3.9694in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;The quantity to be used (the amount of money)&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 1.2937in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;40 &amp;ldquo;OrdType&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;C&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 3.9694in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;The type of order. C = &amp;ldquo;Forex &amp;ndash; Market&amp;rdquo;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="vertical-align: top; width: 1.2937in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;54 &amp;ldquo;Side&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;1&lt;/p&gt;
&lt;/td&gt;
&lt;td style="vertical-align: top; width: 3.9694in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;2 = &amp;ldquo;Buy&amp;rdquo;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 1.3131in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;64 &amp;ldquo;FutSettDate&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;20180508&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 3.95in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;The settlement date&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="vertical-align: top; width: 1.2937in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;167 &amp;ldquo;SecurityType&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;FOR&lt;/p&gt;
&lt;/td&gt;
&lt;td style="vertical-align: top; width: 3.9694in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;FOR = &amp;ldquo;Foreign Exchange Contract&amp;rdquo;&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 1.2937in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;&lt;span style="font-weight: bold;"&gt;10 &amp;ldquo;CheckSum&amp;rdquo;&lt;/span&gt;&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 2.3152in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;150&lt;/p&gt;
&lt;/td&gt;
&lt;td style="background-color: #deeaf6; vertical-align: top; width: 3.9972in; padding: 4pt 4pt 4pt 4pt; border: 1pt solid #A3A3A3;"&gt;
&lt;p style="margin: 0in; font-family: Calibri; font-size: 11.0pt;"&gt;A calculated three byte checksum based on a calculation referenced in the linked doc&lt;/p&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;p&gt;Hopefully this will be useful to someone (other than me)!&lt;/p&gt;</description><pubDate>Thu, 31 Oct 2019 15:41:20 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/the-anatomy-of-a-fix-message</guid><category>FIX</category></item><item><title>Using PowerShell to start and stop Hyper-V virtual machines</title><link>https://robertwray.co.uk:443/blog/using-powershell-to-start-and-stop-hyper-v-virtual-machines</link><description>&lt;p&gt;&lt;img class="img-responsive&amp;quot;" alt="PowerShell in the process of starting one of the VMs on this machine" src="/Media/Default/Blog/PowerShell/powershell_startvm.png" /&gt;&lt;/p&gt;
&lt;p&gt;I've been doing a bit of VM Host upgrading recently&amp;nbsp;and being able to mainpulate VMs on said hosts via the command line&amp;nbsp;makes things go a lot quicker! Here's two PowerShell commands I've executed more times than I'd care to mention over the past few days:&lt;/p&gt;
&lt;pre&gt;Get-VM | Start-VM

Get-VM | Save-VM
&lt;/pre&gt;
&lt;p&gt;The first command iterates over all VMs on the current machine (or another one if you've used Enter-PSSession to remote to another machine) and, well, starts them. The second command iterates over all VMs and saves them to disk - effectively hibernating them so you can bring them back up quickly once whatever VM Host upgrading task you're going through has been completed.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;</description><pubDate>Mon, 30 Sep 2019 20:22:50 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/using-powershell-to-start-and-stop-hyper-v-virtual-machines</guid><category>powershell</category><category>hyper-v</category><category>windows server</category></item><item><title>How to force Windows 10 / Server 201x to scan for updates</title><link>https://robertwray.co.uk:443/blog/how-to-force-windows-10-server-201x-to-scan-for-updates</link><description>&lt;p&gt;&lt;img class="img-responsive" alt="The Windows Settings app showing updates being scanned for" src="/Media/Default/Blog/windows_10_scanning_for_updates.PNG" /&gt;&lt;/p&gt;
&lt;p&gt;One of the irritations of Windows 10 and its accompanying server editions is that there's no user interface surfaced to re-scan for available updates. The ability to do this comes in handy if, say, you've forgotten to approve all the relevant updates in WSUS before machines have scanned for them, or, a fresh install of Server 2016 has arbitrarily decided that the most recent cumulative update available is from over a year ago and you want to give it a chance to reconsider its choices. Enter&amp;nbsp;&lt;em&gt;UsoClient&lt;/em&gt; ...&lt;/p&gt;
&lt;p&gt;From a command prompt (I've habitually run this as Administrator but it may not need that..) runt the following command:&lt;/p&gt;
&lt;pre&gt;UsoClient StartScan&lt;/pre&gt;
&lt;p&gt;If you've got the&amp;nbsp;Windows Update user interface open you'll see it starting a fresh scan for updates and any&amp;nbsp;newly applicable updates should pop-up and be available for download and install.&lt;/p&gt;
&lt;p&gt;(This post is brought to you by my complete inability to remember this command)&lt;/p&gt;</description><pubDate>Wed, 28 Aug 2019 05:00:00 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/how-to-force-windows-10-server-201x-to-scan-for-updates</guid><category>windows</category><category>windows 10</category><category>windows server</category></item><item><title>Don't slash your globs the wrong way!</title><link>https://robertwray.co.uk:443/blog/don-t-slash-your-globs-the-wrong-way</link><description>&lt;p&gt;&lt;img class="img-responsive" alt="A piece of a gulp script" src="/Media/Default/Blog/gulp_moving_files_around.png" /&gt;&lt;/p&gt;
&lt;p&gt;I inherited a couple of&amp;nbsp;&lt;a href="https://angularjs.org/"&gt;AngularJS&lt;/a&gt; applications that&amp;nbsp;are built using&amp;nbsp;&lt;a href="https://gulpjs.com/"&gt;gulp scripts&lt;/a&gt; when I started in my current role. Putting to one side the swathes of repeated code both within the build scripts for the same application (literally huge swathes of code copied and pasted from&amp;nbsp;the script for building the desktop version into the script for building the mobile version) and that had subsequently been copied to other applications, these scripts were, a bit shit. To put it diplomatically. Over the past month we've factored up and out code that's common to building all applications and&amp;nbsp;overall reduced the complexity of the build process - who wouldn't want&amp;nbsp;a simpler build process that produces exactly the same end result, right?&lt;/p&gt;
&lt;p&gt;The biggest problem we've had is that all projects were using what can only be described as a franken-version of gulp. Version &lt;em&gt;4.0.0-alpha.2&lt;/em&gt;, to be precise. Upgrading from this version to version 4.0.2 resulted in some very weird things happening around file copies; files that started in &lt;em&gt;app\images\&lt;/em&gt; and should end up in .&lt;em&gt;dev\images\&lt;/em&gt; were ending up in &lt;em&gt;.dev\images\app\images&lt;/em&gt; - not helpful! Here's a snippet of the build script in question:&lt;/p&gt;
&lt;pre&gt;pump([
    gulp.src(['app\\images\\**\\*.*']),
    gulp.dest('.dev\\images')
] , done);
&lt;/pre&gt;
&lt;p&gt;I&amp;nbsp;asked&amp;nbsp;&lt;a href="https://stackoverflow.com/q/56477817/7872"&gt;a question&lt;/a&gt; on Stack Overflow back at the beginning of June, and after offering a bounty a few days ago finally got &lt;a href="https://stackoverflow.com/a/57121753/7872"&gt;an answer&lt;/a&gt;... When building gobs, use / as a delimiter, not \ or bad things will happen. Changing the direction of all the slashes resulted in a build that gave the same output, but wasn't using a&amp;nbsp;&lt;a href="/blog/running-a-local-npm-repository-on-windows-server-using-verdaccio"&gt;locally cached&lt;/a&gt; (the&amp;nbsp;alpha version doesn't exist on npm!) copy of the package, in a simplified test-harness.&lt;/p&gt;
&lt;p&gt;The last hurdle to jump was the fact that these build scripts are run on Wndows and the majority of the globs are generated by using &lt;em&gt;path.join&lt;/em&gt; to concatenate their constituent parts. This always gives&amp;nbsp;results that contain \ which complicates things, especially as the scripts contain hundreds of instances of this. Our solution, for now, was to replace:&lt;/p&gt;
&lt;pre&gt;const join = require('path').join;
&lt;/pre&gt;
&lt;p&gt;With:&lt;/p&gt;
&lt;pre&gt;const path = require('path');
const join = (...args) =&amp;gt; path.join(...args).replace(/\\/g, '/');&lt;/pre&gt;
&lt;p&gt;With that in place, the main build scripts are now using a "real" version of&amp;nbsp;gulp, the builds are doing what they're&amp;nbsp;expected to&amp;nbsp;and all is once again well. For what it's worth,&amp;nbsp;having&amp;nbsp;dug into the changes it looks like the use of a later version of &lt;em&gt;glob-parent&lt;/em&gt; by gulp 4.0.2 is responsible for the change in behaviour.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;: Use / in globs, not \&lt;/p&gt;</description><pubDate>Wed, 24 Jul 2019 06:16:46 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/don-t-slash-your-globs-the-wrong-way</guid><category>gulp</category><category>npm</category><category>angularjs</category><category>javascript</category><category>glob</category></item><item><title>Refreshing the Windows icon cache</title><link>https://robertwray.co.uk:443/blog/refreshing-the-windows-icon-cache</link><description>&lt;p&gt;I just updated to the latest and greatest version of Visual Studio Code, but alas my taskbar was still showing the old icon:&lt;/p&gt;
&lt;p&gt;&lt;img class="img-responsive" alt="Windows 10 taskbar with the old Visual Studio Code icon showing after installing the version with the new icon" src="/Media/Default/Blog/taskbar_old_vscode_icon.PNG" /&gt;&lt;/p&gt;
&lt;p&gt;From this &lt;a href="https://github.com/microsoft/vscode/issues/71827#issuecomment-501374831"&gt;GitHub comment&lt;/a&gt;, here's a quick way to force the shell to refresh its icon cache, [Win]-[R] &amp;gt;&amp;nbsp;ie4uinit.exe -show and there it is:&lt;/p&gt;
&lt;p&gt;&lt;img class="img-responsive" alt="Windows 10 taskbar with the new Visual Studio Code icon showing after running the command to trigger an icon cache refresh" src="/Media/Default/Blog/taskbar_new_vscode_icon.PNG" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;</description><pubDate>Thu, 13 Jun 2019 07:52:00 GMT</pubDate><guid isPermaLink="true">https://robertwray.co.uk:443/blog/refreshing-the-windows-icon-cache</guid><category>visual studio code</category><category>windows</category><category>windows 10</category></item></channel></rss>