Beispiel für CLR-Transaktionen
Dieses Beispiel veranschaulicht das Steuern von Transaktionen mithilfe der im System.Transactions-Namespace vorhandenen verwalteten APIs. Dabei wird mithilfe der System.Transactions.TransactionScope-Klasse eine Transaktionsgrenze festgelegt, um sicherzustellen, dass keine Lagerbestandsdaten angepasst werden, solange kein genügend großer Lagerbestand zur Deckung der Anforderung vorhanden ist, und dass bei einem ausreichenden Lagerbestand der Transfer von Beständen zwischen den Standorten auf atomare Weise erfolgt. Zur Demonstration der automatischen Registrierung in einer verteilten Transaktion werden Bestandsänderungen in einer Überwachungsdatenbank protokolliert, die in einer eigenen Instanz von SQL Server gespeichert ist.
Voraussetzungen
Zum Erstellen und Ausführen dieses Projekts muss die folgende Software installiert sein:
SQL Server oder SQL Server Express. SQL Server Express erhalten Sie kostenlos auf der Website mit der Dokumentation und den Beispielen für SQL Server Express.
Die AdventureWorks-Datenbank, die auf der SQL Server Developer-Website zur Verfügung gestellt wird.
.NET Framework SDK 2.0 oder höher oder Microsoft Visual Studio 2005 oder höher. Das .NET Framework SDK ist kostenlos erhältlich.
Außerdem müssen die folgenden Bedingungen erfüllt sein:
In der von Ihnen verwendeten SQL Server-Instanz muss die CLR-Integration aktiviert sein.
Führen Sie zum Aktivieren der CLR-Integration die folgenden Schritte aus:
Aktivieren der CLR-Integration
- Führen Sie die folgenden Transact-SQL-Befehle aus:
sp_configure 'clr enabled', 1
GO
RECONFIGURE
GO
Hinweis Um CLR zu aktivieren, benötigen Sie die ALTER SETTINGS-Serverberechtigung, die Mitglieder der festen Serverrollen sysadmin und serveradmin implizit erhalten.
Die AdventureWorks-Datenbank muss in der von Ihnen verwendeten SQL Server-Instanz installiert sein.
Falls Sie kein Administrator für die verwendete SQL Server-Instanz sind, muss Ihnen ein Administrator die CreateAssembly -Berechtigung erteilen, damit Sie die Installation ausführen können.
Erstellen des Beispiels
Verwenden Sie die folgenden Anweisungen, um das Beispiel zu erstellen und auszuführen:
Wechseln Sie zu einer Visual Studio- oder .NET Framework-Eingabeaufforderung.
Erstellen Sie ggf. ein Verzeichnis für das Beispiel. Für dieses Beispiel wird C:\MySample verwendet.
Da dieses Beispiel eine signierte Assembly erfordert, erstellen Sie einen asymmetrischen Schlüssel, indem Sie folgenden Befehl eingeben:
sn -k SampleKey.snk
Kompilieren Sie an der Eingabeaufforderung den Beispielcode, indem Sie je nach gewählter Sprache eine der folgenden Anweisungen ausführen.
Vbc /reference:"C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll","C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Data.DataSetExtensions.dll","C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll","C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll","C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Transactions.dll","C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll" /keyfile:Key.snk /target:Library /out:Transaction.dll InventoryMover.vb
Csc /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Data.dll /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Transactions.dll /reference:C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Xml.dll /keyfile:key.snk /out:Transaction.dll /target:library InventoryMover.cs
Kopieren Sie den Transact-SQL-Installationscode in eine Datei, und speichern Sie sie als install.sql im Beispielverzeichnis.
Stellen Sie die Assembly und die gespeicherte Prozedur bereit, indem Sie die folgende Anweisung ausführen:
- sqlcmd -E -I -i install.sql -v root = "C:\MySample\"
Kopieren Sie den Transact-SQL-Installationscode für die Datenbank in eine Datei, und speichern Sie diese als installDB.sql im Beispielverzeichnis.
Installieren Sie die Überwachungsdatenbank, indem Sie
- Sqlcmd –S server_name [ \instance_name ] -E -I -i installDB.sql
mit entsprechenden Werten der Instanz und des Servers ausführen.
Kopieren Sie das Transact-SQL-Testbefehlsskript in eine Datei, und speichern Sie diese als test.sql im Beispielverzeichnis.
Führen Sie das Testskript mit dem folgenden Befehl aus:
- sqlcmd -E -I -i test.sql
Kopieren Sie das Transact-SQL-Bereinigungsskript für die Datenbank in eine Datei, und speichern Sie diese als cleanupDB.sql im Beispielverzeichnis.
Führen Sie das Skript mit dem folgenden Befehl
Sqlcmd –S server_name [ \instance_name ] -E -I -i cleanup.sql
sowie entsprechenden Werten der Instanz und des Servers aus.
Kopieren Sie das Transact-SQL-Bereinigungsskript in eine Datei, und speichern Sie diese als cleanup.sql im Beispielverzeichnis.
Führen Sie das Skript mit dem folgenden Befehl aus:
- sqlcmd -E -I -i cleanup.sql
Beispielcode
Die Codelistings für dieses Beispiel lauten wie folgt.
C#
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlTypes;
using System.Data.SqlClient;
using System.Transactions;
using Microsoft.SqlServer.Server;
using System.Security.Principal;
public static class InventoryMover
{
const string contextConnectionString = "context connection=true";
// **********
// Important: Change this connection string to refer to a server other than the one
// you have installed the AdventureWorks database. This sample demonstrates
// two-phase commit across multiple servers, and loopback to the same server is not
// permitted from CLR integrated based stored procedures.
// **********
const string auditConnectionString = "server=<YourServer>; database=InventoryAudit; user=<YourUser>; password=<YourPassword>";
[SqlMethod(DataAccess = DataAccessKind.Read, IsMutator = true)]
public static void ExecuteTransfer(int productID, short fromLocationID,
short toLocationID, short quantityToTransfer)
{
// Establish bounds of the transaction
using (TransactionScope transScope = new TransactionScope())
{
using (SqlConnection adventureworksConnection = new
SqlConnection(contextConnectionString))
{
// Opening adventureworksConnection automatically enlists it in the
// TransactionScope as part of the transaction.
adventureworksConnection.Open();
SqlCommand checkCommand = adventureworksConnection.CreateCommand();
checkCommand.CommandText = "SELECT TOP 1 Quantity"
+ " FROM Production.ProductInventory WITH (REPEATABLEREAD)"
+ " WHERE ProductID = @ProductID AND LocationID = @LocationID;";
checkCommand.Parameters.Add("@ProductID", SqlDbType.Int);
checkCommand.Parameters[0].Value = productID;
checkCommand.Parameters.Add("@LocationID", SqlDbType.Int);
checkCommand.Parameters[1].Value = fromLocationID;
object result = checkCommand.ExecuteScalar();
short availableQuantity = (short)result;
if (availableQuantity < quantityToTransfer)
//It would be better to throw a custom error, and in some cases to actually
//RAISERROR. Also, it would be better to map product IDs and location IDs to
//names for the error message.
throw new ArgumentOutOfRangeException("quantityToTransfer",
string.Format("Attempt to transfer {0} of product {1} from"
+ " location {2} but only {3} is available.",
quantityToTransfer, productID, fromLocationID,
availableQuantity));
//Remove inventory count from source
SqlCommand reduceCommand = adventureworksConnection.CreateCommand();
reduceCommand.CommandText = "UPDATE Production.ProductInventory"
+ " SET Quantity = Quantity - @QuantityToTransfer"
+ " WHERE ProductID = @ProductID AND LocationID = @LocationID;";
reduceCommand.Parameters.Add("@ProductID", SqlDbType.Int);
reduceCommand.Parameters[0].Value = productID;
reduceCommand.Parameters.Add("@LocationID", SqlDbType.SmallInt);
reduceCommand.Parameters[1].Value = fromLocationID;
reduceCommand.Parameters.Add("@QuantityToTransfer", SqlDbType.SmallInt);
reduceCommand.Parameters[2].Value = quantityToTransfer;
reduceCommand.ExecuteNonQuery();
//Increate inventory count at destination
SqlCommand increaseCommand = adventureworksConnection.CreateCommand();
increaseCommand.CommandText = "UPDATE Production.ProductInventory"
+ " SET Quantity = Quantity + @QuantityToTransfer"
+ " WHERE ProductID = @ProductID AND LocationID = @LocationID;";
increaseCommand.Parameters.Add("@ProductID", SqlDbType.Int);
increaseCommand.Parameters[0].Value = productID;
increaseCommand.Parameters.Add("@LocationID", SqlDbType.SmallInt);
increaseCommand.Parameters[1].Value = toLocationID;
increaseCommand.Parameters.Add("@QuantityToTransfer", SqlDbType.SmallInt);
increaseCommand.Parameters[2].Value = quantityToTransfer;
increaseCommand.ExecuteNonQuery();
// Create an audit trail of the inventory transfer. We must impersonate the
// client credentials in order for this to work. Otherwise we'd have to
// set up security for the machine account.
// SqlConnection auditConnection = adventureworksConnection;
using (SqlConnection auditConnection = new SqlConnection(auditConnectionString))
{
SqlCommand auditCommand = auditConnection.CreateCommand();
auditCommand.CommandText = "INSERT InventoryChange "
+ " (ProductID, FromLocationID, ToLocationID, Quantity) "
+ " VALUES (@ProductID, @FromLocationID, @ToLocationID, @Quantity);";
auditCommand.Parameters.Add("@ProductID", SqlDbType.Int);
auditCommand.Parameters[0].Value = productID;
auditCommand.Parameters.Add("@FromLocationID", SqlDbType.SmallInt);
auditCommand.Parameters[1].Value = fromLocationID;
auditCommand.Parameters.Add("@ToLocationID", SqlDbType.SmallInt);
auditCommand.Parameters[2].Value = toLocationID;
auditCommand.Parameters.Add("@Quantity", SqlDbType.SmallInt);
auditCommand.Parameters[3].Value = quantityToTransfer;
// Opening auditConnection automatically enlists it in the
// TransactionScope as part of the transaction.
auditConnection.Open();
auditCommand.ExecuteNonQuery();
}
}
// The Complete method commits the transaction.
transScope.Complete();
}
}
}
Visual Basic
Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Data
Imports System.Data.SqlTypes
Imports System.Data.SqlClient
Imports System.Transactions
Imports Microsoft.SqlServer.Server
Imports System.Security.Principal
Imports System.Globalization
Public NotInheritable Class InventoryMover
Private Const contextConnectionString As String = "context connection=true"
Private Sub New()
End Sub
' **********
' Important: Change this connection string to refer to a server other than the one
' you have installed the AdventureWorks database. This sample demonstrates
' two-phase commit across multiple servers, and loopback to the same server is not
' permitted from CLR integrated based stored procedures.
' **********
Private Const auditConnectionString As String = "server=<YourServer>; database=InventoryAudit; user=<YourUser>; password=<YourPassword>"
<SqlMethod(DataAccess:=DataAccessKind.Read, IsMutator:=True)> _
Public Shared Sub ExecuteTransfer(ByVal productID As Integer, ByVal fromLocationID As Short, _
ByVal toLocationID As Short, ByVal quantityToTransfer As Short)
' Establish bounds of the transaction
Using transScope As New TransactionScope()
Using adventureworksConnection As New SqlConnection(contextConnectionString)
' Opening adventureworksConnection automatically enlists it in the
' TransactionScope as part of the transaction.
adventureworksConnection.Open()
Dim checkCommand As SqlCommand = adventureworksConnection.CreateCommand()
checkCommand.CommandText = "SELECT TOP 1 Quantity" _
& " FROM Production.ProductInventory WITH (REPEATABLEREAD)" _
& " WHERE ProductID = @ProductID AND LocationID = @LocationID;"
checkCommand.Parameters.Add("@ProductID", SqlDbType.Int)
checkCommand.Parameters(0).Value = productID
checkCommand.Parameters.Add("@LocationID", SqlDbType.Int)
checkCommand.Parameters(1).Value = fromLocationID
Dim result As Object = checkCommand.ExecuteScalar()
Dim availableQuantity As Short = CType(result, Short)
If (availableQuantity < quantityToTransfer) Then
'It would be better to throw a custom error, and in some cases to actually
'RAISERROR. Also, it would be better to map product IDs and location IDs to
'names for the error message.
Throw New ArgumentOutOfRangeException("quantityToTransfer", _
String.Format(CultureInfo.InvariantCulture, "Attempt to transfer {0} of product {1} from" _
& " location {2} but only {3} is available.", _
quantityToTransfer, productID, fromLocationID, _
availableQuantity))
End If
'Remove inventory count from source
Dim reduceCommand As SqlCommand = adventureworksConnection.CreateCommand()
reduceCommand.CommandText = "UPDATE Production.ProductInventory" _
& " SET Quantity = Quantity - @QuantityToTransfer" _
& " WHERE ProductID = @ProductID AND LocationID = @LocationID;"
reduceCommand.Parameters.Add("@ProductID", SqlDbType.Int)
reduceCommand.Parameters(0).Value = productID
reduceCommand.Parameters.Add("@LocationID", SqlDbType.SmallInt)
reduceCommand.Parameters(1).Value = fromLocationID
reduceCommand.Parameters.Add("@QuantityToTransfer", SqlDbType.SmallInt)
reduceCommand.Parameters(2).Value = quantityToTransfer
reduceCommand.ExecuteNonQuery()
'Increate inventory count at destination
Dim increaseCommand As SqlCommand = adventureworksConnection.CreateCommand()
increaseCommand.CommandText = "UPDATE Production.ProductInventory" _
& " SET Quantity = Quantity + @QuantityToTransfer" _
& " WHERE ProductID = @ProductID AND LocationID = @LocationID;"
increaseCommand.Parameters.Add("@ProductID", SqlDbType.Int)
increaseCommand.Parameters(0).Value = productID
increaseCommand.Parameters.Add("@LocationID", SqlDbType.SmallInt)
increaseCommand.Parameters(1).Value = toLocationID
increaseCommand.Parameters.Add("@QuantityToTransfer", SqlDbType.SmallInt)
increaseCommand.Parameters(2).Value = quantityToTransfer
increaseCommand.ExecuteNonQuery()
' Create an audit trail of the inventory transfer. We must impersonate the
' client credentials in order for this to work. Otherwise we'd have to
' set up security for the machine account.
'SqlConnection auditConnection = adventureworksConnection
Using auditConnection As New SqlConnection(auditConnectionString)
Dim auditCommand As SqlCommand = auditConnection.CreateCommand()
auditCommand.CommandText = "INSERT InventoryChange " _
& " (ProductID, FromLocationID, ToLocationID, Quantity) " _
& " VALUES (@ProductID, @FromLocationID, @ToLocationID, @Quantity);"
auditCommand.Parameters.Add("@ProductID", SqlDbType.Int)
auditCommand.Parameters(0).Value = productID
auditCommand.Parameters.Add("@FromLocationID", SqlDbType.SmallInt)
auditCommand.Parameters(1).Value = fromLocationID
auditCommand.Parameters.Add("@ToLocationID", SqlDbType.SmallInt)
auditCommand.Parameters(2).Value = toLocationID
auditCommand.Parameters.Add("@Quantity", SqlDbType.SmallInt)
auditCommand.Parameters(3).Value = quantityToTransfer
' Opening auditConnection automatically enlists it in the
' TransactionScope as part of the transaction.
auditConnection.Open()
auditCommand.ExecuteNonQuery()
End Using
End Using
' The Complete method commits the transaction.
transScope.Complete()
End Using
End Sub
End Class
Dies ist das Transact-SQL-Installationsskript (Install.sql), das die Assembly bereitstellt und die gespeicherte Prozedur in der Datenbank erstellt.
USE AdventureWorks
GO
-- Drop existing sproc and assembly if any.
IF EXISTS (SELECT * FROM sys.procedures WHERE [name] = 'usp_ExecuteTransfer')
DROP PROCEDURE usp_ExecuteTransfer;
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'Transaction')
DROP ASSEMBLY [Transaction];
GO
USE master
GO
IF EXISTS (SELECT * FROM sys.server_principals WHERE [name] = 'ExternalSample_Login')
DROP LOGIN ExternalSample_Login;
GO
IF EXISTS (SELECT * FROM sys.asymmetric_keys WHERE [name] = 'ExternalSample_Key')
DROP ASYMMETRIC KEY ExternalSample_Key;
GO
DECLARE @SamplesPath nvarchar(1024)
-- You may need to modify the value of the this variable if you have installed the sample someplace other than the default location.
set @SamplesPath = 'C:\MySample\'
EXEC('CREATE ASYMMETRIC KEY ExternalSample_Key FROM EXECUTABLE FILE = ''' + @SamplesPath + 'Transaction.dll'';');
CREATE LOGIN ExternalSample_Login FROM ASYMMETRIC KEY ExternalSample_Key
GRANT EXTERNAL ACCESS ASSEMBLY TO ExternalSample_Login
GO
USE AdventureWorks
GO
DECLARE @SamplesPath nvarchar(1024)
-- You may need to modify the value of this variable if you have installed the sample someplace other than the default location.
set @SamplesPath = 'C:\MySample\'
-- Add the assembly and CLR integration based stored procedure
CREATE ASSEMBLY [Transaction]
FROM @SamplesPath + 'Transaction.dll'
WITH permission_set = External_Access;
GO
CREATE PROCEDURE usp_ExecuteTransfer
(
@ProductID int,
@FromLocationID smallint,
@ToLocationID smallint,
@QuantityToTransfer smallint
)
AS EXTERNAL NAME [Transaction].[InventoryMover].ExecuteTransfer;
GO
Dies ist das Transact-SQL-Installationsskript (InstallDB.sql), das die Überwachungsdatenbank in der zweiten Instanz von SQL Server erstellt.
SET NOCOUNT OFF;
GO
PRINT CONVERT(varchar(1000), @@VERSION);
GO
PRINT '';
PRINT 'Started - ' + CONVERT(varchar, GETDATE(), 121);
GO
USE [master];
GO
-- ****************************************
-- Drop Database
-- ****************************************
PRINT '';
PRINT '*** Dropping Database';
GO
IF EXISTS (SELECT [name] FROM [master].[sys].[databases] WHERE [name] = N'InventoryAudit')
DROP DATABASE [InventoryAudit];
-- If the database has any other open connections close the network connection.
IF @@ERROR = 3702
RAISERROR('[InventoryAudit] database cannot be dropped because there are still other open connections', 127, 127) WITH NOWAIT, LOG;
GO
-- ****************************************
-- Create Database
-- ****************************************
PRINT '';
PRINT '*** Creating Database';
GO
DECLARE
@sql_path nvarchar(256),
@sql_cmd nvarchar(256);
SELECT @sql_path = SUBSTRING([physical_name], 1, CHARINDEX(N'master.mdf', LOWER([physical_name])) - 1)
FROM [master].[sys].[master_files]
WHERE [database_id] = 1
AND [file_id] = 1;
-- COLLATE Latin1_General_CS_AS
EXECUTE (N'CREATE DATABASE [InventoryAudit]
ON (NAME = ''InventoryAudit_Data'', FILENAME = N''' + @sql_path + N'InventoryAudit_Data.mdf'', SIZE = 120, FILEGROWTH = 8)
LOG ON (NAME = ''InventoryAudit_Log'', FILENAME = N''' + @sql_path + N'InventoryAudit_Log.ldf'' , SIZE = 2, FILEGROWTH = 96);');
GO
ALTER DATABASE [InventoryAudit]
SET RECOVERY SIMPLE,
ANSI_NULLS ON,
ANSI_PADDING ON,
ANSI_WARNINGS ON,
ARITHABORT ON,
CONCAT_NULL_YIELDS_NULL ON,
QUOTED_IDENTIFIER ON,
NUMERIC_ROUNDABORT OFF,
PAGE_VERIFY CHECKSUM,
ALLOW_SNAPSHOT_ISOLATION OFF;
GO
USE [InventoryAudit];
GO
PRINT '';
PRINT '*** Creating Table';
GO
CREATE TABLE [InventoryChange] (
[InventoryChangeID] int IDENTITY (1, 1) NOT NULL,
[ProductID] int NOT NULL,
[FromLocationID] smallint,
[ToLocationID] smallint,
[Quantity] smallint NOT NULL
);
GO
Dies ist die Datei test.sql, die das Beispiel durch das Ausführen der Funktionen testet.
USE AdventureWorks
GO
SELECT 'Before first transfer quantity of adjustable races at Tool Crib = ', Quantity FROM Production.ProductInventory
WHERE ProductID = 1 AND LocationID = 1
SELECT 'Before first transfer quantity of adjustable races at Miscellaneous Storage = ', Quantity FROM Production.ProductInventory
WHERE ProductID = 1 AND LocationID = 6
--Move 12 adjustable race parts (product id 1) from the Tool Crib (location id 1)
--to Miscellaneous Storage (location id 6).
EXEC usp_ExecuteTransfer 1, 1, 6, 12
SELECT 'After first transfer quantity of adjustable races at Tool Crib = ', Quantity FROM Production.ProductInventory
WHERE ProductID = 1 AND LocationID = 1
SELECT 'After first transfer quantity of adjustable races at Miscellaneous Storage = ', Quantity FROM Production.ProductInventory
WHERE ProductID = 1 AND LocationID = 6
--Move them back
EXEC usp_ExecuteTransfer 1, 6, 1, 12
SELECT 'After second transfer quantity of adjustable races at Tool Crib = ', Quantity FROM Production.ProductInventory
WHERE ProductID = 1 AND LocationID = 1
SELECT 'After second transfer quantity of adjustable races at Miscellaneous Storage = ', Quantity FROM Production.ProductInventory
WHERE ProductID = 1 AND LocationID = 6
The following tsql removes the assembly and stored procedure from the database (Cleanup.sql).
USE AdventureWorks
GO
-- Drop existing sproc and assembly if any.
IF EXISTS (SELECT * FROM sys.procedures WHERE [name] = 'usp_ExecuteTransfer')
DROP PROCEDURE usp_ExecuteTransfer;
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'Transaction')
DROP ASSEMBLY [Transaction];
GO
USE master
GO
IF EXISTS (SELECT * FROM sys.server_principals WHERE [name] = 'ExternalSample_Login')
DROP LOGIN ExternalSample_Login;
GO
IF EXISTS (SELECT * FROM sys.asymmetric_keys WHERE [name] = 'ExternalSample_Key')
DROP ASYMMETRIC KEY ExternalSample_Key;
GO
USE AdventureWorks
GO
Im folgenden Transact-SQL-Code wird die Überwachungsdatenbank aus der zweiten Instanz entfernt.
SET NOCOUNT OFF;
GO
PRINT CONVERT(varchar(1000), @@VERSION);
GO
PRINT '';
PRINT 'Started - ' + CONVERT(varchar, GETDATE(), 121);
GO
USE [master];
GO
-- ****************************************
-- Drop Database
-- ****************************************
PRINT '';
PRINT '*** Dropping Database';
GO
IF EXISTS (SELECT [name] FROM [master].[sys].[databases] WHERE [name] = N'InventoryAudit')
DROP DATABASE [InventoryAudit];
-- If the database has any other open connections close the network connection.
IF @@ERROR = 3702
RAISERROR('[InventoryAudit] database cannot be dropped because there are still other open connections', 127, 127) WITH NOWAIT, LOG;
GO
Im folgenden Transact-SQL-Code werden die Assembly und die Funktionen aus der Datenbank entfernt.
SE AdventureWorks
GO
-- Drop existing sproc and assembly if any.
IF EXISTS (SELECT * FROM sys.procedures WHERE [name] = 'usp_ExecuteTransfer')
DROP PROCEDURE usp_ExecuteTransfer;
GO
IF EXISTS (SELECT * FROM sys.assemblies WHERE [name] = 'Transaction')
DROP ASSEMBLY [Transaction];
GO
USE master
GO
IF EXISTS (SELECT * FROM sys.server_principals WHERE [name] = 'ExternalSample_Login')
DROP LOGIN ExternalSample_Login;
GO
IF EXISTS (SELECT * FROM sys.asymmetric_keys WHERE [name] = 'ExternalSample_Key')
DROP ASYMMETRIC KEY ExternalSample_Key;
GO
USE AdventureWorks
GO
Siehe auch
Konzepte
Verwendungsszenarien und Beispiele für Common Language Runtime (CLR)-Integration