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)
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)
归档时间: |
|
查看次数: |
9652 次 |
最近记录: |