2010-11-05 16 views
15

Sto cercando di aiutare un amico personale (che ora è anche un client) con un problema relativo CLR SQL. Ha un server SQL con un database che include 3 assembly .NET incorporati. Mi ha chiesto di aiutarlo a estrarre gli assembly dal database e salvarli come file .dll sul disco. È possibile?Estrazione di un assembly .NET da SQL Server 2005

risposta

12

Sì, questo è possibile. La rappresentazione binaria effettiva degli assiemi viene aggiornata in nel catalogo SQL per il server. Vale a dire, se si esegue un join tra sys.assembly_files e sys.assemblies, è possibile ottenere tutte le informazioni richieste da . Il binario degli assembly si trova nella colonna di contenuto della vista sys.assembly_files .

Ma al fine di estrarre la rappresentazione binaria da SQL Server e in un file su disco dovrete scrivere del codice .NET che deve eseguire sul stesso database in cui le assemblee che si riferiscono a si trovano ora. In Visual Studio avviare un progetto CLR SQL e aggiungere una classe ad esso con il seguente codice:

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(); 
       } 
      } 
     } 
    } 
} 

quindi generare il progetto e distribuirlo al database. Assicurarsi che l'opzione di configurazione abilitata CLR sia abilitata su SQL Server. Questo è probabilmente già abilitato, dal momento che ci sono assiemi su di esso. Nel caso in cui l'esecuzione CLR non è abilitato è possibile eseguire il seguente codice su SQL Server Management Studio per attivarlo:

sp_configure 'clr enabled', 1 
go 

reconfigure 
go 

Una cosa che dovete essere consapevoli è il server SQL predefinita può non consentono di scrivere al disco dal codice .NET. Se si verifica un errore di sicurezza FileIO quando si esegue il codice sopra richiamando la stored procedure in SSMS, sarà necessario configurare il set di autorizzazioni appropriato per l'assembly . È possibile farlo tramite SSMS: fare clic con il pulsante destro del mouse sul nuovo assieme e controllare il set di autorizzazioni nella finestra di dialogo Proprietà. Impostalo su Accesso esterno. Ora si dovrebbe essere in grado di esportare i gruppi eseguendo il seguente codice nel SSMS:

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

Spero che questo funziona per voi ...

+0

Questo approccio funziona bene quando eseguito ext Anche da un punto di vista ernale - non è necessario seguire il percorso SQLCLR. Vedere la mia risposta sotto – piers7

8

Sì.

fare un select * from sys.assembly_files Per trovare l'ID del gruppo che si desidera

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 
+0

impressionante. Semplice e pulito. Grazie. –

3

soluzione di Preet ha funzionato per me, ma ho dovuto configurare automazione per lavorare su SQL Server 2008 R2. Si noti inoltre che SaveToFile non funziona, né fornisce un messaggio di errore, a meno che SQL Server non disponga delle autorizzazioni per quella directory. Nel mio caso ho usato la cartella dati dell'istanza di SQL Server che ha funzionato bene.

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 
6

approccio Jonas' funziona bene come una console app o uno script LINQPad anche - non c'è bisogno per il codice da eseguire localmente all'interno del processo di SQL, come egli suggerisce.ad esempio, l'estrazione del montaggio tSQLt (uno strumento di test) da un database:

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"); 
      } 
     } 
    } 
} 
0

Prendendo soluzioni Preet e Nate e trasformandoli in uno script che esportare tutti i procs CLR utilizzando un cursore:

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 
0

I Ho trovato una soluzione più semplice a questo problema, che era necessaria perché sp_OACreate non sembra essere disponibile per SQL Server 2017 (almeno, non la versione Linux).

Si può semplicemente utilizzare l'utilità BCP di scrivere l'assemblea a un file su disco, in questo modo:

/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 

E utilizzare questo file di formato (bcp.fmt):

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

Il file risultante (/tmp/my_assembly.so) può essere utilizzato nella creazione di un assembly, ad esempio:

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