Can .Net Class Libraries Utilise app.config files?

A few months back I was migrating a .Net solution with 110 projects from .Net 4.5 to .Net 4.6.2 as well as upgrading the Azure SDK. Each project came with its own set of NuGet packages. As you can guess, there were a decent number of packages that needed to be upgraded. Given that the whole solution required testing after the upgrade, then was as good a time as any to bring everything up to date.

When it was time to merge this branch back to the main development branch, there were a number of conflicts, mostly in app.config files belonging to class library projects. Virtually every class library project in the solution had an app.config which didn’t sit right with me. It was something I’d often noticed but never really given any thought to, until now that it was causing some pain.

Does an app.config file in a class library project do anything?

In a nutshell, no, but with a caveat. Configuration files have application wide scope, not assembly wide scope (ignoring machine configurations and publisher policy files). Even though the configuration files contain assembly binding redirects and other important things, they’re really not used (unless you have code to load settings from a local configuration file). It’s one of those things that you know the answer too, but there is a shadow of a doubt in there.

So why do these files appear?

Continue reading

NLog Timestamp Accuracy vs Performance

During a recent debugging session involving some juicy threading issues, the logs were showing some scary stuff. Given that we’re talking about threading issues, order of operations and timing is everything. And the operations were all out of order! When the log entries were ordered by record ID you have one sequence of events yet when ordered by timestamp you have another sequence. In terms of the desired behaviour, one is spot on (when sorted by ID), the other is very wrong.

Continue reading

SQL Error SQL72045 When Importing a BACPAC

Problem

You receive the following error when trying to import a BACPAC that was generated on a different server:

TITLE: Microsoft SQL Server Management Studio
------------------------------

Could not import package.
Warning SQL0: A project which specifies Microsoft Azure SQL Database v12 as the target platform may experience compatibility issues with SQL Server 2014.
Error SQL72014: .Net SqlClient Data Provider: Msg 12824, Level 16, State 1, Line 5 The sp_configure value 'contained database authentication' must be set to 1 in order to alter a contained database.  You may need to use RECONFIGURE to set the value_in_use.
Error SQL72045: Script execution error.  The executed script:
IF EXISTS (SELECT 1
           FROM   [master].[dbo].[sysdatabases]
           WHERE  [name] = N'$(DatabaseName)')
    BEGIN
        ALTER DATABASE [$(DatabaseName)]
            SET CONTAINMENT = PARTIAL 
            WITH ROLLBACK IMMEDIATE;
    END

Error SQL72014: .Net SqlClient Data Provider: Msg 5069, Level 16, State 1, Line 5 ALTER DATABASE statement failed.
Error SQL72045: Script execution error.  The executed script:
IF EXISTS (SELECT 1
           FROM   [master].[dbo].[sysdatabases]
           WHERE  [name] = N'$(DatabaseName)')
    BEGIN
        ALTER DATABASE [$(DatabaseName)]
            SET CONTAINMENT = PARTIAL 
            WITH ROLLBACK IMMEDIATE;
    END

 (Microsoft.SqlServer.Dac)

Solution

Execute the following T-SQL on your local instance:

sp_configure 'contained database authentication', 1;  
GO  
RECONFIGURE;  
GO  

Background

Initially I thought this error was arising due to enabling Transparent Data Encryption (TDE) on SQL Azure databases, but in actual fact it was down to switching to database level authentication rather than relying on server level logins.

A side-effect of this change was that the database is now considered to be (partially) contained, i.e. it has no hard dependencies on master. Read more on Contained Databases here. Support for contained databases is disabled by default as they have security implications. From the contained database authentication Server Configuration Option MSDN entry:

When contained databases are enabled, database users with the ALTER ANY USER permission, such as members of the db_owner and db_accessadmin database roles, can grant access to databases and by doing so, grant access to the instance of SQL Server. This means that control over access to the server is no longer limited to members of the sysadmin and securityadmin fixed server role, and logins with the server level CONTROL SERVER and ALTER ANY LOGIN permission. Before allowing contained databases, you should understand the risks associated with contained databases.

RDP With a Retina / HDPI Display

If you own a device with a high-resolution display and ever find yourself using RDP to connect to a remote machine, chances are you’ve hit the problem of the RDP client not honouring your scaling settings and displaying the remove machine with the full resolution. Not sure what I mean? Take a look at this…

Remote machine over an RDP instance

