Castle.Windsor 使用 Dapper 实例化错误版本的 SqlConnection

Castle.Windsor instantiating wrong version of SqlConnection with Dapper

我们在使用 Castle.Windsor 通过类型化工厂实例化 SqlConnection 时遇到了一个奇怪的问题:

注册看起来像这样:

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

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

IDbConnectionFactory:

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

现在,当我尝试使用此代码访问新连接时:

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

}

我遇到异常:

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.

此异常的问题是 .NET 4.5.1 System.Data 中的类型 SqlConnection 不包含 属性 AccessToken 而 .NET 中的类型。 NET 4.6 可以。换句话说,如果我尝试手动执行

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

如果项目是为 .NET 4.5.1 配置的,我会收到构建错误,但如果项目是为 .NET 4.6 配置的,则会在设置 AccessToken 时出现运行时错误。

知道为什么 Castle.Windsor 尝试创建 v4.6 SqlConnection 而不是 .NET 4.5.1 吗?

Workaround/Hack

我可以通过告诉 Castle 忽略 属性 来解决这个问题,但这似乎是一个 hack。这样做需要我将其添加到注册中的PropertiesIgnore

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

自 4.5 以来的所有 .NET 版本都已就位更新 as you can see here.

这意味着一旦安装了 .NET 4.6,无论您如何实例化它,您将始终获得 .NET 4.6 版本的 SqlConnection。

在 Visual Studio 中构建应用程序时,您针对特定版本的 .NET Framework 进行构建,该版本通常位于以下文件夹中: C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETFramework

这意味着在构建时 msbuild 可以检查您是否使用了您所定位的框架版本中不可用的东西。

然而,当您 运行 您的 64 位应用程序时,它将使用通常位于 C:\Windows\Microsoft.NET\Framework64\v4.0.30319

中的程序集

这是从 .NET 4.0 到 .NET 4.6 的所有版本的同一文件夹,这就是就地升级的意思。

因此,当您在安装了 .NET 4.6 的开发环境中执行应用程序时,您将始终获得 .NET 4.6 版本(至少除非您执行一些特殊操作来加载其他版本的程序集)。

Castle Windsor 将尝试 set properties with public setter 并且它将使用反射来查找属性,这意味着它将在 .NET 4.6 计算机上找到 .NET 4.6 属性,即使您是针对 4.5 构建的。 1.

尝试设置 AccessToken 失败的原因很可能是因为您的连接字符串与设置 AccessToken 不兼容。

如果您检查 AccessToken setter 的 source code,您会发现如果您尝试为不兼容的连接字符串设置它,它会抛出异常,即使您只是尝试设置AccessToken 为空字符串。

由于您不需要将任何依赖项注入到 SqlConnection 对象中,您不妨使用 new 运算符简单地创建它,然后避免因 Windsors 尝试注入连接属性而引起的问题。 使用此注册应该有效:

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