从SQL Server 2005中提取.NET程序集

我正在尝试帮助个人朋友(现在也是客户)与SQL CLR相关的问题。 他有一个SQL Server,其数据库中嵌入了3个.NET程序集。 他让我帮他从数据库中提取程序集并将它们保存为磁盘上的.dll文件。 这有可能吗?

是的,这是可能的。 程序集的实际二进制表示forms位于服务器的SQL目录中。 也就是说,如果在sys.assembly_files和sys.assemblies之间运行连接,则可以获得所需的所有信息。 程序集二进制文件位于sys.assembly_files视图的content列中。

但是为了从SQL Server中提取二进制表示并将其提取到磁盘上的文件中,您必须编写一些需要在您引用的程序集所在的同一数据库上运行的.NET代码。 在Visual Studio中,启动SQL CLR项目并使用以下代码向其添加类:

using System; using System.IO; using System.Data; using System.Data.SqlClient; using System.Data.SqlTypes; using Microsoft.SqlServer.Server; using System.Security.Permissions; namespace ExtractSqlAssembly { [PermissionSet(SecurityAction.Demand, Unrestricted = true, Name = "FullTrust")] public partial class SaveSqlAssembly { [SqlProcedure] public static void SaveAssembly(string assemblyName, string path) { string sql = @"SELECT AF.content FROM sys.assembly_files AF JOIN sys.assemblies A ON AF.assembly_id = A.assembly_id where AF.file_id = 1 AND A.name = @assemblyname"; using (SqlConnection conn = new SqlConnection("context connection=true")) { using (SqlCommand cmd = new SqlCommand(sql, conn)) { SqlParameter param = new SqlParameter("@assemblyname", SqlDbType.VarChar); param.Value = assemblyName; cmd.Parameters.Add(param); cmd.Connection.Open(); // Read in the assembly byte stream SqlDataReader reader = cmd.ExecuteReader(); reader.Read(); SqlBytes bytes = reader.GetSqlBytes(0); // write the byte stream out to disk FileStream bytestream = new FileStream(path, FileMode.CreateNew); bytestream.Write(bytes.Value, 0, (int)bytes.Length); bytestream.Close(); } } } } } 

然后构建项目并将其部署到数据库。 确保在SQL Server上启用了CLR Enabled配置选项。 这可能已经启用,因为你有程序集。 如果未启用clr执行,您可以在SSMS上运行以下代码以启用它:

 sp_configure 'clr enabled', 1 go reconfigure go 

您需要注意的另一件事是默认情况下SQL服务器可能不允许您从.NET代码写入磁盘。 如果通过调用SSMS中的存储过程运行上面的代码时出现FileIO安全性错误,则需要为程序集配置适当的权限集。 您可以通过SSMS执行此操作:右键单击新程序集并查看“属性”对话框中的“权限集”。 将其设置为外部访问。 现在,您应该可以通过在SSMS中运行以下代码来导出程序集:

 exec SaveAssembly 'AssemblyName', 'f:\path\to\assemblyname.dll' 

希望这对你有用……

是。

select * from sys.assembly_files执行select * from sys.assembly_files以查找所需程序集的id

 DECLARE @IMG_PATH VARBINARY(MAX) DECLARE @ObjectToken INT SELECT @IMG_PATH = content FROM sys.assembly_files WHERE assembly_id = 65536 EXEC sp_OACreate 'ADODB.Stream', @ObjectToken OUTPUT EXEC sp_OASetProperty @ObjectToken, 'Type', 1 EXEC sp_OAMethod @ObjectToken, 'Open' EXEC sp_OAMethod @ObjectToken, 'Write', NULL, @IMG_PATH EXEC sp_OAMethod @ObjectToken, 'SaveToFile', NULL, 'c:\temp\myassembly.dll', 2 EXEC sp_OAMethod @ObjectToken, 'Close' EXEC sp_OADestroy @ObjectToken 

Jonas的方法也适用于Console应用程序或Linqpad脚本 – 他不需要在SQL进程中本地执行代码,正如他所暗示的那样。 例如,从数据库中提取tSQLt程序集(测试工具):

 void Main() { var assemblyName = "tSQLtCLR"; var serverName = "localhost"; var databaseName = "MyDb"; var targetDir = Environment.ExpandEnvironmentVariables("%TEMP%"); var targetFile = Path.Combine(targetDir, assemblyName) + ".dll"; var sql = @"SELECT AF.content FROM sys.assembly_files AF JOIN sys.assemblies A ON AF.assembly_id = A.assembly_id where AF.file_id = 1 AND A.name = @assemblyName"; var connectionString = string.Format("Data Source={0};Initial Catalog={1};Integrated Security=true", serverName, databaseName); using(var connection = new System.Data.SqlClient.SqlConnection(connectionString)){ connection.Open(); var command = connection.CreateCommand(); command.CommandText = sql; command.Parameters.Add("@assemblyName", assemblyName); using(var reader = command.ExecuteReader()){ if(reader.Read()){ var bytes = reader.GetSqlBytes(0); File.WriteAllBytes(targetFile, bytes.Value); Console.WriteLine(targetFile); }else{ throw new Exception("No rows returned"); } } } } 

Preet的解决方案对我有用,但我不得不将Ole Automation配置为使用SQL Server 2008 R2。 另请注意,SaveToFile不起作用 – 它也不会给出错误消息 – 除非SQL Server具有该目录的权限。 在我的情况下,我使用了SQL Server实例的数据文件夹,工作正常。

 EXECUTE SP_CONFIGURE 'show advanced options', 1 RECONFIGURE WITH OVERRIDE GO EXEC sp_configure 'Ole Automation Procedures', 1; RECONFIGURE WITH OVERRIDE GO DECLARE @IMG_PATH VARBINARY(MAX) DECLARE @ObjectToken INT SELECT @IMG_PATH = content FROM sys.assembly_files WHERE assembly_id = 65546 EXEC sp_OACreate 'ADODB.Stream', @ObjectToken OUTPUT EXEC sp_OASetProperty @ObjectToken, 'Type', 1 EXEC sp_OAMethod @ObjectToken, 'Open' EXEC sp_OAMethod @ObjectToken, 'Write', NULL, @IMG_PATH EXEC sp_OAMethod @ObjectToken, 'SaveToFile', NULL, 'C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\DATA\myassembly.dll', 2 EXEC sp_OAMethod @ObjectToken, 'Close' EXEC sp_OADestroy @ObjectToken EXEC sp_configure 'Ole Automation Procedures', 0; RECONFIGURE WITH OVERRIDE GO EXECUTE SP_CONFIGURE 'show advanced options', 0 RECONFIGURE WITH OVERRIDE GO 

我找到了一个更简单的解决方案,这是必要的,因为sp_OACreate似乎不适用于SQL Server 2017(至少不是Linux版本)。

您可以使用BCP实用程序将程序集写入磁盘上的文件,如下所示:

 /opt/mssql-tools/bin/bcp "SELECT content FROM sys.assembly_files WHERE name = '${ASSEMBLY_NAME}'" \ queryout /tmp/my_assembly.so -f bcp.fmt \ -S localhost -U sa -P "${SA_PASSWORD}" -d master 

并使用此格式文件(bcp.fmt):

 13.0 1 1 SQLBINARY 0 0 "" 1 content "" 

生成的文件(/tmp/my_assembly.so)可用于创建程序集,如下所示:

 CREATE ASSEMBLY [MyAssembly] AUTHORIZATION [dbo] FROM '/tmp/my_assembly.so' WITH PERMISSION_SET = SAFE; 

使用Preet和Nate的解决方案并将它们转换为将使用游标导出所有clr过程的脚本:

 EXECUTE SP_CONFIGURE 'show advanced options', 1 RECONFIGURE WITH OVERRIDE GO EXEC sp_configure 'Ole Automation Procedures', 1; RECONFIGURE WITH OVERRIDE GO RAISERROR ('Starting...', 0, 1) WITH NOWAIT DECLARE @ObjectToken INT DECLARE @AssemblyLocation VARCHAR(MAX) DECLARE @Msg VARCHAR(MAX) DECLARE @Content VARBINARY(MAX) DECLARE @Count AS INT = (SELECT COUNT(name) FROM sys.assembly_files) DECLARE AssemblyFiles CURSOR FAST_FORWARD FOR SELECT CAST(ROW_NUMBER() OVER (ORDER BY name) AS VARCHAR(10)) + ' of ' + CAST(@Count AS VARCHAR(10)) + ' - ' + name AS Msg, '[a location the server can write to]' + name + '.dll' AS AssemblyLocation, content FROM sys.assembly_files ORDER BY name OPEN AssemblyFiles FETCH NEXT FROM AssemblyFiles INTO @Msg, @AssemblyLocation, @Content WHILE @@FETCH_STATUS = 0 BEGIN EXEC sp_OACreate 'ADODB.Stream', @ObjectToken OUTPUT EXEC sp_OASetProperty @ObjectToken, 'Type', 1 EXEC sp_OAMethod @ObjectToken, 'Open' EXEC sp_OAMethod @ObjectToken, 'Write', NULL, @Content EXEC sp_OAMethod @ObjectToken, 'SaveToFile', NULL, @AssemblyLocation, 2 EXEC sp_OAMethod @ObjectToken, 'Close' EXEC sp_OADestroy @ObjectToken RAISERROR (@Msg, 0, 1) WITH NOWAIT FETCH NEXT FROM AssemblyFiles INTO @Msg, @AssemblyLocation, @Content END CLOSE AssemblyFiles DEALLOCATE AssemblyFiles RAISERROR ('Done', 0, 1) WITH NOWAIT EXEC sp_configure 'Ole Automation Procedures', 0; RECONFIGURE WITH OVERRIDE GO EXECUTE SP_CONFIGURE 'show advanced options', 0 RECONFIGURE WITH OVERRIDE GO 

没有任何编码的替代解决方案是VisualStudio数据库项目。 创建一个新的数据库项目并使用程序集导入数据库。 您将获得DDL脚本和程序集。

注意,您需要为Visual Studio安装SSDT 。