There are myriad solutions out there talking about either using mRemoteNG or an outdated version of Remote Desktop Connection Manager (the solution works with v2.2, the latest, at the time of writing, being v2.7). So what do?

Use the latest version of Remote Desktop Connection Manager and disable the compatibility option: ‘Disable display scaling on high DPI settings’.

  1. After installing Remote Desktop Connection Manager, right click on program and select Properties
  2. Select the ‘Compatibility’ tab
  3. Uncheck the setting ‘Disable display scaling on high DPI settings’
  4. Enjoy headache free RDP sessions!

Disable display scaling on high DPI settings

Note, you’ll still need to use RDCM to initiate the connection as this won’t magically apply to the standard Remote Desktop Connection program.

TypeLoadException when using SqlColumnEncryptionAzureKeyVaultProvider

Problem

An exception is thrown when trying to use the SqlColumnEncryptionAzureKeyVaultProvider for working with Always Encrypted columns in SQL Azure.

An unhandled exception of type 'System.TypeLoadException' occurred in Hyak.Common.dllInheritance security rules violated by type: 'System.Net.Http.WebRequestHandler'. Derived types must either match the security accessibility of the base type or be less accessible.

Solution

There’s an issue with the 4.1 version of System.Net.Http assembly. Downgrade to version 4.0.

Background

