Castle.Windsor crea una instancia de la versión incorrecta de SqlConnection con Dapper

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

Pregunta

Estamos teniendo un problema extraño cuando usamos Castle.Windsor para crear una instancia de SqlConnection usando una fábrica tipeada:

El registro se ve así:

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

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

Y el IDbConnectionFactory :

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

Ahora, cuando intento acceder a una nueva conexión usando este código:

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

}

Me sale una excepción:

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.

El problema con esta excepción es que el tipo SqlConnection en System.Data para .NET 4.5.1 no contiene la propiedad AccessToken mientras que el de .NET 4.6 sí lo hace. En otras palabras, si trato de hacer manualmente

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

Obtengo un error de compilación si el proyecto está configurado para .NET 4.5.1, pero un error de tiempo de ejecución al configurar AccessToken si está configurado para .NET 4.6.

¿Alguna idea de por qué Castle.Windsor intenta crear una v4.6 SqlConnection en lugar de .NET 4.5.1?

Solución / Hack

Puedo evitar el problema diciéndole a Castle que ignore la propiedad, pero parece un truco. Hacer esto requiere que lo agregue al PropertiesIgnore en el registro:

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

Respuesta aceptada

Todas las versiones de .NET desde 4.5 están en su lugar, como se puede ver aquí .

Esto significa que una vez que haya instalado .NET 4.6, siempre obtendrá la versión .NET 4.6 de SqlConnection independientemente de cómo la instancia.

Al compilar su aplicación en Visual Studio, compila con una versión específica de .NET Framework que normalmente se encuentra en una carpeta en: C: \ Archivos de programa (x86) \ Reference Assemblies \ Microsoft \ Framework.NETFramework

Esto significa que al compilar msbuild puede comprobar que no está utilizando algo que no está disponible en la versión de marco a la que se dirige.

Sin embargo, cuando ejecuta su aplicación de 64 bits utilizará los ensamblajes típicamente ubicados en C: \ Windows \ Microsoft.NET \ Framework64 \ v4.0.30319

Esta es la misma carpeta para todas las versiones de .NET 4.0 a .NET 4.6, esto es lo que significa la actualización.

Por lo tanto, cuando ejecuta su aplicación en su entorno de desarrollo que tiene .NET 4.6 instalado, siempre obtendrá la versión .NET 4.6 (al menos a menos que haga algo especial para cargar otras versiones de los ensamblajes).

Castle Windsor intentará establecer propiedades con setter público y utilizará la reflexión para encontrar las propiedades, lo que significa que encontrará las propiedades de .NET 4.6 en una máquina .NET 4.6, incluso si compila contra 4.5.1.

La razón por la que falla cuando intenta establecer AccessToken es muy probable porque su cadena de conexión no es compatible con la configuración de AccessToken.

Si comprueba el código fuente del setter AccessToken, verá que generará una excepción si intenta establecerlo para una cadena de conexión incompatible, incluso si solo intenta configurar AccessToken en la cadena vacía.

Como no necesita inyectar ninguna dependencia en el objeto SqlConnection, puede crearlo simplemente usando el nuevo operador y luego evitar el problema causado por los intentos de Windsors de inyectar las propiedades de la conexión. El uso de este registro debería funcionar:

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


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué