CLR 트랜잭션 예제

이 예제에서는 System.Transactions 네임스페이스에 있는 관리되는 API를 사용하여 트랜잭션을 제어하는 방법을 보여 줍니다. 특히 System.Transactions.TransactionScope 클래스는 요청을 처리할 재고가 충분하지 않는 경우 재고 수치를 조정하지 않고, 재고가 충분한 경우 한 위치에서 다른 위치로 재고를 원자 단위로 이동하도록 트랜잭션 경계를 설정하는 데 사용됩니다. 분산 트랜잭션에서의 자동 등록은 별도의 SQL Server 인스턴스에 저장된 감사 데이터베이스에 재고 변경 내용을 기록하여 표시됩니다.

필수 구성 요소

이 프로젝트를 만들고 실행하려면 다음 소프트웨어가 설치되어 있어야 합니다.

  • SQL Server 또는 SQL Server Express SQL Server Express는 SQL Server Express 설명서 및 예제 웹 사이트에서 무료로 구할 수 있습니다.

  • SQL Server 개발자 웹 사이트에서 제공되는 AdventureWorks 데이터베이스

  • .NET Framework SDK 2.0 이상 또는 Microsoft Visual Studio 2005 이상. .NET Framework SDK는 무료로 구할 수 있습니다.

  • 또한 다음 조건을 충족해야 합니다.

  • 사용하고 있는 SQL Server 인스턴스에 대해 CLR 통합이 설정되어 있어야 합니다.

  • CLR 통합을 설정하려면 다음 단계를 수행합니다.

    CLR 통합 사용

    • 다음 Transact-SQL 명령을 실행합니다.

    sp_configure 'clr enabled', 1

    GO

    RECONFIGURE

    GO

    [!참고]

    CLR을 설정하려면 ALTER SETTINGS 서버 수준 사용 권한이 있어야 합니다. 이 사용 권한은 sysadmin 및 serveradmin 고정 서버 역할의 멤버가 암시적으로 소유합니다.

  • 사용하고 있는 SQL Server 인스턴스에 AdventureWorks 데이터베이스를 설치해야 합니다.

  • 사용하고 있는 SQL Server 인스턴스의 관리자가 아닌 경우 설치를 완료하기 위해 관리자로부터 CreateAssembly 권한을 부여 받아야 합니다.

예제 빌드

다음 지침을 사용하여 예제를 만들고 실행합니다.

  1. Visual Studio 또는 .NET Framework 명령 프롬프트를 엽니다.

  2. 필요한 경우 예제에 대한 디렉터리를 만듭니다. 이 예에서는 C:\MySample을 사용합니다.

  3. 이 예에는 서명된 어셈블리가 필요하므로 다음 명령을 입력하여 비대칭 키를 만듭니다.

    sn -k SampleKey.snk

  4. 선택하는 언어에 따라 다음 중 하나를 실행하여 명령줄 프롬프트에서 예제 코드를 컴파일합니다.

    • 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

  5. Transact-SQL 설치 코드를 파일에 복사하고 해당 파일을 예제 디렉터리에 install.sql로 저장합니다.

  6. 다음을 실행하여 어셈블리 및 저장 프로시저를 배포합니다.

    • sqlcmd -E -I -i install.sql -v root = "C:\MySample\"
  7. Transact-SQL 데이터베이스 설치 코드를 파일에 복사하고 해당 파일을 예제 디렉터리에 installDB.sql로 저장합니다.

  8. 다음을 실행하여 감사 데이터베이스를 설치합니다.

    • Sqlcmd –S server_name [ \instance_name ] -E -I -i installDB.sql

    인스턴스 및 서버의 적합한 값을 사용합니다.

  9. Transact-SQL 테스트 명령 스크립트를 파일에 복사하고 해당 파일을 예제 디렉터리에 test.sql로 저장합니다.

  10. 다음 명령으로 테스트 스크립트를 실행합니다.

    • sqlcmd -E -I -i test.sql
  11. Transact-SQL 데이터베이스 정리 스크립트를 파일에 복사하고 해당 파일을 예제 디렉터리에 cleanupDB.sql로 저장합니다.

  12. 다음 명령으로 스크립트를 실행합니다.

    • Sqlcmd –S server_name [ \instance_name ] -E -I -i cleanup.sql

      인스턴스 및 서버의 적합한 값을 사용합니다.

  13. Transact-SQL 정리 스크립트를 파일에 복사하고 해당 파일을 예제 디렉터리에 cleanup.sql로 저장합니다.

  14. 다음 명령으로 스크립트를 실행합니다.

    • sqlcmd -E -I -i cleanup.sql

예제 코드

다음은 이 예제에 대한 코드 목록입니다.

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

이는 어셈블리를 배포하고 데이터베이스 내에서 저장 프로시저를 만드는 Transact-SQL 설치 스크립트(Install.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
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

이는 SQL Server의 두 번째 인스턴스에서 감사 데이터베이스를 만드는 Transact-SQL 설치 스크립트(InstallDB.sql)입니다.

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

이는 함수를 실행하여 예제를 테스트하는 test.sql입니다.

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

다음 Transact-SQL은 두 번째 인스턴스에서 감사 데이터베이스를 제거합니다.

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

다음 Transact-SQL은 데이터베이스에서 어셈블리 및 함수를 제거합니다.

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

참고 항목

개념

CLR(공용 언어 런타임) 통합에 대한 사용 시나리오 및 예