Refreshing changed .NET SQL CLR assemblies after patching/updates

After applying some Windows updates to one of my servers, I started getting the following error when I ran a customer .NET SQL-CLR stored proc:

Server: Msg 6522, Level 16, State 2, Line 1
A .NET Framework error occurred during execution of user defined routine or aggregate ‘somemethodyourecalling’:

System.IO.FileLoadException: Could not load file or assembly ‘System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a’ or one of its dependencies. Assembly in host store has a different signature than assembly in GAC. (Exception from HRESULT: 0x80131050)

I’d imported some additional assemblies into SQL Server for use in SQL CLR mapped stored procedures, and the Windows updates had included a service pack for .NET, which changed some of the assemblies. Now the version I’d linked to SQL Server no longer existed on disk, and SQL couldn’t load it – that made sense, and is documented pretty clearly in this MS Support article: http://support.microsoft.com/kb/949080

However, I had dozens of servers with SQL CLR components enabled, and hundreds of different assemblies loaded across them all, and not always the same in each server, so a standard update script wouldn’t work to refresh all the changed assemblies (the MS Support link provides a list of the standard ones that cause that error, but if you’ve got custom assemblies loaded, or you’ve loaded an assembly that’s not specifically cleared for SQL CLR, then it’s not on the list either). To deal with this, I wrote a script that fetches the assembly list for a database and attempts to refresh every one of them from their disk location. If they haven’t changed, the update attempt will fail with a specific error message about the MVID, and there’s no change for that assembly.

Also, I’ve commented out the line that restricts it to just framework assemblies (System.* and Microsoft.*), but you can uncomment that line if you’d like to restrict the refresh from attempting to reload your custom assemblies as well.

DECLARE @AssemblyName VARCHAR(255),
	    @AssemblyLocation VARCHAR(255),
	    @AlterAssemblyCommand NVARCHAR(1024),
	    @DotNetFolder VARCHAR(100)

   SET @DotNetFolder = 'C:\Windows\Microsoft.NET\Framework\v2.0.50727'

CREATE TABLE #Results (
		AssemblyName VARCHAR(255),
		AssemblyLocation VARCHAR(255),
		AlterAssemblyCommand NVARCHAR(1024),
		Results VARCHAR(1024)
)

