Sql server 为什么在SQL Server CLR中运行一个函数会导致崩溃,而在独立应用程序中运行正常?

Sql server 为什么在SQL Server CLR中运行一个函数会导致崩溃,而在独立应用程序中运行正常?,sql-server,sqlclr,Sql Server,Sqlclr,下面这两个方法类似,只是一个处理空值,另一个不处理。为了处理空值,它使用SqlString类型并检查“get\u IsNull”属性 为什么第一个会导致错误“在执行用户定义例程或聚合“CheckMailingAddress”期间发生.NET Framework错误:“在SQL CLR内运行时,而第二个不会 特别是,TSQL错误是“Msg 10329,级别16,状态49,第1行.Net Framework执行被中止。” 请记住,据我所知,MSIL是正确的,因为这两种方法在独立应用程序中调用时都有效

下面这两个方法类似,只是一个处理空值,另一个不处理。为了处理空值,它使用SqlString类型并检查“get\u IsNull”属性

为什么第一个会导致错误
“在执行用户定义例程或聚合“CheckMailingAddress”期间发生.NET Framework错误:“
在SQL CLR内运行时,而第二个不会

特别是,TSQL错误是“Msg 10329,级别16,状态49,第1行.Net Framework执行被中止。”

请记住,据我所知,MSIL是正确的,因为这两种方法在独立应用程序中调用时都有效。只有在SQL CLR内部调用时,这两个函数中的第一个才会崩溃。在SQLCLR中,函数是用“nvarchar(4000)”类型定义的,据我所知,它应该可以很好地处理SqlString

我可能也可以使用“string”实现第一个方法,并且仍然执行null检查,但是它使用SqlString来利用不可调用的接口属性“IsNull”和“Value”,因为它是通用代码生成器的一部分,可以使用其他Sql*类型

简单问题摘要: 对于那些被方法体中的MSIL分心的人;别理它。我将函数重新编译为完全不做任何事情,我的观点是,当输入类型为“SqlString”而不是“string”时,CLR将启动并终止,没有错误消息,返回值为NULL而不是TRUE

//Crashes when input parameter is "SqlString"
.method public hidebysig static bool CheckMailingAddress(valuetype [System.Data]System.Data.SqlTypes.SqlString param0) cil managed
{
   .maxstack 8
    L_001f: ldc.i4.1 
    L_0020: ret 
}

//Doesn't Crash when input parameter is "string"
.method public hidebysig static bool CheckMailingAddress(string param0) cil managed
{
   .maxstack 8 
    L_0007: ldc.i4.1 
    L_0008: ret 
}

我找到了问题的根源,并且能够解决它,但我不确定细节

在某个时候,我将我的DeployDatabaseAssembly项目切换到目标.NET 4.0,AssemblyBuilder必须生成一个同样以.NET 4.0为目标的程序集。将项目切换到目标.NET3.5解决了这个问题

有趣的是,包含我所有数据类型的源DLL(database.DLL)仍然以.NET 3.5为目标,并且故意这样做,因为我知道SQL Server现在只支持CLR 2.0,这实际上使它与.NET 4.0不兼容,因为.NET 4.0似乎需要CLR 4.0。使用ILMerge,我将包含生成函数的动态程序集与现有的(.NET 3.5)database.dll相结合。这最终导致了混合汇编.NET4.0汇编,它主要基于.NET3.5特性和类。奇怪的是,我能够让使用基本“String”和“int”类型参数的函数工作,但是SqlString类型导致崩溃。。。显然是因为它是从.NET 4.0 System.Data.dll中提取的,因为它在我的DeployDatabaseAssembly中被引用为“typeof(SqlString)”,它的目标是.NET 4.0。奇怪的是,它是如何崩溃的,没有任何类型的错误消息,也没有任何类型的关于它与加载的SQL CLR模块不兼容的警告

我希望我知道一种强制运行在.NET 4.0应用程序中的AssemblyBuilder生成以.NET 3.5为目标的程序集的方法

更新:问题彻底解决 我专注于ILMerge的输出,继续将DeployDatabaseAssembly切换回.NET4.0。顺便说一下,我在项目中使用ILMerge作为引用,因为它是一个.NET程序集

通过如下设置ILMerge选项:

ILMerge merger = new ILMerge();
merger.SetTargetPlatform( "v2", @"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client");
结果DLL部署到SQL Server(与以前一样),但这次它实际上运行时没有错误

有趣的是,如果我仅将目标平台路径中的“v3.5”替换为“v4.0”,并尝试将程序集部署到SQL Server,则在部署过程中会立即收到一条有用的错误消息:“为程序集“我的程序集名称”创建程序集失败,因为该程序集是为不受支持的CLR运行时版本生成的。”。奇怪的是,当我根本没有设置任何目标平台时,它可以很好地部署,但在没有任何错误消息的情况下崩溃了

此表总结了上述配置组合和结果:

您是否尝试将Visual Studio调试器附加到此服务器?另外,您是否可以使用原始语言(即C#、VB等)而不是MSIL包含代码?MSIL是原始语言。是的,我已经附加了调试器。它失败了,除了我给出的错误消息之外,没有任何特定的错误消息,而且在CLR中也没有遇到任何类型的断点。哇,老兄,你是在MSIL中编写CLR程序吗?我能问一下为什么吗?你有没有试过看看什么编译的C#做同样的事情?是的!应用程序和数据库的统一数据类。单点维护+快速SQL CLR检查约束函数。@Triynko:如果看起来太难,通常是这样。问题最终超出了您发布的代码范围,这并不奇怪。但是作为一个补充,你有没有考虑过其他人可能不得不维持这一事实?除非您的性能要求是绝对的杀手级速度,否则我建议您使用C语言,因为有更多的人可以读/写C语言,而不是读/写MSIL。您可能会为公司的下一位开发人员留下大量难以理解的代码。再说一次,如果这真的是一个要求,那就是必须付出的代价。它必须是绝对的杀手级速度,并且直接发射MSIL确保在任何情况下都不会有编译器在混合中注入低于最佳的代码。该程序实际上完全是用C#编写的,并且使用ILGenerator类及其Emit方法来发出MSIL操作代码(System.Reflection.Emit.OpCodes enum),因此实际上不必处理MSIL语法。发布的MSIL是通过在.NET Reflector中打开生成的程序集获得的。至于其他开发人员的考虑。。。您是否意识到此实用程序的全部目的是使他们的工作更轻松。虽然实用程序本身很复杂,但它不是要更新的,因为它是
ILMerge merger = new ILMerge();
merger.SetTargetPlatform( "v2", @"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v3.5\Profile\Client");