I’m working on a data migration project that involves moving encrypted data over to Always Encrypted columns. The first part of getting that up and running is to get the Always Encrypted SQL database driver up and running. I’ve worked a fair bit with the Azure Key Vault so was surprised to see the exception detailed above when first running my application. The stack trace wasn’t overly helpful:

   at Microsoft.Azure.Common.Platform.HttpTransportHandlerProvider.CreateHttpTransportHandler()
   at Hyak.Common.ServiceClient`1..ctor()
   at Microsoft.Azure.KeyVault.Internal.KeyVaultInternalClient..ctor()
   at Microsoft.Azure.KeyVault.Internal.KeyVaultInternalClient..ctor(KeyVaultCredential credentials)
   at Microsoft.Azure.KeyVault.KeyVaultClient..ctor(AuthenticationCallback authenticationCallback)
   at Microsoft.SqlServer.Management.AlwaysEncrypted.AzureKeyVaultProvider.SqlColumnEncryptionAzureKeyVaultProvider..ctor(AuthenticationCallback authenticationCallback) in d:\DsMain\DS_Main\Sql\mpu\shared\Security\AzureKeyVaultProvider\SqlColumnEncryptionAzureKeyVaultProvider.cs:line 56
   at AlwaysEncryptedKeyVaultIssue.Program.InitializeAzureKeyVaultProvider() in C:\Dev\AlwaysEncryptedKeyVaultIssue\AlwaysEncryptedKeyVaultIssue\Program.cs:line 21
   at AlwaysEncryptedKeyVaultIssue.Program.Main(String[] args) in C:\Dev\AlwaysEncryptedKeyVaultIssue\AlwaysEncryptedKeyVaultIssue\Program.cs:line 44
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

After a fair bit of Googling, I eventually came across up a fairly fresh Microsoft Connect ticket detailing the issue.

The issue really is not obvious and hats off to ‘lucavgobbi’ for diagnosing the root cause! Luckily it’s quite simple to remedy, although there are a few caveats.

For the purpose of this post, I created a simple test project that demonstrates the issue. It’s very easy to create using the following steps.

  1. Create a new console application (targeting 4.6.1)
  2. Install the following 2 Nuget packages:
    • Install-Package Microsoft.SqlServer.Management.AlwaysEncrypted.AzureKeyVaultProvider
    • Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory
  3. Copy this example Program.cs from the demo source.
  4. Run the app and watch it explode!

On running the application, you’ll be presented with the lovely exception from above. So what do?

As described in the Microsoft Connect issue, we need to switch the version of System.Net.Http from v4.1 to v4.0. It’s important to note that this dependency is added via Nuget as a dependency of which is the first gotcha. Run the following command to downgrade the package:

Update-Package System.Net.Http -Version 4.0.0

Run the application again and BOOM, it still fails… Ok…. The problem here lies in your app / web confg (App.config or web.config). An assembly binding redirect is added when the package is installed, redirecting to version 4.1.0, totally subverting our package downgrade. This is again simple to fix, change the dependency from this:

<dependentAssembly>
    <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.1.0.0" />
</dependentAssembly>

To look like this:

<dependentAssembly>
    <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
    <bindingRedirect oldVersion="0.0.0.0-4.1.0.0" newVersion="4.0.0.0" />
</dependentAssembly>

Now you’re good to go!

Browse the demo source code on GitHub.

Source Code for the application’s Program.cs:

namespace AlwaysEncryptedKeyVaultIssue
{
    using Microsoft.IdentityModel.Clients.ActiveDirectory;
    using Microsoft.SqlServer.Management.AlwaysEncrypted.AzureKeyVaultProvider;
    using System;
    using System.Collections.Generic;
    using System.Data.SqlClient;
    using System.Threading.Tasks;

    public class Program
    {
        private static ClientCredential clientCredential;

        static void Main(string[] args)
        {
            InitializeAzureKeyVaultProvider();
        }

        public static void InitializeAzureKeyVaultProvider()
        {
            var clientId = Guid.NewGuid().ToString();
            var clientSecret = "ITS_A_SECRET";

            clientCredential = new ClientCredential(clientId, clientSecret);

            var azureKeyVaultProvider = new SqlColumnEncryptionAzureKeyVaultProvider(GetToken);

            var providers = new Dictionary<string, SqlColumnEncryptionKeyStoreProvider>()
            {
                { SqlColumnEncryptionAzureKeyVaultProvider.ProviderName, azureKeyVaultProvider }
            };

            SqlConnection.RegisterColumnEncryptionKeyStoreProviders(providers);
        }

        public async static Task<string> GetToken(string authority, string resource, string scope)
        {
            var authContext = new AuthenticationContext(authority);
            AuthenticationResult result = await authContext.AcquireTokenAsync(resource, clientCredential);

            if (result == null)
                throw new InvalidOperationException("Failed to obtain the access token");

            return result.AccessToken;
        }
    }
}

Enforcing BuildAction on VS Project Items

Whilst creating an application to run the excellent SQL migration tool DbUp as part of a continuous deployment setup, I stumbled across a question I hadn’t thought about before. Is it possible to enforce a particular build action on certain files in a Visual Studio project? In this particular case, making sure all SQL files are marked as EmbeddedResource.

With DbUp, there are several ways of supplying SQL files to be run during a migration. In my case, I don’t know which set of scripts need to be run until the deployment is underway. After toying around with several ideas, I settled on the approach of embedding the SQL files in to the migration tool. I can then use the connection string that’s passed in to determine a specific folder of scripts to be executed. Having the scripts as embedded resources will allow me to package up the migration tool as an artefact that can be run only when needed, i.e. during a deployment and not during a build.

So how does it work?

There are several ways of adding custom targets to a project file. After Googling around I came across the ever helpful StackOverflow which offered up some solutions. The first approach is simple, but would require you to add a case for each of the BuildAction types that exist. And if a new one is added at some point, you have to update your code. No thanks, I’m too lazy for that.

The second approach looked much more maintainable by focusing on everything that isn’t what you want. The problem was that it didn’t work! Nothing would ever be matched. I wasn’t able to see any information in Visual Studio, so ran the build from the command line which told me the target was being run, but no elements were matched.

After lots of trial and error and head scratching, I came across another StackOverflow post detailing that you need to specify the namespace for elements in the XPath query. Of course!

The following code is placed in to the project file that you wish to run the check in.

<Target Name="EnsureSQLScriptsAreEmbeddedResource" BeforeTargets="BeforeBuild">
    <XmlPeek 
        XmlInputPath="$(MSBuildProjectFile)" 
        Namespaces="&lt;Namespace Prefix='msb' Uri='http://schemas.microsoft.com/developer/msbuild/2003'/&gt;" 
        Query="/msb:Project/msb:ItemGroup/*[not(self::msb:EmbeddedResource)]/@Include">
            <Output TaskParameter="Result" ItemName="AllItems" />
    </XmlPeek>
	
    <ItemGroup>
        <Filtered Include="@(AllItems)" Exclude="SqlTemplate.sql" Condition="'%(Extension)' == '.sql'" />
    </ItemGroup>
    <Error 
        Code="SQL" 
        ContinueOnError="ErrorAndContinue"
        File="$(MSBuildProjectDirectory)\%(Filtered.Identity)" 
        Text="All scripts require a BuildAction of EmbeddedResource" 
        Condition="'@(Filtered)'!=''" />
</Target>

What’s going on here then?

  1. The XmlPeek task will take the project file and run the XPath query, collecting all matching attributes and sticking them in to a new parameter called AllItems.
  2. The XPath query works by looking at child elements of each ItemGroup element, matching against any that are not of type EmbeddedResource.
  3. For each of these, it will take the value of the Include attribute and use that to populate AllItems.
  4. The ItemGroup that follows will filter the AllItems collection to include only things which end with ‘.sql’. These will be stored in a new parameter called Filtered.
  5. Finally, the Error target will be invoked if any items made it through. Including the ContinueOnError attribute makes it possible to see all of the files that cause the build to fail, rather than stopping on the first one.

Props to StackOverflow users jessehouwing and Teun D for getting me on the right path!

Migrating a TFVC shelveset to Git with Git-Tfs

You’ve migrated your code base (or as much of it as you can) from TFVC and now a user just realised they need some super important code from a shelveset. How does one go about migrating this? As far as I see it, you have three options.

  1. Manually copy the changes over.
  2. Unshelve the changes, commit to the branch and pull in to the Git repository.
  3. Use the git-tfs unshelve command.

In my case the shelve was too big for option 1 and too old / contained breaking changes for 2, leaving option 3.

One thing to note that when migrating a shelve from TFS, it will create a new branch against master. In my case, I wanted a new branch against a development branch.

What I want:

master
      _development
                   _shelveset branch

What git-tfs does:

master
    _development
 _shelveset branch

But hey, this is Git, so it’s no problem!!

# Get the shelveset from TFS and stick it in to a new branch
git tfs unshelve -u="Shelveset Owner Name" "Shelveset Name" TargetBranchName_TEMP

# Checkout that new branch
git checkout TargetBranchName_TEMP

# Get the hash for the head of the new branch
git log --pretty=format:%h -n 1

# Now switch to your dev branch
git checkout development

# Create a new branch for the shelveset
git branch TargetBranchName
git checkout TargetBranchName

# Merge the temp shelveset branch in to it's final location
git cherry-pick HASH

# You might have some things to merge now.
git commit -m "Migrated shelveset 'Shelveset Name'"

ASP.Net Cookies[FormsAuthentication.FormsCookieName] always null

Now and then you forget some small detail that has you running in circles trying to figure something out. In my case it was an authentication cookie not being persisted. After trying a few options, I realised that a list of claims were being persisted in the user data parameter! Of course, a cookie can only be 4096 bytes 😉

Azure Storage Explorer – Crash When Removing Account

The Azure Storage Explorer tool is great for when you need to work with an Azure Storage Account. I’m running version 6, preview 3 (6.0.3.1) and recently ran in to a problem.

Having deleted and re-created a storage account in a new subscription, I needed to update the access key in Storage Explorer. Pretty simple right? There’s certainly a missing ‘Manage Connections’ view, or perhaps there is and I wasn’t able to find it yet.

First you have to select the storage account to remove, wait for it to fail to connect, then click on the ‘Remove’ button. The trouble is that once you click on ‘Remove’ the app quits. Hmmm.

It turns out that the storage accounts are stored in a single file:

C:\Users\{UserName}\AppData\Roaming\Neudesic\AzureStorageExplorer\{Version}\AzureStorageExplorer6.dt1

NOTE: You should probably take a backup of this file before attempting to remove it / change it.

I didn’t have the time to figure out if I could edit this file to remove the troublesome string, so took the nuclear option and deleted it. On starting the app I was now back to an empty connection list! Superb! Whilst I had to add all of my account details in again, I was at least able to continue on my merry way.

If you have a neater way of handling this, please share it with me!

Xamarin: XLabs 2.0 iOS IGeolocator Woes

Having completed an Android app using Xamarin.Forms, I was tasked with making the iOS counterpart. Well it’s Xamarin so ‘write once, run everywhere’ right? Haha, ok sure.

This particular issue was pretty confusing but was related to changes in the XLabs breaking changes introduced in v2, in particular this inconspicuous line:

You will have to use DependencyService.Register<>() on your platforms packages to register XLabs Services.

Add this to your app delegate and you should be good to go:

DependencyService.Register<IGeolocator, Geolocator>();

There’s also this issue to watch out for too: https://github.com/XLabs/Xamarin-Forms-Labs/issues/657