Castle.Windsor instantiating wrong version of SqlConnection with Dapper

.net-4.5 .net-4.6 c# castle-windsor dapper

Question

We're having a weird problem when using Castle.Windsor to instantiate an SqlConnection using a typed factory:

The registration looks like this:

container.Register(Component.For<IDbConnectionFactory>().AsFactory().LifestyleTransient());

container.Register(Component.For<IDbConnection>().ImplementedBy<SqlConnection>()
            .LifestyleTransient()
            .DependsOn(Dependency.OnValue<string>
            (ConfigurationManager.ConnectionStrings["DbConnectionString"].ConnectionString)));

And the IDbConnectionFactory:

public interface IDbConnectionFactory
{
    IDbConnection Create();
    void Release();
}

Now, when I try to access a new connection using this code:

using (var connection = _connectionFactory.Create())
{

}

I get an exception:

An unhandled exception of type 
'Castle.MicroKernel.ComponentActivator.ComponentActivatorException' occurred 
in Castle.Windsor.dll

Additional information: Error setting property SqlConnection.AccessToken in component 
System.Data.SqlClient.SqlConnection. See inner exception for more information.

If you don't want Windsor to set this property you can do it by either decorating it 
with DoNotWireAttribute or via registration API.

Alternatively consider making the setter non-public.

The problem with this Exception is that the type SqlConnection in System.Data for .NET 4.5.1 does not contain the property AccessToken whereas the one in .NET 4.6 does. In other words, if I try to manually do

var connection = new SqlConnection("connectionstring");
connection.AccessToken = "";

I get a build-error if the project is configured for .NET 4.5.1, but a runtime error on setting the AccessToken if it's configured for .NET 4.6.

Any idea why Castle.Windsor attempts to create a v4.6 SqlConnection instead of a .NET 4.5.1?

Workaround/Hack

I can get around the problem by telling Castle to ignore the property, but this seems like a hack. Doing this requires me to add it to the PropertiesIgnore in the registration:

container.Register(Component.For<IDbConnection>().ImplementedBy<SqlConnection>()
            .PropertiesIgnore(info => info.Name.Equals("AccessToken"))
            .LifestyleTransient()
            .DependsOn(Dependency.OnValue<string>          
             (ConfigurationManager.ConnectionStrings["DbConnectionString"].ConnectionString)));

Accepted Answer

All .NET versions since 4.5 are in place updates as you can see here.

This means that once you have installed .NET 4.6 you will always get the .NET 4.6 version of SqlConnection regardless of how you instantiate it.

When building your application in Visual Studio you build against a specific version of the .NET framework typically located in a folder under: C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework

This means that when building msbuild can check that you are not using something which isn't available in the framework version you are targeting.

However when you run your 64 bit application it will use the assemblies typically located in C:\Windows\Microsoft.NET\Framework64\v4.0.30319

This is the same folder for all versions from .NET 4.0 through .NET 4.6, this is what in place upgrade means.

So when you execute your application on your developement environment that has .NET 4.6 installed, you will always get the .NET 4.6 version (at least unless you do something special to load other versions of the assemblies).

Castle Windsor will try to set properties with public setter and it will use reflection to find the properties which means that it will find the .NET 4.6 properties on a .NET 4.6 machine, even if you are building against 4.5.1.

The reason it fails when it tries to set the AccessToken is most likely because your connection string is not compatible with setting AccessToken.

If you check the source code of the AccessToken setter you will see that it will throw an exception if you try to set it for a incompatible connection string, even if you only try to set the AccessToken to the empty string.

As you don't need to inject any dependencies into the SqlConnection object you may as well create it simply using the new operator and then you avoid the problem caused by Windsors attempts to inject the properties of the connection. Using this registration should work:

container.Register(Component.For<IDbConnection>().ImplementedBy<SqlConnection>()
        .LifestyleTransient()
        .UsingFactoryMethod(() => new SqlConnection          
       (ConfigurationManager.ConnectionStrings["DbConnectionString"].ConnectionString)));



Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why