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)));
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)));