Ci sono diversi problemi in corso in questo codice che devono essere risolti:
-
Per quanto riguarda la domanda indicata, quando ottieni un System.Security.SecurityException errore, che si riferisce al codice che tenta di raggiungere l'esterno del database, cosa che non è consentita in un
SAFE
assemblaggio. Il modo in cui lo risolvi dipende da ciò che stai cercando di ottenere.- Se stai tentando di accedere al file system, leggere dal registro, ottenere una variabile di ambiente, accedere alla rete per una connessione non SQL Server (ad es. http, ftp), ecc., l'assembly ha bisogno di un
PERMISSION_SET
diEXTERNAL_ACCESS
. Per impostare il tuo assieme su qualcosa di diverso daSAFE
, devi:- Crea un certificato o una chiave asimmetrica in base alla stessa chiave che hai utilizzato per firmare il tuo assembly (ovvero assegnagli un nome sicuro), crea un accesso basato su quel certificato o chiave asimmetrica, quindi concedi il
EXTERNAL ACCESS ASSEMBLY
autorizzazione a tale accesso. Questo metodo è ottimo preferito rispetto all'altro metodo, che è: - Imposta il database contenente l'assembly su
TRUSTWORTHY ON
. Questo metodo dovrebbe essere utilizzato solo come ultima risorsa se non è possibile firmare l'assemblea. O per scopi di test rapidi. Impostazione di un database suTRUSTWORTHY ON
apre la tua istanza a potenziali minacce alla sicurezza e dovrebbe essere evitato, anche se più veloce/semplice rispetto all'altro metodo.
- Crea un certificato o una chiave asimmetrica in base alla stessa chiave che hai utilizzato per firmare il tuo assembly (ovvero assegnagli un nome sicuro), crea un accesso basato su quel certificato o chiave asimmetrica, quindi concedi il
-
Se stai tentando di accedere all'istanza di SQL Server a cui hai già effettuato l'accesso, hai la possibilità di utilizzare la connessione in-process di
Context Connection = true;
che può essere fatto in unSAFE
assemblaggio. Questo è ciò che @Marc ha suggerito nella sua risposta. Sebbene ci siano sicuramente dei vantaggi nell'usare questo tipo di connessione e mentre la connessione al contesto è stata la scelta appropriata in questo particolare scenario, è eccessivamente semplicistico e scorretto affermare che dovresti sempre utilizzare questo tipo di connessione. Diamo un'occhiata agli aspetti positivi e negativi della Connessione al contesto :- Positivi:
- Può essere fatto in un
SAFE
montaggio. - Spessore di connessione molto basso, se presente, in quanto non è una connessione aggiuntiva.
- Fa parte della sessione corrente, quindi qualsiasi SQL eseguito ha accesso a elementi basati sulla sessione come tabelle temporanee locali e
CONTEXT_INFO
.
- Può essere fatto in un
-
Negativi:
- Non può essere utilizzato se è stata abilitata la rappresentazione.
- Può connettersi solo all'istanza corrente di SQL Server.
- Quando viene utilizzato in Funzioni (scalare e con valori di tabella), ha tutte le stesse restrizioni delle funzioni T-SQL (ad es. non sono consentite operazioni con effetti collaterali), tranne per il fatto che puoi eseguire procedure memorizzate di sola lettura.
- Le funzioni con valori di tabella non sono autorizzate a trasmettere i risultati in streaming se leggono un set di risultati.
Tutti questi "negativi" sono consentiti quando si utilizza una connessione normale/esterna, anche se si tratta della stessa istanza da cui si esegue questo codice.
- Positivi:
- Se stai tentando di accedere al file system, leggere dal registro, ottenere una variabile di ambiente, accedere alla rete per una connessione non SQL Server (ad es. http, ftp), ecc., l'assembly ha bisogno di un
-
Se ti stai connettendo all'istanza da cui stai eseguendo questo codice e utilizzando una connessione esterna/normale, non è necessario specificare il nome del server o utilizzare
localhost
. La sintassi preferita èServer = (local)
che utilizza la memoria condivisa mentre gli altri a volte potrebbero utilizzare TCP/IP che non è così efficiente. -
A meno che tu non abbia un motivo molto specifico per farlo, non utilizzare
Persist Security Info=True;
-
È buona norma
Dispose()
del tuoSqlCommand
-
È più efficiente chiamare
insertcommand.Parameters.Add()
appena prima delfor
loop, quindi all'interno del loop, imposta semplicemente il valore tramitefirstname.Value =
, cosa che stai già facendo, quindi sposta semplicemente ilinsertcommand.Parameters.Add()
righe appena prima difor
linea. -
tel
/@tel
/listtelnumber
sonoINT
invece diVARCHAR
/string
. I numeri di telefono, proprio come i codici postali ei numeri di previdenza sociale (SSN), non numeri, anche se sembrano esserlo.INT
impossibile memorizzare0
iniziale s o qualcosa comeex.
per indicare una "estensione". -
Detto questo, anche se tutto quanto sopra è corretto, c'è ancora un grosso problema con questo codice che dovrebbe essere risolto :questa è un'operazione piuttosto semplicistica da eseguire in T-SQL diretto, e farlo in SQLCLR è eccessivamente complicato, più difficile e più costoso da mantenere e molto più lento. Questo codice sta eseguendo 10.000 transazioni separate mentre potrebbe essere facilmente eseguito come una singola query basata su set (cioè una transazione). Potresti avvolgere il tuo
for
loop in una transazione che la accelererebbe, ma sarà comunque sempre più lenta dell'approccio T-SQL basato su set poiché deve ancora emettere 10.000INSERT
separati dichiarazioni. Puoi facilmente randomizzare in T-SQL usandoNEWID()
o CRYPT_GEN_RANDOM che è stato introdotto in SQL Server 2008. (consultare UPDATE sezione seguente)
Se vuoi saperne di più su SQLCLR, dai un'occhiata alla serie che sto scrivendo per SQL Server Central: Scala per SQLCLR (richiesta registrazione gratuita).
AGGIORNAMENTO
Ecco un metodo T-SQL puro per generare questi dati casuali, utilizzando i valori della domanda. È facile aggiungere nuovi valori a una qualsiasi delle 4 variabili della tabella (per aumentare il numero di possibili combinazioni) poiché la query regola dinamicamente l'intervallo di randomizzazione per adattarsi a qualsiasi dato si trovi in ciascuna variabile della tabella (es. righe 1 - n).
DECLARE @TelNumber TABLE (TelNumberID INT NOT NULL IDENTITY(1, 1),
Num VARCHAR(30) NOT NULL);
INSERT INTO @TelNumber (Num) VALUES ('1525407'), ('5423986'), ('1245398'), ('32657891'),
('123658974'), ('7896534'), ('12354698');
DECLARE @FirstName TABLE (FirstNameID INT NOT NULL IDENTITY(1, 1),
Name NVARCHAR(30) NOT NULL);
INSERT INTO @FirstName (Name) VALUES ('Babak'), ('Carolin'), ('Martin'), ('Marie'),
('Susane'), ('Michail'), ('Ramona'), ('Ulf'), ('Dirk'), ('Sebastian');
DECLARE @LastName TABLE (LastNameID INT NOT NULL IDENTITY(1, 1),
Name NVARCHAR(30) NOT NULL);
INSERT INTO @LastName (Name) VALUES ('Bastan'), ('Krause'), ('Rosner'),
('Gartenmeister'), ('Rentsch'), ('Benn'), ('Kycik'), ('Leuoth'),
('Kamkar'), ('Kolaee');
DECLARE @Address TABLE (AddressID INT NOT NULL IDENTITY(1, 1),
Addr NVARCHAR(100) NOT NULL);
INSERT INTO @Address (Addr) VALUES ('Deutschlan Chemnitz Sonnenstraße 59'), (''),
('Deutschland Chemnitz Arthur-Strobel straße 124'),
('Deutschland Chemnitz Brückenstraße 3'),
('Iran Shiraz Chamran Blvd, Niayesh straße Nr.155'), (''),
('Deutschland Berlin Charlotenburg Pudbulesky Alleee 52'),
('United State of America Washington DC. Farbod Alle'), ('');
DECLARE @RowsToInsert INT = 10000;
;WITH rowcounts AS
(
SELECT (SELECT COUNT(*) FROM @TelNumber) AS [TelNumberRows],
(SELECT COUNT(*) FROM @FirstName) AS [FirstNameRows],
(SELECT COUNT(*) FROM @LastName) AS [LastNameRows],
(SELECT COUNT(*) FROM @Address) AS [AddressRows]
), nums AS
(
SELECT TOP (@RowsToInsert)
(CRYPT_GEN_RANDOM(1) % rc.TelNumberRows) + 1 AS [RandomTelNumberID],
(CRYPT_GEN_RANDOM(1) % rc.FirstNameRows) + 1 AS [RandomFirstNameID],
(CRYPT_GEN_RANDOM(1) % rc.LastNameRows) + 1 AS [RandomLastNameID],
(CRYPT_GEN_RANDOM(1) % rc.AddressRows) + 1 AS [RandomAddressID]
FROM rowcounts rc
CROSS JOIN msdb.sys.all_columns sac1
CROSS JOIN msdb.sys.all_columns sac2
)
-- INSERT dbo.Unsprstb(Firstname, Lastname, Tel, Address)
SELECT fn.Name, ln.Name, tn.Num, ad.Addr
FROM @FirstName fn
FULL JOIN nums
ON nums.RandomFirstNameID = fn.FirstNameID
FULL JOIN @LastName ln
ON ln.LastNameID = nums.RandomLastNameID
FULL JOIN @TelNumber tn
ON tn.TelNumberID = nums.RandomTelNumberID
FULL JOIN @Address ad
ON ad.AddressID = nums.RandomAddressID;
Note:
- Il
FULL JOIN
s sono necessari invece diINNER JOIN
s per ottenere l'intero@RowsToInsert
numero di righe. - Sono possibili righe duplicate a causa della natura stessa di questa randomizzazione E del fatto di non filtrarle utilizzando
DISTINCT
. Tuttavia,DISTINCT
non può essere utilizzato con i dati di esempio forniti nella domanda poiché il numero di elementi in ciascuna variabile di matrice/tabella fornisce solo 6300 combinazioni univoche e il numero richiesto di righe da generare è 10.000. Se vengono aggiunti più valori alle variabili della tabella in modo tale che il totale delle possibili combinazioni univoche superi il numero di righe richiesto, allora ilDISTINCT
la parola chiave può essere aggiunta ainums
CTE, oppure la query può essere ristrutturata semplicemente inCROSS JOIN
tutte le variabili della tabella, includono unROW_COUNT()
campo e prendi ilTOP(n)
utilizzandoORDER BY NEWID()
. - Il
INSERT
è commentato, quindi è più facile vedere che la query sopra produce il risultato desiderato. Decommenta semplicemente ilINSERT
per fare in modo che la query esegua l'effettiva operazione DML.