select sa.name as AssemblyName,
		saf.name as Assemblylocation,
		case when charindex('', saf.name) = 0
			then 'ALTER ASSEMBLY [' + sa.name + '] FROM ''' + @DotNetFolder
			else 'ALTER ASSEMBLY [' + sa.name + '] FROM '''
		end + saf.name + (case right(saf.name, 4) when '.dll' then '' else '.dll' end) + ''''
		as AlterAssemblyCommand
INTO #Refresh
from sys.assemblies sa
join sys.assembly_files saf
  on sa.assembly_id = saf.assembly_id
where sa.name <> ('Microsoft.SqlServer.Types')
  --and (sa.name like 'System.%' or sa.name like 'microsoft.%')

DECLARE Commands CURSOR FAST_FORWARD FOR
SELECT AssemblyName,
	   AssemblyLocation,
	   AlterAssemblyCommand
  FROM #Refresh

OPEN Commands

FETCH NEXT FROM Commands
INTO @AssemblyName,
	   @AssemblyLocation,
	   @AlterAssemblyCommand

WHILE @@FETCH_STATUS = 0
BEGIN

	BEGIN TRY
		exec sp_executesql @AlterAssemblyCommand

		insert into #Results
		SELECT @AssemblyName,
				@AssemblyLocation,
				@AlterAssemblyCommand,
				'Assembly refreshed successfully'

	END TRY
	BEGIN CATCH

		insert into #Results
		SELECT @AssemblyName,
				@AssemblyLocation,
				@AlterAssemblyCommand,
				CASE ERROR_NUMBER()
					WHEN 6285 THEN 'No update necessary (MVID match)'
					WHEN 6501 THEN 'Physical assembly not found at specified location (SQL Error 6501)'
					ELSE ERROR_MESSAGE() + ' (SQL Error ' + convert(varchar(10), ERROR_NUMBER()) + ')'
				END

	END CATCH

	FETCH NEXT FROM Commands
	INTO @AssemblyName,
		   @AssemblyLocation,
		   @AlterAssemblyCommand

END

CLOSE Commands
DEALLOCATE Commands

SELECT * FROM #Results

drop table #refresh
drop table #Results

While troubleshooting the error, I came across this as well – I don’t have a SQL 2012 server handy to check with, it looks like this problem might be resolved with a reboot in SQL 2012:

http://msdn.microsoft.com/en-us/library/hh479773.aspx

I’m not sure if that’s the case, but it would make things easier. Also, it would be nice if there was some kind of “ALTER ASSEMBLY [SomeAssembly] REFRESH” command to address this problem, but maybe in a future version.

Additionally, this error can be caused if the signing of an assembly has changed, but not necessarily the signature, but just reloading it from disk won’t work because the method that SQL Server uses to determine if there’s been a change is the MVID, and that doesn’t change unless there’s been a code change of some kind (see the Connect bug here). In those cases, you’ll actually need to drop and recreate the assembly, including any supporting objects that rely on that assembly.

Matching up SQL CE DLL version numbers

The Abstract:

While developing a Windows Mobile device application on two different computers, I thought I’d installed SQL Server Compact Edition 3.5 SP2 on both of them, but ran into some version difference issues. In researching it, I couldn’t find good information about the different versions of the System.Data.SqlCompactCe DLL, so thought some future developers might enjoy what I found out, all in one place.

The Problem:

After checking the project into source control on one computer and fetching it on the other, I’d see a broken reference to System.Data.SqlServerCe:

Missing reference to System.Data.SqlServerCe
Missing reference to System.Data.SqlServerCe

And I received the following message (and about 100 errors – one for every reference to the namespace in my code) when I attempted to compile:

Warning 10
Could not resolve this reference. Could not locate the assembly “System.Data.SqlServerCe, Version=3.5.1.0, Culture=neutral, PublicKeyToken=3be235df1c8d2ad3, processorArchitecture=MSIL”. Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors.

If I dropped the reference, re-added it on my second computer (BTW – only one version was listed in my .NET reference list on both machines) and checked it back in, then I’d get the same situation on my first computer with the following error message:

Warning 10
Could not resolve this reference. Could not locate the assembly “System.Data.SqlServerCe, Version=3.5.0.0, Culture=neutral, PublicKeyToken=3be235df1c8d2ad3, processorArchitecture=MSIL”. Check to make sure the assembly exists on disk. If this reference is required by your code, you may get compilation errors.

The Research:

I had a DLL version mismatch – a general problem when you’ve got multiple computers you’re developing something on, since you have to ensure the same versions and packages are installed on each. However, searching for details on the different versions of this DLL in the wild wasn’t fruitful – did I have SQL Compact 3.5 RTM installed (3.5.0.0)? If I’d installed v3.5 SP2, why was it shown as 3.5.1.0 (shouldn’t it be 3.5.2.0?) on one machine, but as 3.5.0.0 the other? To top that off, both versions (v3.5.0.0 and v3.5.1.0) were in my GAC as MSIL – what?

It turns out that when you add a reference to this DLL in a Compact Framework project, Visual Studio is using the following version (depending on whether you’re on an x86 or x64 machine):

x86: C:\Program Files\Microsoft SQL Server Compact Edition\v3.5\Devices\System.Data.SqlServerCe.dll
x64: C:\Program Files (x86)\Microsoft SQL Server Compact Edition\v3.5\Devices\System.Data.SqlServerCe.dll

There, I found my issue – that file version was a different version on each machine (3.5.5692.0 on one and 3.5.8080.0 on the other). To find out why, I had to dig into the registry, where I found all kinds of version information about the SQL Server Compact Edition components I’d installed, the DLL version, and the service pack levels:

x86 or x64 components
HKLM\SOFTWARE\Microsoft\Microsoft SQL Server Compact Edition\v3.5\ENU

or if you want to see x86 components installed on your x64 machine:
HKLM\SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server Compact Edition\v3.5\ENU

There, you’ll find a combination of “Version” and “ServicePackLevel” entries for every SQL CE component you have installed, and it will shed some light. Here are the version numbers I found (for the various components):

SQL Compact 3.5 RTM
ServicePackLevel: 0
Version: 3.5.5386.0

SQL Compact 3.5 SP1
ServicePackLevel: 1
Version: 3.5.5692.0

SQL Compact 3.5 SP2
ServicePackLevel: 2
Version: 3.5.8080.0

So how did I end up with the SP1 version installed on one machine, but not on the other? Since I’d installed SQL Compact SP2 on both machines, I was baffled. As it turns out, Visual Studio 2008 RTM deploys SQL Compact 3.5 RTM (3.5.5386.0), and applying Visual Studio 2008 SP1 patches that to the SP1 version (3.5.5692.0) – notice the VSToolsVersion/VSToolsServicePackLevel values in the registry location above, which reflect your current service pack level of Visual Studio 2008.

The Solution:

While I’d installed VS 2008 SP1 on both machines, I’d installed the actual SQL Compact 3.5 SP2 for Windows Mobile (EDIT 2016-10-31: This link has been updated to the current location for this download) package on only one machine and not the other – this left the DLL on one machine patched to SP1 (courtesy of Visual Studio) and the other machine with a fully-patched SP2 DLL (courtesy of the actual SP2 Windows Mobile installer).

I confirmed this by running the SP2 for Windows Mobile installer, and it patched my DLL right up to the full SP2 version – problem resolved, and the project now opens up on both workstations with no need for any DLL reference swapping.

The Moral:

Honestly, a bit embarrassing and a pretty amateur move where some vigilance could have saved me quite a bit of aggrivation.

ENSURE YOU’RE DEPLOYING ALL THE SAME PACKAGES ON ALL DEVELOPMENT MACHINES! JUST BECAUSE SOMETHING ALREADY EXISTS ON ONE MACHINE, DON’T ASSUME IT’S THE SAME VERSION!

SQL Server Contention Monitor (super-alpha) posted to Codeplex

In response to some conversation in an Experts-Exchange question, I decided to post an application I wrote about four years ago and never did anything with (or add much polish, as you’ll see). It’s a small VB.NET application that polls MSSQL servers and displays information about currently blocked SPIDs, as well as the block chain and age of blocked processes.

It’s something I’ve used when I troubleshoot blocking on a SQL Server, and generally leave running in my system tray – it will pop up a balloon when there’s a blocked process that lasts longer than some pre-determined threshold, and then optionally pop another balloon when it’s resolved. It works well as an early warning system for a critical server, and I’ve often used it when I’m performing maintenance on a production server that I’m concerned might block legitimate use – running the application lets me know within a few seconds when I’ve gotten in somebody’s way so I can act accordingly.

It’s still very much a work in progress and is pretty rough around the edges (and well inside the edges), but I’ve posted it to a Codeplex project for anybody else who might find it useful. Though I’m working on a few other things right now and haven’t done any development with this app in almost four years, I’ll keep an eye on the issues list at Codeplex (or feedback here) and try to address anything that comes up.

Without any further ado, here it is!

https://mssqlblockmonitor.codeplex.com/

Dealing with an exception: “An attempt was made to access a socket in a way forbidden by its access permissions”

I was attempting to bind a remoting listening to a particular port and kept receiving an exception when attempting to bind on the production Windows 2008 R2 server itself – it always worked fine on both my development box and our test server. Here’s the exception:

Exception message: An attempt was made to access a socket in a way forbidden by its access permissions
Stack Trace:
   at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress)
   at System.Net.Sockets.Socket.Bind(EndPoint localEP)
   at System.Net.Sockets.TcpListener.Start(Int32 backlog)
   at System.Runtime.Remoting.Channels.ExclusiveTcpListener.Start(Boolean exclusiveAddressUse)
   at System.Runtime.Remoting.Channels.Tcp.TcpServerChannel.StartListening(Object data)
   at System.Runtime.Remoting.Channels.Tcp.TcpServerChannel..ctor(IDictionary properties, IServerChannelSinkProvider sinkProvider, IAuthorizeRemotingConnection authorizeCallback)

Though Googling gave some suggestions to run the process as an administrator (no effect), the actual problem was that my process was trying to listen on a port that was already taken by another listener. Since the port was already in use, it couldn’t bind and I received the exception. Stopping the other process resolved the issue immediately.

Not really a clear error message, since it really had nothing to do with permissions at all, but there you go.

Get process ID for IIS App Pool

Ever try to debug something on a machine with many w3wp.exe worker processes and weren’t sure which one was yours? There’s a command line you can use to tell which is which:

In IIS 6 (Windows XP/2003), type:

iisapp

It is located in C:\Windows\System32, which is almost always part of the PATH variable, so you can run the command from anywhere (like iisreset).

In IIS 7/7.5 (Windows Vista/7/2008/2008R2):

appcmd list wp

It is in C:\Windows\System32\Inetsrv, which is not in the PATH variable by default, so you can switch to that folder first and run the command.

If you’re running multiple app pools on the server with the same proxy user account, now you can tell which one owns the application you’re trying to debug!