Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/292.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 从SQL Server 2005中提取.NET程序集_C#_.net_Sql Server_Sqlclr - Fatal编程技术网

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

C# 从SQL Server 2005中提取.NET程序集,c#,.net,sql-server,sqlclr,C#,.net,Sql Server,Sqlclr,我正试图用SQLCLR帮助一位私人朋友(他现在也是一位客户) 相关问题。他有一个SQL Server,数据库有3个.NET程序集 嵌入其中。他让我帮他从内部提取组件 数据库并将其另存为磁盘上的.dll文件。这可能吗?是的,这是可能的。程序集的实际二进制表示形式是活动的 在服务器的SQL目录中。也就是说,如果在 sys.assembly_文件和sys.assemblies您可以获得所需的所有信息 需要。程序集二进制文件位于sys.assembly\u文件的内容列中 视图 但是为了从SQL Serv

我正试图用SQLCLR帮助一位私人朋友(他现在也是一位客户) 相关问题。他有一个SQL Server,数据库有3个.NET程序集 嵌入其中。他让我帮他从内部提取组件
数据库并将其另存为磁盘上的.dll文件。这可能吗?

是的,这是可能的。程序集的实际二进制表示形式是活动的 在服务器的SQL目录中。也就是说,如果在 sys.assembly_文件和sys.assemblies您可以获得所需的所有信息 需要。程序集二进制文件位于sys.assembly\u文件的内容列中 视图

但是为了从SQL Server中提取二进制表示并将其转换为 您将不得不编写一些需要在磁盘上运行的.NET代码 您引用的程序集现在位于同一数据库中。在视觉上 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();
                }
            }
        }
    }
}
然后构建项目并将其部署到数据库中。确保CLR 已在SQL Server上启用启用的配置选项。这可能是 已启用,因为上面有程序集。如果clr执行不正确 已启用您可以在SSMS上运行以下代码以启用它:

sp_configure 'clr enabled', 1
go

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

exec SaveAssembly 'AssemblyName', 'f:\path\to\assemblyname.dll'
希望这对你有用…

是的

执行
从sys.assembly\u文件中选择*以查找所需程序集的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

Preet的解决方案适合我,但我必须配置Ole自动化才能在SQLServer2008R2上工作。另外请注意,除非SQL Server具有该目录的权限,否则SaveToFile不起作用,也不会给出错误消息。在我的例子中,我使用了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

Jonas的方法同样适用于控制台应用程序或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和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

我找到了一个更简单的解决方案来解决这个问题,这是必要的,因为
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
CREATE ASSEMBLY [MyAssembly] AUTHORIZATION [dbo]
FROM '/tmp/my_assembly.so' WITH PERMISSION_SET = SAFE;
并使用此格式文件(bcp.fmt):

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

/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
CREATE ASSEMBLY [MyAssembly] AUTHORIZATION [dbo]
FROM '/tmp/my_assembly.so' WITH PERMISSION_SET = SAFE;

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


请注意,您将需要。

太棒了。简单干净。谢谢。这种方法在外部执行时也可以很好地工作——您不需要走SQLCLR路线。下面是我的答案,这是最有效的解决方案。非常感谢你。