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

Leo*_*mps 17 .net c# sql-server sqlclr

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

λ J*_*kas 13

是的,这是可能的.程序集的实际二进制表示形式位于服务器的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();
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

sp_configure 'clr enabled', 1
go

reconfigure
go
Run Code Online (Sandbox Code Playgroud)

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

exec SaveAssembly 'AssemblyName', 'f:\path\to\assemblyname.dll'
Run Code Online (Sandbox Code Playgroud)

希望这对你有用......


Pre*_*gha 10

是.

做一个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
Run Code Online (Sandbox Code Playgroud)


pie*_*rs7 6

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");
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)