I have migrated my on-premise SQL Server 2008 R2 databases to Azure SQL Server Managed Instance (SQL Server 2017). One database is an archive (read-only) database, one is the OLTP database, and the third is an utility database, where I keep generic functions, stored procedures, and maintenance scripts. Everything went very smoothly for all three DBs except for a CLR assembly in the utility DB. The assembly provides Regex functionality in TSQL code - very useful! I based it off of the Phil Factor code here. Originally it was loaded into an on-premise DB from the compiled DLL. It works like a champ there. But on SQL MI, I get the following error when running a SP that uses one of the CLR functions.
Msg 10314, Level 16, State 11, Procedure dbo.globalSearch, Line 22 [Batch Start Line 2]
An error occurred in the Microsoft .NET Framework while trying to load assembly id 65541. The server may be running out of resources, or the assembly may not be trusted. Run the query again, or check documentation to see how to solve the assembly trust issues. For more information about this error:
System.IO.FileLoadException: Could not load file or assembly 'regexsqlclr, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. An error relating to security occurred. (Exception from HRESULT: 0x8013150A)
System.IO.FileLoadException:
at System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, RuntimeAssembly reqAssembly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks)
at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean forIntrospection)
at System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection)
at System.Reflection.Assembly.Load(String assemblyString)
I've tried solving the assembly trust issues using the steps in this MSDN post, specifically executing
sys.sp_add_trusted_assembly
which succeeds, but changes nothing. Then I thought that since it is suggesting that it cannot load from file, which seems to make sense since the file does not exist in SQL MI where I have no access to the file system, that I should try dropping and recreating from varbinary
. I only said that it seems to make sense because the file also does not exist on any of my other on-premise servers other than the one I originally loaded it from, and it works perfectly on all of them. But, I'm willing to try anything! So using SSMS I scripted out the assembly as DROP
and CREATE
, which uses the FROM BINARY
syntax, and scripted out all of the functions likewise. CREATE ASSEMBLY
succeeds, so I'm thinking I'm on the right track. Then I try to create the first function and BAM, another error! This time the error reads
Msg 6505, Level 16, State 2, Procedure RegexIndex, Line 2
Could not find Type 'RegexSQLCLR.RegularExpressionFunctions' in assembly 'RegexFunctions'.
I've been Googling for hours trying to find a solution to that problem and have had zero luck. Even though that syntax for the class portion of the EXTERNAL NAME
clause works perfectly for the assembly loaded from file. I verified that the varbinary
scripted out by SSMS is identical to the binary of the original DLL. Someone on a Microsoft forum suggested that I make sure the DLL was compiled with the Any CPU
option - it was. As a sanity check, I did the same procedure on one of the on-premise servers, i.e., DROP
and CREATE ASSEMBLY FROM BINARY
, and got the exact same result: I cannot load any of the CLR functions! I've tried every conceivable permutation of the class names that I can think of, to no avail. Here is the code for CREATE ASSEMBLY
and CREATE FUNCTION
CREATE ASSEMBLY [RegexFunction]
AUTHORIZATION [dbo]
FROM 0x4D5A90000 *truncated_for_brevity*
WITH PERMISSION_SET = SAFE
CREATE FUNCTION RegExIndex
(
@Pattern NVARCHAR(4000),
@Input NVARCHAR(MAX),
@Options int
)
RETURNS int
AS EXTERNAL NAME
RegexFunction.[RegexSQLCLR.RegularExpressionFunctions].RegExIndex
GO
RegexSQLCLR
is the name of the original DLL and RegularExpressionFunctions
is the name of the class. RegexSQLCLR
is also the name specified in the name
column of sys.assembly_files
after using CREATE ASSEMBLY FROM BINARY
; the full path to the original DLL is in the name
column otherwise.