소프트웨어/C# & ASP.NET

.NET의 Enterprise Service(COM+) 이해

falconer 2008. 9. 5. 10:46

원문 : http://www.microsoft.com/korea/msdn/library/dndotnet/html/entserv.asp

.NET의 Enterprise Service(COM+) 이해


Shannon Pahl
Microsoft Corporation

요약: Microsoft .NET과 COM+ 서비스 통합의 이면에 있는 기술적 정보를 제공하고 관리되는 코드에 사용할 수 있는 서비스에 대해 설명합니다(26페이지/인쇄 페이지 기준).

목차

소개
트랜잭션
배포
서비스되는 구성 요소
개체 수명
보안
원격 구성 요소
결론

소개

이 기사에서는 Microsoft.NET Framework와 COM+ 서비스를 잘 알고 있어야 합니다. Enterprise Services를 반드시 알고 있을 필요는 없지만 알고 있으면 도움이 됩니다. 이 항목에 대한 자세한 배경은 다음을 참조하십시오.

COM은 구성 요소 기반 응용 프로그램을 작성하는 한 가지 방법을 제공합니다. COM 구성 요소를 작성하는 데 필요한 측정 작업이 중요하고 반복된다는 사실은 잘 알려져 있습니다. COM+는 COM의 새로운 버전이 아닙니다. COM+는 구성 요소에 대한 서비스 인프라를 제공합니다. 배포가 쉽고 처리량이 많은 확장 가능한 서버 응용 프로그램을 작성하려면 구성 요소를 작성한 다음 COM+ 응용 프로그램에 설치합니다. 구성 요소에서 서비스를 사용할 필요가 없을 경우 해당 구성 요소를 COM+ 응용 프로그램에 설치할 필요가 없습니다. 응용 프로그램을 처음부터 트랜잭션, 개체 풀링, 동작 구문 등을 사용하도록 디자인하면 확장성 및 처리량을 얻을 수 있습니다.

.NET Framework는 구성 요소 기반 응용 프로그램을 작성하는 다른 방법을 제공하며, 보다 나은 도구를 지원하는 COM 프로그래밍 모델, 공용 언어 런타임(CLR), 훨씬 쉬워진 코딩 구문보다 이점들을 가지고 있습니다. COM+ 서비스 인프라는 관리되는 코드와 관리되지 않는 코드 모두에서 액세스될 수 있습니다. 관리되지 않는 코드의 서비스를 COM+ 서비스라 합니다. .NET에서는 이 서비스를 Enterprise Services라 합니다. ServicedComponent에서 클래스가 파생될 경우 한 구성 요소를 위해 서비스가 필요하다는 것을 나타냅니다. 구성 요소에서 서비스를 사용할 필요가 없을 경우 ServicedComponent에서 해당 구성 요소를 파생할 필요가 없습니다. 프로그래머들이 서버 기반 응용 프로그램을 작성할 수 있도록 도구 지원을 향상시켰지만, 우수한 프로그래밍 영역에서는 여전히 확장성 및 처리량 문제가 존재합니다. 서비스의 이면에 있는 기본적인 생각은 처음부터 처리량과 확장성을 고려해서 디자인하고 Enterprise Services를 사용하여 해당 위치에서 디자인 패턴을 쉽게 구현하게 하는 것입니다.

서비스 인프라 디자인은 COM 또는 구성 요소와 거의 관련이 없다고 주장할 수도 있습니다. 그러나, COM+ services는 이제 COM 구성 요소와 .NET 구성 요소에 적용될 수 있으며, 심지어는 ASP 페이지, 임의의 코드 블록 등과 같은 구성 요소가 아닌 엔티티에도 적용될 수 있습니다(Microsoft Windows XP의 Components COM+ 기능 없는 서비스 참조).

현재 사용할 수 있는 모든 COM+ 서비스는 .NET 및 COM 개체에 사용될 수 있습니다. 이러한 서비스로는 트랜잭션, 개체 풀링 및 작업 문자열, JIT, 동기화, 역할 기반 보안, CRM, BYOT 등이 있습니다. Microsoft Windows 2000의 전체 서비스 목록은 Platform SDK의 COM+에 제공되는 서비스를 참조하십시오. Microsoft Windows XP에는 .NET 구성 요소와 함께 사용될 수 있는 추가 서비스가 있는 COM+ 1.5라는 새로운 COM+ 버전이 포함되어 있습니다.

트랜잭션

서비스를 사용하는 관리되는 응용 프로그램을 작성하려면 서비스를 필요로 하는 클래스를 ServicedComponent에서 파생하고 다양한 사용자 지정 특성을 사용하여 실제로 필요한 서비스를 지정해야 합니다. 이 절에서는 서비스의 개념과 서비스가 관리되는 코드 작성에 어떤 영향을 미치는지에 대해 설명합니다. 자세한 설명은 이후의 절들에 제공됩니다.

Account라는 클래스(실제 코드는 뒤에 나열됨)가 작성되어 BankComponent 어셈블리에 있다고 가정합니다. 이 클래스는 다음과 같이 사용될 수 있습니다.

BankComponent Client

using system;
using BankComponent;
namespace BankComponentClient
{
class Client
{
public static int Main()
{
Account act = new Account();
act.Post(5, 100);
act.Dispose();
return 0;
}
}
}

클라이언트를 구축하려면 BankComponent 네임스페이스에 참조를 추가해야 합니다. 또한, BankComponentClient 네임스페이스에 System.EnterpriseServices 어셈블리에 대한 참조를 추가해야, 클라이언트가 Dispose() 및 ServicedComponent 생성자를 호출합니다. 이 생성자는 BankComponent가 포함된 어셈블리가 아니라 System.EnterpriseServices에 정의되는 메서드입니다. 다음은 파생 클래스가 모든 기본 클래스 메서드를 재정의하지 않는 일반적인 .NET 요구 사항입니다.

BankComponent Server 코드는 .NET에서 트랜잭션을 사용하는 Account 클래스 구현을 표시합니다. Account 클래스는 System.EnterpriseServices.ServicedComponent 클래스에서 파생됩니다. Transaction 특성은 클래스에 트랜잭션이 필요하다고 표시합니다. Transaction 특성이 사용되기 때문에 Synchronization 및 JIT 서비스는 자동으로 구성됩니다. AutoComplete 특성은 메서드를 실행하는 동안 처리되지 않은 예외가 throw될 경우 런타임에 트랜잭션을 위한 SetAbort 함수가 자동으로 호출되고, 그렇지 않을 경우 SetComplete 함수가 호출되도록 지정하는 데 사용됩니다. ApplicationName 특성은 이 응용 프로그램의 서비스 구성 데이터를 저장하는 COM+ 응용 프로그램에 이 어셈블리를 연결합니다. 이 클래스에 필요한 추가 수정은 코드에서 강조 표시됩니다.

BankComponent Server

using System.EnterpriseServices;
[assembly: ApplicationName("BankComponent")]
[assembly: AssemblyKeyFileAttribute("Demos.snk")]

namespace BankComponentServer
{
[Transaction(TransactionOption.Required)]
public class Account : ServicedComponent
{
[AutoComplete]
public bool Post(int accountNum, double amount)
{
// SetComplete를 호출하지 않고 데이터베이스를 업데이트합니다.
// 예외가 throw되지 않으면 SetComplete가 자동으로 호출됩니다.
}
}
}

BankComponent Server 네임스페이스에 있는 코드는 .NET에서 COM+ 서비스를 얼마나 쉽게 사용할 수 있는지를 보여 줍니다. 아래 목록은 코딩에서 배포까지의 전체 프로세스에 대한 요약입니다.

  1. 서버 어셈블리를 작성합니다.
  2. 어셈블리 작성:
    1. 어셈블리에 서명합니다. 키 파일은 프로젝트마다 한 번만 생성될 수 있으므로 컴파일할 때마다 생성할 필요가 없습니다. 키는 Microsoft .NET 명령 프롬프트 및 sn.exe를 다음과 같이 사용하여 생성될 수 있습니다.
      sn –k Demos.snk
    2. 코드를 컴파일합니다. System.EnterpriseServices에 대한 참조가 추가되어야 합니다.
  3. 응용 프로그램을 배포합니다.

    서비스되는 구성 요소를 사용하는 어셈블리는 COM+ 카탈로그에 등록되어야 합니다. ServicedComponent 클래스와 사용자 지정 특성은 관리되는 코드에서 COM+ 서비스에 액세스하는 두 가지 핵심 개념입니다. 서비스의 구성은 COM+ 카탈로그에 저장됩니다. 개체는 CLR 내부에서 존재하고 실행됩니다. 관리되는 개체와 관련된 COM+ 컨텍스트는 그림 1에 표시되어 있으며 다음 두 절에서 자세히 설명합니다.

    그림 1. 관리되는 구성 요소와 연결된 서비스

    COM+ 구성 요소에서는 카탈로그를 수동으로 구성해야 하지만, 서비스되는 구성 요소에서는 카탈로그가 코드에 있는 특성을 기반으로 업데이트될 수 있습니다. 명령줄 도구 regsvcs.exe를 사용하거나 관리되는 API를 액세스하는 스크립트를 작성하여 어셈블리를 명시적으로 등록할 수 있습니다. 자세한 내용은 아래의 배포 정보 절을 참조하십시오. 개발하는 동안 편리한 때에 어셈블리를 응용 프로그램 디렉터리에 간단하게 복사하여 배포하는 XCopy 배포 기능이 제공됩니다. 클라이언트 응용 프로그램이 ServicedComponent에서 파생되는 클래스의 인스턴스를 만들 때마다 런타임에서 COM+ 응용 프로그램의 어셈블리가 이미 등록되어 있는지 여부를 검색합니다. 등록되어 있지 않을 경우 로컬 디렉터리에서 어셈블리를 검색하여, 어셈블리가 있으면 해당 어셈블리에 있는 모든 서비스된 구성 요소를 COM+ 응용 프로그램에 등록하여 활성화될 수 있는 상태로 만듭니다. 이 등록을 지연 등록이라 하지만, 모든 시나리오에 적용되지는 않습니다. 예를 들어, COM+ 서버 응용 프로그램으로 표시되는 어셈블리는 모두 명시적으로 등록해야 합니다(아래 참조). 지연 등록은 관리되는 서비스 구성 요소를 호출하는 관리되지 않는 클라이언트에는 적용되지 않습니다. 지연 등록은 개발 기간 동안에는 유용하지만, 그 외에는 스크립트, 코드 또는 RegSvcs를 사용하여 어셈블리를 등록합니다.

  4. 어셈블리를 GAC에 저장합니다. 자세한 내용은 배포 절을 참조하십시오.
  5. 클라이언트를 실행합니다.

배포

사용자 지정 특성은 관리되는 코드에서 COM+ 서비스를 액세스하는 두 가지 핵심 개념 중 하나입니다. 사용자 지정 특성은 이전 코드 목록의 Transaction 사용자 지정 특성과 같은 필수 서비스를 지정하는 데 사용됩니다. 이 특성은 어셈블리 메타데이터에 있는 서비스에 대한 구성 옵션을 저장합니다. 사용자 지정 특성은 일부 코드가 어셈블리를 로드하고 반사를 사용하여 특성 인스턴스를 만든 다음 해당 특성에 대한 메서드를 호출하여 특성에 저장된 서비스 구성을 추출하는 방식으로 사용됩니다. 그런 다음 COM+ 카탈로그에 정보를 기록할 수 있습니다. 모든 단계를 실행하는 코드는 EnterpriseServices.RegistrationHelper에 포함됩니다. 등록 프로세스를 보다 쉽게 만들기 위해 모든 등록 양식에서 EnterpriseServices.RegistrationHelper 구성 요소를 사용합니다. 이 구성 요소는 COM 개체 뿐만 아니라 관리되는 클래스로도 액세스될 수 있습니다.

그림 2. 서비스되는 구성 요소 등록

개념적으로 RegistrationHelper는 다음 단계를 실행합니다.

  • RegistrationServices.RegisterAssembly를 사용하여 레지스트리에 어셈블리를 등록합니다. 따라서, 클래스는 레지스트리에 관리되는 코드로 작성된 COM 구성 요소로 표시되며 mscoree.dll을 가리키는 InprocServer32 키를 가집니다. 관리되는 클래스가 인터페이스를 구현하지 않을 경우 ClassInterfaceAttribute가 사용되지 않으면 해당 클래스의 공개 메서드가 COM+ 카탈로그에 표시되지 않습니다. 이것은 메서드 수준에 연결된 서비스 구성이 카탈로그에 저장될 수 없음을 의미합니다. 그러나, 일부 COM+ 서비스는 메서드 수준에서 구성될 수 있으며 인터페이스를 COM+ 카탈로그에 표시된대로 노출하려면 구성 요소가 있어야 합니다. 예를 들어, 메서드 수준의 COM+ 역할 기반 보안에서는 서비스를 구성하기 위한 인터페이스를 구현하는 구성 요소가 필요합니다. 이 문제에 대해서는 보안 절에 자세히 설명되어 있습니다.
  • TypeLibConverter를 사용하여 어셈블리에서 COM 형식 라이브러리를 생성합니다. ConvertAssemblyToTypeLib.
  • 형식 라이브러리를 등록합니다. 지금까지는 RegAsm.exe /tlb와 거의 동일합니다.
  • COM+ 응용 프로그램을 찾거나 만듭니다. 이름은 ApplicationName 특성, 어셈블리 이름, 제공된 응용 프로그램 이름/GUID 등에서 추출됩니다.
  • 형식 라이브러리에서 COM+ 관리 API를 사용하여 COM+ 응용 프로그램을 구성합니다.
  • 사용자 지정 특성을 모두 진행한 다음 IConfigurationAttribute를 사용하여 COM+ 카탈로그에 특정 서비스에 대한 구성 데이터를 기록합니다.

RegistrationHelper는 .NET를 설치할 때 만들어지는 COM+ 응용 프로그램에 있는 RegistrationHelperTx 클래스를 사용하여 트랜잭션 내부에서 이 단계를 실행하려고 시도합니다. 따라서, 등록에 실패하면 COM+ 카탈로그와 레지스트리가 원래의 상태로 복원됩니다. 그러나, 현재 생성된 형식 라이브러리는 디스크에(어셈블리가 GAC에 있을 경우 GAC에) 그대로 유지됩니다. 등록 중인 어셈블리가 또한 COM+ 서비스를 사용하는 다른 어셈블리를 참조할 경우 종속성 그래프에 있는 모든 어셈블리는 위에 나열된 단계를 실행합니다.

RegistrationHelper는 COM+ 카탈로그를 액세스하기 때문에 시스템에 관리되지 않는 코드 사용 권한 및 관리자 권한이 있어야 합니다. 클라이언트의 RegistrationHelper(지연 등록, RegSvcs, 스크립트/코드)의 경우에도 마찬가지입니다. 이것은 인터넷에서 다운로드했거나 네트워크 공유에 저장된 코드는 등록될 수 없음을 의미합니다.

트랜잭션 필요, 동기화를 사용 안 함으로 설정과 같이 호환되지 않는 특성 조합을 코딩할 수 있습니다. 이러한 조합은 현재 컴파일 기간이 아니라 COM+ 카탈로그에 기록하는 등록 기간 동안 검색됩니다. 일부 특성은 다른 특성에 암시적으로 종속됩니다. 예를 들어, Transaction 특성만 사용해도 Transaction, JustInTimeActivation 및 Synchronization 특성을 사용하는 것과 동일합니다. 관리되는 구성 요소가 등록되면 특성을 사용하여 '구성되지 않은' 기본값을 덮어쓰지 않는 한 COM+ 카탈로그 기본값이 사용됩니다. 예를 들어, 구성 요소를 등록하고 Transaction 특성을 지정하지 않으면 카탈로그의 트랜잭션 설정에 대한 구성되지 않은 기본값이 TransactionOption.Disabled로 설정됩니다. 개발자는 이 접근법을 사용하여 구성 요소에 어느 특성이 더 이상 필요하지 않을 경우 코드에서 해당 특성을 제거할 수 있습니다. 트랜잭션의 카탈로그 항목은 어셈블리가 다시 등록될 때 적절하게 다시 설정됩니다. 구성되지 않은 기본값에 대한 자세한 내용은 온라인 설명서를 참조하십시오. 기본 구성 값은 특성 매개 변수의 기본 값입니다. 예를 들어, [Transaction] 특성을 사용하는 것은 TransactionOption.Required를 나타냅니다.

관리되는 클래스의 서비스에 대한 구성 데이터는 COM+ 카탈로그에 저장되기 때문에 어셈블리가 등록된 후 특정 카탈로그 항목이 관리상 수정될 수도 있습니다. 일부 서비스는 이런 방식으로 수정되어서는 안됩니다. 예를 들어, 카탈로그에서 트랜잭션 서비스를 비활성화하면 코드가 올바르게 작동되지 않을 수 있습니다. 개체 생성 문자열, 보안 역할 등과 같은 배포 관련 설정은 등록 후에도 조작될 수 있습니다. 사후 등록 설정을 만들 경우에는 서비스된 구성 요소가 포함된 어셈블리를 XCopy 배포만으로 완전히 배포할 수 없을 수도 있습니다. COM+ 응용 프로그램의 가져오기/내보내기 기능은 응용 프로그램을 현재 상태로 분배하도록 도와 줍니다. 가져오기 및 내보내기에 대한 자세한 내용은 원격 절에 제공됩니다.

카탈로그가 구성 데이터에서는 참조되지 않고 어셈블리 메타데이터에서만 추출되는 경우도 있습니다. AutoComplete, JIT, 개체 풀링(풀 크기는 카탈로그에서 추출됨), 보안 메서드 특성 등의 경우가 그렇습니다. 이 문제에 대한 자세한 내용은 서비스를 설명하는 해당 절을 참조하십시오.

어셈블리를 등록하는 동안 COM+에 필요한 GUID가 자동으로 생성됩니다. 어셈블리가 서명되지 않을 경우 GUID는 형식 이름과 네임스페이스 이름만을 기반으로 하여 생성됩니다. 따라서, 어셈블리가 서명되지 않으면 고유하지 않은 GUID가 생성될 수 있습니다. COM+ 서비스를 사용하지 않지만 고유한 형식 이름이 필요한 .NET 어셈블리의 경우에도 마찬가지입니다. 따라서, COM+ 서비스를 사용하는 어셈블리는 서명되어야 합니다. 어셈블리가 서명되지 않으면 등록되지 않습니다. 등록은 COM+ 서비스를 사용하는 .NET 클래스에 글로벌 구성 데이터 저장소가 하나 있다는 의미를 내포합니다. 전용 어셈블리를 여러 응용 프로그램 디렉터리에 복사할 수 있더라도 궁극적으로는 그런 응용 프로그램들도 모두 서비스되는 구성 요소에 대해 하나의 구성 데이터를 참조합니다. 따라서, COM+ 카탈로그에서 구성 데이터를 변경하면 해당 클래스를 사용하는 모든 응용 프로그램이 영향을 받습니다. 이것은 서비스되는 구성 요소를 사용하는 동일한 어셈블리 사본을 구성하는 Microsoft ASP.NET 구성에 여러 vroot가 있다는 증거입니다. 동일한 응용 프로그램이 여러 구성을 갖게 하려면 Microsoft Windows .NET에서 COM+ 파티션을 사용합니다. .NET에서 COM+ 파티션을 사용하려면 ApplicationID 특성을 사용하지 않고 동일한 구성 요소를 여러 파티션에 설치하십시오. COM+에는 고유한 응용 프로그램 ID가 필요합니다.

일반적으로 클라이언트가 클라이언트 응용 프로그램 디렉터리가 아닌 다른 디렉터리에 있는 어셈블리에 액세스해야 하거나 어셈블리가 클라이언트 디렉터리에 없는 다른 프로세스에 로드되는 경우 GAC가 사용됩니다. 개념적으로, 서비스되는 구성 요소를 사용하는 전용 어셈블리는 실제로 공유되는 구성 데이터를 사용하는 공유 어셈블리입니다. ApplicationActivationOption이 라이브러리에 설정되면 어셈블리 내의 클래스에서 트랜잭션을 사용할 수 있습니다. 모든 어셈블리가 동일한 디렉터리에서 로드될 경우 하나의 클라이언트에서 해당 어셈블리를 사용할 수 있습니다. ApplicationActivationOption을 사용하는 어셈블리가 서버에 설정되어 있으면, 해당 어셈블리는 dllhost.exe에 의해 로드되며, 대체로 클라이언트와 동일한 디렉터리에 존재하지 않습니다. COM+ 서버 응용 프로그램에서 서비스되는 구성 요소를 사용하는 어셈블리는 GAC에 배치되어야 합니다. COM+ 라이브러리 응용 프로그램에서 서비스되는 구성 요소를 사용하는 어셈블리는 서로 다른 디렉터리에 있지 않는 한 GAC에 배치되지 않아도 됩니다. ASP.NET에서 호스팅될 경우에만 예외입니다. 섀도 복사를 사용하여 올바르게 작업하려면 어셈블리를 GAC에 배치하지 않아야 합니다.

서비스되는 구성 요소를 사용하는 .NET 응용 프로그램을 제거하려면 GAC에서 해당 어셈블리를 제거(GAC에 등록되어 있을 경우)하고, regsvcs.exe를 사용하여 COM+에서 어셈블리를 다시 등록한 다음 해당 어셈블리와 연결된 형식 라이브러리를 삭제합니다.

버전 지정

GUID 특성을 사용하여 COM+에 필요한 GUID를 고정시킬 수 있습니다. 그러나, GUID를 명시적으로 사용하는 대신 버전 지정을 사용하는 것이 좋습니다. 클래스가 서로 다른 서비스 특성으로 데코레이팅되거나 새로운 메서드 서명이 만들어질 때마다 어셈블리의 주 버전 번호 또는 부 버전 번호가 증분되어야 합니다. 등록은 버전마다 한 번만 실행하면 됩니다. 새로운 어셈블리 버전을 등록하면 해당 버전에 대한 새로운 GUID가 생성되고 구성 요소가 동일한 구성 요소 이름을 사용하여 동일한 COM+ 응용 프로그램에 등록됩니다. 따라서 구성 요소는 COM+ 응용 프로그램에 여러 번 표시됩니다. 그러나, 각 구성 요소에는 GUID에 의해 지정되는 고유한 ID가 있습니다. 각 인스턴스는 구성 요소의 특정 버전을 참조합니다. Microsoft Visual Studio .NET을 사용하여 .NET 응용 프로그램을 작성할 때 통지되는 경우도 있습니다. 이 환경에서는 프로젝트에 [assembly: AssemblyVersion("1.0.*")] 특성이 추가됩니다. 프로젝트는 빌드마다 새로운 빌드 번호가 생성되기 때문에 어셈블리가 다시 등록되면 GUID가 새로 생성됩니다. 따라서, 가능하면 빌드 번호를 수동으로 증분하는 것이 좋습니다. 클라이언트는 CLR 버전 정책을 사용하여 어셈블리에 바인딩하기 때문에 COM+ 응용 프로그램에서 올바른 클래스 버전이 사용됩니다. 다음은 서비스되는 구성 요소를 사용하는 어셈블리를 작성하는 side-by-side 시나리오입니다. 아래에 사용된 일부 활성화 항목은 다음 절에 설명되어 있습니다.

  • 관리되는 클라이언트, 관리되는 서버, 고정되지 않은 GUID가 어셈블리에서 사용됩니다.
  • 클라이언트는 버전 정책에 지정된 어셈블리를 로드합니다.
  • 관리되는 클라이언트, 관리되는 서버, 고정 GUID가 사용됩니다.
  • 클라이언트가 클래스를 활성화하고 버전 정책을 사용하여 이전 어셈블리 버전으로 이동하면, 활성화하는 동안 코드에 있는 고정 GUID를 사용하여 카탈로그에서 서비스 정보를 추출합니다. 따라서, 이 GUID를 사용하여 마지막으로 등록된 어셈블리에서 추출한 정보를 사용하여 개체를 만듭니다. 실제로 개체가 최신 버전인 경우도 있기 때문에 만들어진 개체(v2)에서 코드에 있는 참조(v1)로 캐스트하려고 시도하면 형식 캐스트 예외가 발생될 수 있습니다.
  • 관리되는 클라이언트, 관리되는 서버, 고정되지 않은 GUID, 빌드 번호만 변경합니다.
  • 새로운 GUID가 생성되더라도 형식 라이브러리는 하나의 버전에 대해 두 개의 번호만 가질 수 있기 때문에 여전히 동일한 버전 번호를 갖습니다. 작동은 되지만, 버전 2를 버전 1 위에 설치하면 버전 1이 제거되고, 버전 2의 형식 라이브러리 등록이 취소됩니다. 해결 방법 1: 다음에 릴리스되는 .NET Framework(V1.1)에서는 형식 라이브러리를 사용하여 어셈블리 버전을 독립적으로 지정할 수 있게 하여 이 문제를 해결했습니다. 이것은 어셈블리 버전 번호를 변경하면 형식 라이브러리 버전도 함께 변경되어야 함을 의미합니다. 해결 방법 2: 주 버전 번호와 부 버전 번호만 사용합니다.
  • 관리되지 않는 클라이언트, 관리되는 서버, 고정되지 않은 GUID가 사용됩니다.
    • 클라이언트는 GUID를 사용하여 구성 요소를 만듭니다. Interop은 GUID에서 이름을 확인한 다음 버전 정책을 적용합니다. 동일한 어셈블리의 버전 1과 버전 2가 동일한 시스템에 있고 정책을 사용하여 버전 2로 이동하면 버전 2가 관리되지 않는 클라이언트가 됩니다.
    • 버전 1을 설치하고, 버전 2를 설치한 다음 버전 1을 제거합니다. 이제 버전 2로 리디렉션하는 버전 정책이 없으면 클라이언트가 구성 요소를 만들 수 없습니다. 또한, 버전 1 등록 정보에 대한 레지스트리 항목이 있어야 합니다. 제거된 버전 1에 대한 레지스트리 정보를 만드는 한 가지 방법은 Windows XP의 COM+ 별칭 기능을 사용하는 것입니다.

버전 지정은 동일한 COM+ 응용 프로그램에 있는 모든 구성 요소에 적용됩니다. 즉, 개별 응용 프로그램의 버전을 자동으로 지정할 수 있는 방법은 없습니다. 예를 들어, 버전 정책을 사용하여 응용 프로그램에 있는 역할의 버전을 지정할 수 없습니다. 응용 프로그램 이름 특성을 사용하여 응용 프로그램 버전을 지정합니다.

서비스되는 구성 요소

활성화

Enterprise Services 인프라는 컨텍스트의 개념을 기초로 합니다. 컨텍스트는 비슷한 실행 요구 사항을 갖는 개체를 위한 환경입니다. 서비스는 활성화하는 동안 및/또는 메서드 호출을 차단하는 동안 적용될 수 있습니다. COM+ 서비스는 관리되지 않는 코드로 작성되지만 .NET의 COM interop 기술을 사용할 때보다 .NET과 COM+ 서비스가 훨씬 깊게 통합됩니다. ServicedComponent에서 파생하지 않을 경우 등록 프로세스에서 원하는 효과를 얻을 수 없습니다.

서비스되는 구성 요소는 다양한 조합으로 활성화 및 호스트될 수 있습니다. 그림 3에서 설명한 것처럼 이 토론에서는 in-process(동일한 app-domain), 상호 app-domain(동일한 process), 상호 프로세스 활성화의 세 가지 경우를 참조합니다. 세 경우의 중요한 점은 구성 요소를 호출할 때 교차되는 범위입니다. in-process 활성화는 잠정적인 상호 컨텍스트 범위를 증가시키고, 상호 app-domain의 경우에는 상호 컨텍스트 범위와 상호 응용 프로그램 도메인 범위가 모두 있습니다. 반면에 상호 프로세스의 경우에는 상호 시스템/상호 프로세스와 상호 컨텍스트 범위가 함께 처리됩니다.

그림 3. 서비스되는 구성 요소의 활성화 호스트

서비스되는 구성 요소는 원격 .NET에 따라 구현되어 관리되지 않는 코드 또는 관리되는 코드로 작성된 서비스에 연결할 수 있는 포괄적인 메카니즘을 제공합니다. 서비스되는 구성 요소는 ContextBoundObject에서 파생되어 IDisposable와 같은 다양한 인터페이스를 구현합니다. ProxyAttribute 파생 사용자 지정 특성을 사용하면 CLR의 활성화 체인을 사용자에 맞게 쉽게 사용자 지정할 수 있습니다. 사용자 지정 실제 프록시를 작성하여 차단을 사용자에 맞게 설정할 수 있습니다. 새롭게 서비스되는 구성 요소 파생 클래스가 필요할 경우 활성화 호출이 CoCreateInstance에 대한 관리되는 C++ 래퍼를 실제로 호출하도록 활성화 체인을 사용자에 맞게 설정합니다. 이렇게 하면 COM+에서 이전에 등록된 어셈블리의 COM+ 카탈로그에 저장된 정보를 기반으로 하는 관리되지 않는 컨텍스트와 서비스를 설정할 수 있습니다. 이 단계는 지연 등록이 구현되는 단계이기도 합니다. 어셈블리를 등록하는 동안 InprocServer32 키는 mscoree.dll을 가리키므로, COM+ CreateInstance를 리디렉션하면 런타임으로 돌아와 실제 관리되는 개체가 만들어집니다. 따라서, 활성화하는 동안에 사용자 정의 실제 프록시 개체가 만들어집니다. 이 프록시의 in-process 버전을 서비스되는 구성 요소 프록시 또는 SCP라 합니다. 그림 4에 설명되어 있습니다.

그림 4. 활성화 경로

활성화 호출의 반환 경로에서는 관리되지 않는 COM+를 통해 관리되는 코드로부터 관리되는 참조를 마샬링한 다음 관리되는 코드로 돌아옵니다(그림 4, 라인 1의 역경로). 실제 개체가 만들어진 위치에 따라 클라이언트쪽에서 관련 양식으로 참조를 역마샬링합니다. in-process 활성화에서 그림 5는 참조가 투명한 프록시(TP)에 직접 참조로 역마샬링됨을 나타냅니다. 상호 app-domain 참조는 .NET 원격 프록시로 역마샬링됩니다. 상호 프로세스 또는 상호 시스템 참조(그림 6)에서는 추가 역마샬링이 필요합니다. 즉, COM interop은 활성화 및 역마샬링이 실행되는 동안 ServicedComponent에 의해 구현된 IManagedObject를 호출합니다. 원격 서비스되는 구성 요소 프록시(RSCP)는 활성화하는 동안 IServicedComponentInfo를 호출하여 서버 개체의 URI를 가져옵니다. 이것은 활성화하는 동안 두 개의 원격 호출이 만들어짐을 의미합니다. 메서드 수준에서 COM+ 역할 기반 보안이 필요할 경우 인터페이스를 역할에 연결해야 인프라가 해당 인터페이스를 호출할 때 역마샬링할 수 있습니다. 보안 절에서는 역할 기반 보안 구성에서 교차 프로세스 활성화와 마샬링이 갖는 함축된 의미에 대해 설명합니다.

그림 5. in-process 호출 인프라

그림 6. out of process 호출 인프라

사용자 지정 실제 프록시(차단용)를 만들고 관리되지 않는 컨텍스트를 만들기 위해 차단 서비스 구문을 실행하는 데 필요한 컨텍스트 인프라만 COM+에 남겨 두고 활성화 체인을 사용자에 맞게 설정했습니다. COM+ 컨텍스트는 이제 COM 개체가 아니라 관리되는 개체에 연결됩니다.

차단

그림 7에서는 in-process 메서드 호출 인프라를 보여 줍니다. 사용자 지정 프록시(SCP)를 사용하여 관리되는 호출을 차단할 수 있습니다. COM+ 컨텍스트 ID는 활성화하는 동안 SCP에 저장됩니다. 관리되는 개체가 서비스되는 구성 요소를 호출하면 대상 SCP에 저장된 컨텍스트 ID를 현재 컨텍스트의 컨텍스트 ID와 비교하여 두 컨텍스트 ID가 동일할 경우 실제 개체에서 호출이 직접 실행됩니다. 컨텍스트 ID가 서로 다르면 SCP가 COM+에 대한 호출을 만들어 컨텍스트를 전환한 다음 메서드 호출을 받을 서비스를 렌더링합니다. in-process 호출의 경우에는 AppDomain이 COM+가 되는 점을 제외하고는 AppDomain.DoCallBack과 비슷합니다. 'DoCallBack' 함수가 COM+(그림 7의 2단계)에 전달되어 컨텍스트를 전환하고 서비스를 렌더링한 다음 콜백 함수가 SCP를 호출합니다. SCP는 데이터 마샬링을 실행하고 실제 개체에서 메서드를 호출합니다. 메서드가 종료되면 반환 경로를 통해 COM+가 메서드 호출을 빠져나올 수 있는 구문을 렌더링할 수 있습니다(그림 7의 5단계). COM+는 서비스를 렌더링하는 데만 사용됩니다. 데이터 마샬링과 메서드 호출은 .NET 런타임에서 실행되므로, 메서드를 호출할 때 유형을 변환(예: String에서 BSTR로)할 필요가 없습니다. COM interop를 in-process 호출에 사용한 경우 데이터를 마샬링해야 합니다. in-process 호출의 경우 관리되지 않는 코드의 서비스 렌더링 요청은 COM interop 호출이 아닙니다.

그림 7. in-process 호출 인프라

정적 메서드에 대한 호출은 투명하고 실질적인 프록시에 전달되지 않습니다. 따라서, 정적 메서드에서는 차단 서비스를 사용할 수 없습니다. 정적 메서드는 클라이언트의 컨텍스트 내부에서 호출됩니다. 내부 메서드는 올바른 컨텍스트에서 호출됩니다. 이것은 새 트랜잭션을 위해 구성된 개체에 대한 내부 메서드를 호출하는 클라이언트가 새 트랜잭션에 포함됨을 의미합니다. 그러나, 메서드 수준 서비스는 COM+ 카탈로그(자세한 내용은 이 항목 다음에 있는 보안 항목 참조)에 인터페이스가 있어야 하기 때문에 내부 메서드는 메서드 수준 서비스를 위해 구성될 수 없습니다. 서비스는 속성에 적용될 수 있지만 메서드 수준 속성(예: AutoComplete)은 getter/setter 메서드에 개별적으로 배치되어야 합니다.

AutoComplete 특성을 활용하면 코드를 작성하지 않고 트랜잭션을 사용하여 서비스에 편리하게 액세스할 수 있습니다. ContextUtil.SetAbort 또는 ContextUtil.SetComplete를 사용할 수도 있습니다. 메서드의 속성에 있는 확인란 하나를 설정하여 COM+ 탐색기에서 이 서비스를 구성할 수 있습니다. 그러나, 관리되는 개체는 인터페이스를 구현할 필요가 없습니다. 서비스되는 구성 요소의 경우에도 마찬가지입니다. 인터페이스에서 메서드가 선언되지 않으면 등록 시 카탈로그에 메서드 수준 서비스를 구성할 수 없으므로, 구성이 메타데이터에만 저장될 수 있습니다. 메서드에 대한 인터페이스가 없으면 AutoComplete 특성이 있을 경우 IRemoteDispatch.RemoteDispatchAutoDone에 저장된 구성 정보를 사용하여 SCP에서 컨텍스트 전환이 실행됩니다. AutoComplete가 없을 경우 IRemoteDispatch.RemoteDispatchNotAutoDone이 사용됩니다. IRemoteDispatch는 ServicedComponent에 의해 구현되는 인터페이스입니다. 관리되지 않는 클라이언트는 IDispatch(런타임에 바인딩)를 사용하여 인터페이스가 없는 서비스된 구성 요소만 호출할 수 있으므로 이 경우 실제 프록시가 없기 때문에 AutoComplete 구문을 사용할 수 없습니다. 인터페이스가 사용되는 경우에도 AutoComplete의 구성은 관리되는 클라이언트의 매타데이터에 의해 제어됩니다. DCOM 메서드 호출은 out of process의 경우에만 RemoteDispatchAutoDone에 만들어집니다. Out-of-process 구성 요소는 DoCallBack 메카니즘을 사용하지 않는 대신 DCOM을 사용하여 호출을 배달하고 서비스를 렌더링합니다. 인터페이스에 메서드가 있으면 원격 서비스되는 구성 요소의 인터페이스 메서드는 DCOM을 사용하여 호출됩니다. 메서드가 없으면 호출이 ServicedComponent에 있는 IRemoteDispatch 인터페이스에 발송됩니다. 이것은 Dispose()와 같은 호출이 DCOM을 통해 호출됨을 의미합니다. 이것의 의미에 대해서는 나중에 설명합니다.

컨텍스트

ContextUtil 클래스를 사용하여 관련된 COM+ 개체 컨텍스트와 해당 속성을 액세스합니다. 이 클래스는 관리되지 않는 코드에서 CoGetObjectContext에 의해 반환되는 개체와 비슷한 기능을 제공합니다. 서비스되는 구성 요소와 연결된 관리되는 개체 컨텍스트는 관련있는 관리되지 않는 개체 컨텍스트와 다른 목적으로 사용됩니다. 이 클래스는 트랜잭션이 필요한 개체 하나(루트 역할)와 서비스되는 구성 요소에서 파생되지 않는 두 개체(컨텍스트 관리되는 개체를 빠르게 예시하는 자식 개체 역할)의 관리되는 세 개체를 작성하여 매니페스트됩니다. 서비스되지 않는 구성 요소는 Transactions이 지원하여 서비스되는 구성 요소처럼 동작합니다. 즉, 리소스 관리자를 호출하고 필요한 경우 ContextUtil.SetAbort를 사용할 수 있습니다. 루트 개체가 만들어지면 관련된 관리되지 않는 컨텍스트가 만들어진 다음 현재 스레드에 연결됩니다. 자식 개체가 호출되면 해당 개체는 관리되지 않는 컨텍스트와 관련이 없기 때문에 COM+ 컨텍스트를 변경할 필요가 없습니다. 따라서, 스레드는 루트의 관리되지 않는 컨텍스트 ID를 계속해서 유지합니다. 자식 개체가 리소스 관리자를 호출하면 리소스 관리자는 해당 자식 개체를 실행하는 스레드에서 관리되지 않는 컨텍스트를 추출합니다. 이 컨텍스트는 루트 개체의 관리되지 않는 컨텍스트입니다. 관리되지 않는 컨텍스트에 의존하는 것은 매우 위험하므로 이후 버전에서는 관리되지 않는 컨텍스트를 관리되는 컨텍스트에 병합하여 자식 개체를 잠정적으로 다른 관리되는 컨텍스트에 연결할 것입니다. 리소스 관리자는 루트 개체의 컨텍스트를 선택할 수 없습니다. 따라서, 새로운 버전의 .NET으로 업그레이드하면 이런 유형의 동작에 의존하는 코드가 중단될 수 있습니다.

성능 결과

이 절에서는 관리되는 클라이언트/관리되는 서버 서비스된 구성 요소 솔루션의 성능을 관리되지 않는 클라이언트/서버 솔루션과 비교합니다. in-process 메서드에 대해서는 다음 표에서 설명합니다. 트랜잭션에 대해 구성되는 ServicedComponent가 단지 번호만을 추가하는 단일 메서드를 사용하여 C#로 작성되었습니다. 비교를 위해 해당 C++ 구현을 사용했습니다. 이 비교에서는 실제로 작업하지 않고 관리되는 솔루션과 관리되지 않는 솔루션 사이의 차이점을 보여 줍니다. In-process 활성화 속도는 관리되는 솔루션에서 약 3.5배 더 느리고 메서드 호출 비용은 컨텍스트 스위치가 있을 때 약 2배 가량 더 비쌉니다. 그러나, 컨텍스트 전환이 필요한 서비스되는 구성 요소 메서드와 컨텍스트 전환이 필요하지 않는 서비스되는 구성 요소 메서드를 비교해 보면, in-process 서비스되는 구성 요소 차단 인프라의 성공을 나타내는 약 3등급의 차이가 발생합니다. out of process 솔루션의 경우 활성화 비용이 약 2배 더 비싸고, 상호 컨텍스트 메서드 호출 비용은 약 3배 가량 더 비쌉니다.

표 1에서는 관리되는 솔루션과 관리되지 않는 솔루션을 사용할 때의 in-process 활성화와 메서드 호출의 조정되는 시간을 비교하여 보여 줍니다.

표 1. In-process 활성화 및 메서드 호출

  관리되는 솔루션 관리되지 않는 솔루션
활성화 35 10
Cross-context-do-nothing 메서드 호출 2 1
Cross-context-do-work 메서드 호출 200 100

활성화는 'do-nothing' 메서드에 대한 메서드 호출보다 약 한 등급 정도 비용이 비쌉니다. 일부 작업에 추가하면 활성화 및 메서드 호출 시와 동일한 등급의 관련이 없는 DTC 트랜잭션만 가져옵니다. 메서드 호출이 풀링된 데이터베이스 연결을 열면 메서드 호출 작업의 등급이 활성화와 'do-nothing' 메서드 호출을 합한 것보다 한 등급 더 커지므로, 서비스되는 구성 요소 인프라의 오버헤드가 실제 작업이 실험에 추가될 때 이론적으로 일치됩니다.

개체 수명

Just-In-Time 활성화

JIT(just-in-time) 서비스는 격리에는 일반적으로 사용되지 않습니다. 트랜잭션 서비스 및 종종 개체 풀링과 함께 암시적으로 사용됩니다. 다음 예는 일부 관심 항목을 강조 표시하는 데 도움이 됩니다. 아래 코드에서 .NET 클래스는 JIT 서비스만을 사용하여 작성됩니다.

using System;
using System.EnterpriseServices;
[assembly: AssemblyKeyFile("Demos.snk")]
[assembly: ApplicationName("JITDemo")]

namespace Demos
{
[JustInTimeActivation]
public class TestJIT : ServicedComponent
{
public TestJIT()
{ // 호출되기 전에
}
[AutoComplete]
public void DoWork ()
{ // 다음을 사용하여 결과 표시 ..
// 1. 자동 완성 특성 또는
// 2. ContextUtil.DeactivateOnReturn = true 또는
// 3. ContextUtil.SetComplete();
}
public override void Dispose(bool b)
{ // 이 메서드를 선택적으로 재지정하고 사용자에 맞게 설정할 수 있는
// 삭제 논리를 실행합니다. b==true이고, Dispose()가
// 클라이언트에서 호출되었고 false이면 GC는 해당 개체를 정리합니다.
}
}
}

클래스는 ServicedComponent에서 파생되고 JIT 특성을 사용하여 필요한 특정 서비스를 나타냅니다. 관리되지 않는 코드에서 Activate 및 Deactivate 메서드를 재정의하려면 클래스에서 IObjectControl 인터페이스를 구현해야 합니다. ServicedComponent 클래스는 Activate 및 Deactivate 이벤트를 처리하기 위해 재정의될 수 있는 가상 메서드를 갖습니다. 그러나, ServicedComponent와 실제 프록시인 SCP 모두 IObjectControl을 구현하지 않습니다. 대신 SCP는 COM+에서 IObjectControl 인터페이스를 요청하면 프록시를 해체합니다. 해체시 COM+의 호출은 ServicedComponent의 가상 메서드에 전달됩니다. 메서드의 AutoComplete 특성을 사용하거나, ContextUtil.SetComplete(), ContextUtil.SetAbort()을 호출하거나 ContextUtil.DeactivateOnReturn을 설정하여 DeactivateOnReturn 비트를 설정합니다. 메서드가 호출될 때마다 DeactivateOnReturn 비트가 설정된다고 가정할 경우 메서드는 클래스 생성자, Activate, 실제 메서드 호출, Deactivate, Dispose(true), 클래스 종료자(있을 경우)의 순으로 호출됩니다. 다른 메서드가 호출되면 동일한 시퀀스가 반복됩니다. 우수한 디자인을 위해서는 Activate 및 Deactivate 메서드만 무시하여 개체를 제거한 다음 개체 풀로 다시 놓을 때를 잘 알고 있어야 합니다. Activate 및 Deactivate의 나머지 논리는 클래스 생성자와 Dispose(bool) 메서드에 놓여야 합니다. DeactivateOnReturn 비트는 다음과 같은 방법으로 설정될 수 있습니다.

  1. 클라이언트는 단일 메서드 호출에 대해서만 개체 상태를 사용합니다. 메서드에서 시작할 때 새로운 실제 개체가 만들어져 SCP에 첨부됩니다. 메서드가 종료될 때 Dispose(true)와 실제 개체 종료자(있을 경우)가 차례로 호출되어 실제 개체가 비활성화됩니다. 그러나, 연결된 COM+ 컨텍스트, SCP 및 TP는 활성 상태로 유지됩니다. 클라이언트 코드는 실제 개체라고 판단되는 참조를 그대로 유지합니다(투명한 프록시). 클라이언트가 동일한 참조에 대해 다음 메서드 호출을 만들면 새로운 실제 개체를 만들어 SCP에 첨부하여 메서드 호출 서비스를 제공합니다. 새 개체 만들기 요구사항을 제거하려면 개체 풀링 절을 참조하십시오. 실제 개체를 비활성화하려면 메서드 호출이 종료될 때 실제 개체가 완료 표시되어야 합니다. 이것은 다음을 사용하면 가능합니다.
    1. 클래스 메서드의 AutoComplete 특성
    2. ContextUtil class, DeactivateOnReturn 또는 SetComplete에 대한 두 메서드 호출 중 하나
  2. 클라이언트는 메서드를 종료하기 전에 완료 비트를 false로 설정하여 각 메서드 호출 후에 개체를 비활성화하지 않고 동일한 개체에서 여러 메서드를 호출합니다. 예를 들어, 양식 수준에서 JIT를 사용하는 서비스되는 구성 요소의 범위를 지정하고 메서드의 완료 비트를 false로 명시적으로 설정하여 두 양식 단추를 사용하여 동일한 개체 인스턴스에서 메서드를 호출합니다. 완료 비트가 true로 설정되어야 하는 경우도 있습니다. 이 접근 방법은 계약이 클라이언트와 개체 사이에 있다는 것을 나타냅니다. 이는 클라이언트에 의해 명시적 또는 암시적으로 실행될 수 있습니다.
    1. 클라이언트는 완료된 개체에서 특정 메서드를 호출하여 해당 개체를 비활성화하는 방법을 알고 있어야 합니다. 메서드 구현에서는 옵션 1의 아이디어를 사용합니다. 개체 참조는 동일한 호출 시퀀스를 사용하여 다시 호출될 수 있으며 이는 실제 개체를 새로 만들다는 것을 의미합니다.
    2. 개체는 클라이언트가 해당 개체에서 Dispose() 메서드를 호출하면 명시적으로 삭제됩니다. Dispose()는 ServicedComponent에 정의된 메서드이며, Dispose(true)와 클래스 종료자(있는 경우)를 차례로 호출한 다음 관련된 COM+ 컨텍스트를 해체합니다. 이 경우 개체 참조에 대한 추가 메서드 호출은 더 이상 만들어질 수 없습니다. 추가 메서드 호출을 시도하면 예외가 throw됩니다. 많은 클라이언트에서 동일한 개체를 사용할 경우 마지막 클라이언트에서 개체 작업이 완료되면 Dispose() 호출이 완료되어야 합니다. 그러나, JIT 개체의 상태 비저장 특성으로 인해 클라이언트 모델별로 단일 인스턴스를 사용하는 디자인 연습을 해야 합니다.
    3. 개체는 완료 비트를 절대 true로 설정하지 않고 클라이언트는 절대 Dispose()를 호출하지 않습니다. 실제 개체, 프록시 및 컨텍스트는 가비지 수집이 발생되면 삭제됩니다. GC에 의해 초기화되는 메서드 호출 순서는 Deactivate, Dispose(false), 클래스 종료자(있을 경우)입니다.

모든 서비스되는 구성 요소에는 SCP(원격의 경우 RSCP)에 참조로 저장되는 관련된 COM+ 컨텍스트가 있습니다. 참조는 GC가 발생되거나 클라이언트가 Dispose()를 호출하는 경우에만 릴리스됩니다. GC에 의존하여 컨텍스트를 정리하지 않는 것이 좋습니다. COM+ 컨텍스트는 하나의 OS 핸들을 잡고 있으며 일부 메모리에서는 GC가 발생할 때까지 핸들의 해제가 지연될 수 있습니다. 또한, ServicedComponent에 종료자가 없더라도 SCP는 종료자를 구현합니다. 즉, COM+ 컨텍스트 참조는 첫 번째 수집 단계에서 정확히 수집됨을 의미합니다. 실제로, SCP에서 종료자가 호출될 때 컨텍스트는 종료자 스레드에 의해 삭제되지 않고, 종료자 스레드로부터 컨텍스트 삭제 작업을 제거하여 내부 대기열에 놓습니다. 서비스되는 구성 요소의 생성, 사용 및 삭제가 빠르게 진행되는 업무가 가중되는 환경에서는 작업에 의해 종료자 스레드가 소비될 수 있기 때문에 이 작업이 수행됩니다. 대신, 내부 스레드가 대기열에 서비스를 제공하고 이전 컨텍스트는 삭제됩니다. 또한, 새 ServicedComponent를 만드는 응용 프로그램 스레드는 대기열에서 항목을 제거하려고 시도한 다음 이전 컨텍스트를 삭제합니다. 따라서, 클라이언트에서 Dispose()를 호출하면 클라이언트 스레드를 사용하여 COM+ 컨텍스트를 바로 해체하여 컨텍스트가 사용하는 핸들과 메모리 리소스를 해제합니다. Dispose()를 호출하면 예외가 throw되는 경우도 있습니다. 한 가지 경우는 개체가 루트가 아닌 중단된 트랜잭션 컨텍스트에서 활성화되는 경우입니다. 이 경우 Dispose()를 호출하면 CONTEXT_E_ABORTED 예외가 발생될 수 있습니다. 다른 경우에 대해서는 개체 풀링에 설명되어 있습니다.

성능의 관점에서는 ServicedComponent 파생 클래스에서 종료자를 구현하는 대신 이 논리를 Dispose(bool) 메서드에 놓는 것이 더 좋습니다. SCP는 종료자를 구현하지만 실제 개체의 종료자는 반사를 통해 호출됩니다.

다음은 JIT 사용에 대한 좋은 디자인 사례입니다.

  • 생성자 및 Dispose(bool) 메서드에 사용자 지정 활성 및 종료 코드를 배치하여 종료자를 구현하지 않고 메서드에 있는 AutoComplete 특성을 사용하여 종료를 나타내는 단일 호출 패턴을 사용합니다.
  • 클라이언트에서 개체 작업이 완료되면 클라이언트에서 Dispose()를 호출합니다.

이 토론에서는 클라이언트가 관리되고 구성 요소가 in-process인 것으로 가정합니다. 구성 요소가 out-of-process인 경우:(자세한 내용은 원격 절 참조)

  • GC는 클라이언트 활성 개체에 대한 .NET 원격 임대 기간이 만료되면 해당 개체를 정리합니다.
  • 앞에서 설명한 것처럼 out-of-process 구성 요소에서 메서드를 호출하면 DCOM을 사용하여 컨텍스트를 전환한 다음 메서드 호출을 전달합니다. 구성 요소가 JIT에 의해 비활성화된 다음 Dispose()가 호출되면 서버 컨텍스트가 입력되고 실제 개체가 다시 만들어져 DCOM을 호출한 다음 다시 비활성화됩니다. in-process 구성 요소의 경우 실제 개체가 비활성화되면 구성 요소를 재활성화하는 Dispose() 호출이 서비스되기 전에는 올바른 컨텍스트로 전환하려는 시도 없이 해당 컨텍스트만 삭제됩니다.

개체 풀링

개체 풀링의 기본 전제는 개체 재사용입니다. 개체 풀링은 대부분 JIT와 함께 사용됩니다. 풀링된 COM 구성 요소와 풀링된 .NET 구성 요소의 경우에도 마찬가지입니다.

using System;
using System.EnterpriseServices;
[assembly: AssemblyKeyFile("Demos.snk")]
[assembly: ApplicationName("OPDemo")]

namespace Demos
{
[ObjectPooling(MinPoolSize=2, MaxPoolSize=50, CreationTimeOut=20)]
[JustInTimeActivation]
public class DbAccount : ServicedComponent
{
[AutoComplete]
public bool Perform ()
{ // 작업을 실행합니다.
}
public override void Activate()
{ // .. 활성화 메시지를 처리합니다.
}
public override void Deactivate()
{ // .. 비활성화 메시지를 처리합니다.
}
public override bool CanBePooled()
{ // .. CanBe Pooled 메시지를 처리합니다.
// 기본 구현은 false를 반환합니다.
return true;
}
}
}

JIT를 사용하는 경우처럼 개체 풀링은 다음과 같은 두 가지 방법 중 하나로 사용될 수 있습니다.

  1. 단일 호출 패턴. 코드에서 개체는 클라이언트가 메서드를 호출하려고 시도할 때 풀로부터 검색된 다음 JIT가 개체 풀링과 함께 사용되고 메서드를 호출하는 동안 완료 비트가 true로 설정되는 단일 메서드 호출이 종료되면 풀로 다시 반환됩니다. JIT 사용과 동일한 단일 호출 접근법이 여기서도 적용됩니다. 생성자는 개체가 만들어져 풀에 배치될 때 한 번만 호출됩니다. JIT 및 풀링된 개체를 사용할 때 메서드 호출 순서는 활성화, 메서드 호출, 비활성화, CanBePooled입니다. CanBePooled가 true를 반환하면 개체가 풀에 다시 놓입니다. 컨텍스트는 앞에서 설명한 것처럼 활성 상태로 유지됩니다. 서비스되는 구성 요소는 매개 변수화된 생성자를 사용할 수 없기 때문에 임의의 개체가 풀로부터 추출된 후의 후속 메서드 호출에서는 생성자를 다시 호출하지 않고 동일한 메서드 호출 순서가 반복됩니다. 마지막으로, 클라이언트가 풀링된 개체에서 Dispose()를 호출하면 in-process 경우에서 컨텍스트만 삭제됩니다. out-of-process의 경우에는 앞에서 설명한 것처럼 Dispose()를 호출하여 개체를 다시 활성화할 수 있습니다. 개체가 풀링되면 해당 풀에서 개체를 가져와야 합니다. 즉, Dispose()는 CO_E_ACTIVATION_TIMEOUT과 함께 예외를 throw할 수 있습니다.
  2. 다중 호출 패턴. 비슷한 여러 메서드 호출 접근법을 사용하여 JIT 서비스를 강조 표시하면 많은 개체에 대한 많은 메서드 호출이 있은 후에만 풀로 다시 배치될 수 있습니다. 그러나, 클라이언트가 Dispose를 호출하지 않고 JIT가 사용되지 않으면 GC에 의해 개체가 풀에 다시 놓일 때 종료해야 하는 풀링된 개체의 자식 개체가 부활되는지 확인할 수 없습니다. 풀링된 개체가 가비지 수집되면 구성원이 여전히 유효한 비활성화가 보장되지 않습니다. 다음에 릴리스되는 .NET Framework(V1.1)에서는 canBePooled 및 Deactivate가 호출되지 않고 개체가 풀로 다시 놓이지 않습니다. 이 접근법에는 보다 일관성 있는 모델이 있습니다. Deactivate에서 자식 개체가 활성화되고, Dispose()에서는 자식 개체의 활성 상태가 보장되지 않습니다. 따라서, JIT를 사용하지 않는 풀링된 개체에 대해 Dispose()를 호출해야 합니다. 그렇지 않으면 개체가 풀로 반환되지 않습니다.

관리자는 어셈블리가 배포 및 등록된 후에 풀 크기와 시간 초과를 수정할 수 있습니다. 풀 크기 변경은 프로세스가 다시 시작될 때 적용됩니다. Windows XP 이상에서 풀 크기는 프로세스 내에 있는 각 응용 프로그램 도메인에 적용됩니다. Windows 2000에서 풀 크기는 기본 응용 프로그램 도메인에 존재하는 풀링된 개체의 프로세스 너비입니다. 동일한 프로세스 내의 다른 응용 프로그램 도메인에서 풀링된 개체를 필요로 할 경우 클라이언트는 응용 프로그램 도메인과 풀링된 개체 사이에서 효과적으로 통신합니다. 이것을 실현하는 방법은 각 IIS vroot가 개별 응용 프로그램 도메인에 하우징되는 ASP.NET 응용 프로그램 내부에서 COM+ 라이브러리 응용 프로그램에 정의된 풀링된 .NET 개체를 사용하는 것입니다.

서비스되는 구성 요소는 매개 변수화된 생성자를 사용할 수 없습니다.

보안

CAS(코드 액세스 보안)

.NET Framework 보안을 사용하면 실행 권한이 있는 경우에만 코드로 리소스를 액세스할 수 있습니다. 이것을 설명하기 위해 .NET Framework는 보호되는 리소스를 액세스할 수 있는 코드의 권한을 의미하는 사용 권한의 개념을 사용합니다. 코드는 필요한 사용 권한을 요청합니다. .NET Framework에는 코드 액세스 권한 클래스가 제공됩니다. 또한, 사용자 지정 권한 클래스를 작성할 수 있습니다. 이 사용 권한을 사용하면 .NET Framework에서 작업을 허용할 때 코드에 필요한 내용 및 코드 호출자에 필요한 권한을 나타낼 수 있습니다. System.EnterpriseServices를 통과하는 모든 코드 경로에는 관리되지 않는 코드 권한이 필요합니다.

.NET의 코드 액세스 보안은 웹으로부터 코드를 다운로드하여 작성자를 완전히 신뢰할 수 없는 응용 프로그램에서 가장 유용합니다. 일반적으로 서비스되는 구성 요소를 사용하는 응용 프로그램은 완전히 신뢰할 수 있고, 여러 프로세스 간에 적용되는 보안을 필요로 하며, 배포시에 역할 구성을 가능하게 합니다. 이 기능은 COM+ 역할 기반 보안에 노출되는 기능입니다.

System.EnterpriseServices를 통과하는 모든 코드 경로에는 관리되지 않는 코드 권한이 필요합니다. 이것은 다음과 같은 의미를 내포합니다.

  • 관리되지 않는 코드 사용 권한은 서비스되는 구성 요소에 대한 상호 컨텍스트 호출을 활성화 및 실행하는 데 필요합니다.
  • 서비스되는 구성 요소에 대한 참조가 신뢰할 수 없는 코드에 전달될 경우 신뢰할 수 없는 코드로부터 ServicedComponent에 정의된 메서드를 호출할 수 없습니다. 그러나, 일부 환경에서는 ServicedComponent에서 파생된 클래스에 정의된 사용자 지정 메서드를 신뢰할 수 없는 코드로부터 호출할 수 있습니다. 컨텍스트 전환, 차단 서비스 등이 필요하지 않거나 메서드 구현에서 System.EnterpriseServices의 구성원을 호출할 수 없는 경우에는 신뢰할 수 없는 코드로부터 호출을 만들 수 있습니다.

또한, .NET 버전 1에서는 스레드가 전환될 때 보안 스택이 복사되지 않기 때문에 서비스되는 구성 요소에서 사용자 지정 보안 권한을 사용할 수 없습니다.

RBS(약할 기반 보안)

System.EnterpriseServices에서는 COM+ 보안 메카니즘의 기능을 미러하는 .NET 개체에 보안 서비스를 제공합니다. COM+ 서버 응용 프로그램을 사용하여 구성 요소를 호스트할 경우 RBS는 DCOM 전송 프로토콜을 사용하여 원격 클라이언트에서 구성 요소를 활성화해야 합니다. 원격에 대한 자세한 내용은 다음 절을 참조하십시오. COM+의 보안 호출 컨텍스트와 ID는 관리되는 코드에서 사용할 수 있습니다. 또한, CoImpersonateClient, CoInitializeSecurity 및 CoRevertClient는 서버쪽에서 일반적으로 사용되는 익숙한 호출인 반면, CoSetProxyBlanket는 클라이언트쪽에서 일반적으로 사용됩니다.

특성 사용, 역할에 사용자 추가, 프로세스 보안 ID 설정 등과 같은 특정 보안 설정은 메타데이터에 저장되지 않습니다. 그러나, 어셈블리 수준 특성을 사용하여 COM+ 서버 응용 프로그램의 COM+ 탐색기의 보안 탭에 표시되는 내용을 구성할 수 있습니다.

  • 응용 프로그램에 대해 인증 사용
    (ApplicationAccessControlAttribute(bool)). 이 탭에서는 RBS를 실제로 지원해야 합니다.
  • 보안 수준 (ApplicationAccessControlAttribute(AccessChecksLevelOption)). AccessChecksLevelOption.Application으로 설정되면 응용 프로그램에서 역할에 지정된 사용자는 프로세스 보안 설명자에 추가되고 구성 요소, 메서드 및 인터페이스 수준에서의 미세 조정 검사 기능이 해제됩니다. 보안 검사는 응용 프로그램 수준에서만 실행되며 라이브러리 응용 프로그램의 프로세스 수준 보안은 호스트 프로세스에 따라 달라집니다. 특성이 AccessChecksLevelOption.ApplicationComponent로 설정되면 응용 프로그램에서 역할에 지정된 사용자가 프로세스 보안 설명자에 추가되고 응용 프로그램에서 역할 기반 보안 검사가 실행됩니다. 또한, ComponentAccessControl 특성을 클래스에 적용하여 RBS를 필요로 하는 모든 구성 요소에서 액세스 검사를 실행해야 합니다. 라이브러리 응용 프로그램에서 역할 기반 보안 검사는 서버 응용 프로그램처럼 실행됩니다. 보안 속성은 응용 프로그램 내에 있는 모든 개체의 컨텍스트에 포함되며 보안 호출 컨텍스트를 사용할 수 있습니다. 개체에 작성자의 컨텍스트와 호환되지 않는 구성이 있으면 자체 컨텍스트로 활성화됩니다. 프로그래밍 역할 기반 보안은 보안 호출 컨텍스트의 사용 가능성에 따라 달라집니다.

    의미 있는 액세스 검사를 위해 COM+ 라이브러리 응용 프로그램에서 작업하려면 프로세스 및 구성 요소 수준에서 액세스 검사를 실행하도록 선택합니다.

  • 가장인증 선택 항목은 ApplicationAccessControl 특성의 ImpersonationLevel 및 Authentication 속성과 해당합니다.

    SecurityRole 특성은 어셈블리, 클래스, 또는 메서드 수준에 적용될 수 있습니다. 어셈블리 수준에 적용될 경우 해당 역할에 있는 사용자는 응용 프로그램에 있는 모든 구성 요소를 활성화할 수 있습니다. 클래스 수준에 적용될 경우 해당 역할에 있는 사용자는 해당 구성 요소에 있는 모든 메서드를 호출할 수 있습니다. 응용 프로그램 및 클래스 수준 역할은 메타데이터에 구성되거나, COM+ 카탈로그에 액세스하여 관리 방식으로 구성될 수 있습니다.

    메타데이터를 사용하여 어셈블리 수준으로 RBS 구성:

    [assembly: ApplicationAccessControl(true,
    AccessCheckLevel=AccessChecksLevelOption.ApplicationComponent)]
    // 이 역할에 NTAuthority\everyone을 추가합니다.
    [assembly:SecurityRole("TestRole1",true)]
    // 관리를 위해 역할에 사용자를 추가합니다.
    [assembly:SecurityRole("TestRole2")]

    RBS를 메타데이터에 클래스 수준으로 구성:

    [assembly: ApplicationAccessControl(true,
    AccessCheckLevel=AccessChecksLevelOption.ApplicationComponent)]
    ?[ComponentAccessControl()]
    [SecurityRole("TestRole2")]
    public class Foo : ServicedComponent
    {
    public void Method1() {}
    }

    어셈블리 수준 또는 클래스 수준의 RBS는 어셈블리가 등록된 후 COM+ 카탈로그에 존재하므로 관리 방식으로 구성될 수 있습니다. 그러나, 앞에서 설명한 것처럼 클래스 메서드는 COM+ 카탈로그에 표시되지 않습니다. 메서드에서 RBS를 구성하려면 클래스가 인터페이스의 메서드를 구현하고 클래스 수준에서 SecureMethod 특성을 사용하거나, 메서드 수준에서 SecureMethod 또는 SecurityRole 특성을 사용해야 합니다. 또한, 특성이 인터페이스 정의의 인터페이스 메서드가 아니라 클래스 메서드 구현에 나타나야 합니다.

  • 메서드에서 RBS를 사용하는 가장 쉬운 방법은 SecureMethod 특성을 클래스 수준에 적용한 다음 역할을 구성(SecurityRole 특성을 메서드에 배치하거나 관리 방식으로)하는 것입니다.
    [assembly: ApplicationAccessControl(true,
    AccessCheckLevel=AccessChecksLevelOption.ApplicationComponent)]
    Interface IFoo
    {
    void Method1();
    void Method2();
    }
    [ComponentAccessControl()]
    [SecureMethod]
    public class Foo : ServicedComponent, IFoo
    {
    // 관리를 위해 이 메서드에 역할을 추가합니다.
    public void Method1() {}
    // "RoleX"가 이 메서드용 카탈로그에 추가됩니다.
    SecurityRole("RoleX")
    public void Method2() {}
    }

    클래스 수준에서 SecureMethod를 사용하면 클래스의 모든 인터페이스에 있는 모든 메서드가 COM+ 카탈로그의 역할에 관리 방식으로 구성될 수 있습니다. 클래스에서 이름이 같은 두 인터페이스를 구현하고 역할이 관리 방식으로 구성될 경우(클래스가 IFooMethod1과 같은 특정 메서드를 구현하지 않는 한) COM+ 카탈로그에 해당 역할이 표시되는 두 가지 메서드에 역할이 구성되어야 합니다. 그러나, 클래스 메서드에서 SecurityRole 특성이 사용될 경우에는 어셈블리가 등록될 때 이름이 같은 모든 메서드가 해당 역할에 자동으로 구성됩니다.

  • SecureMethod 특성은 메서드 레벨에도 배치될 수 있습니다.
    [assembly: ApplicationAccessControl(true, 
    AccessCheckLevel=AccessChecksLevelOption.ApplicationComponent)]
    Interface IFoo
    {
    void Method1();
    void Method2();
    }
    [ComponentAccessControl()]
    public class Foo : ServicedComponent, IFoo
    {
    // 관리를 위해 이 메서드에 역할을 추가합니다.
    [SecureMethod] // 또는 SecurityRole(SecureMethod++로 변환)을 사용합니다.
    public void Method1() {}
    public void Method2() {}
    }

    예에서 IFoo와 두 메서드는 COM+ 카탈로그에 표시되므로 관리상 두 메서드 모두에서 역할을 구성할 수 있지만, 메서드 수준 RBS는 Method1에만 적용됩니다. 메서드 수준 RBS에 참가해야 하는 모든 메서드에서 SecureMethod 또는 SecurityRole을 사용하거나 앞에서 설명한 것처럼 SecureMethod를 클래스 수준에 배치합니다.

RBS가 메서드 수준에 구성될 때마다 Marshaller 역할이 필요합니다. 메서드가 호출될 때 메서드에 RBS가 없을 경우 서비스되는 구성 요소 인프라가 IRemoteDispatch를 호출합니다. 메서드가 호출되고 RBS가 메서드에 구성되어 있을 경우(SecureMethod 특성이 있을 경우) 해당 메서드와 연결된 인터페이스를 사용하는 DCOM을 사용하여 메서드 호출이 만들어집니다. 따라서, DCOM은 RBS가 메서드 수준에 적용됨을 보장합니다. 그러나, 활성화 및 차단 절에서 설명한 것처럼 COM interop 및 RSCP는 IManagedObject(원격 활성기가 자체 공간으로 참조를 마샬링) 및 IServicedComponentInfo(원격 개체를 쿼리)를 호출합니다. 이들 인터페이스는 서비스되는 구성 요소에 연결됩니다. 구성 요소는 메서드 수준 검사를 실행하도록 구성되기 때문에 인프라가 성공적으로 호출하게 하려면 이들 인터페이스에 역할을 연결해야 합니다.

따라서, 어셈블리가 등록될 때 응용 프로그램에 Marshaller 역할이 추가된 다음 사용자를 이 역할에 추가해야 합니다. 대부분의 경우 모든 응용 프로그램 사용자가 이 역할에 추가됩니다. 이것은 메서드에 RBS를 구성하여 이 추가 구성 단계가 필요하지 않는 관리되지 않는 COM+의 경우와는 다소 차이가 있습니다. 등록하는 동안 이 역할에 '모든 사람'이 자동으로 추가되면 모든 사람이 활성화 권한 없이 구성 요소를 활성화할 수 있기 때문에(호출은 불가능) 보안 구멍이 생길 수 있습니다. 클라이언트가 개체를 삭제할 수 있도록 Marshaller 역할이 IDisposable 인터페이스에 추가됩니다. 사용자는 Marshaller 역할 대신 앞에서 설명한 세 인터페이스 각각에 대한 적절한 역할을 직접 추가하는 것입니다.

원격 구성 요소

ServicedComponent 클래스는 상속 트리에 MarshalByRefObject를 포함하고 있기 때문에 원격 클라이언트에서 액세스될 수 있습니다. 서비스되는 구성 요소를 원격으로 노출하는 방법은 여러 가지가 있습니다. 서비스되는 구성 요소를 원격으로 액세스하려면 다음과 같은 방법을 사용합니다.

  • ASP.NET에 기록되거나 ASP.NET에서 호출된 서비스되는 구성 요소가 있는 HTTP 채널은 높은 확장성 및 성능과 함께 우수한 보안 및 암호 옵션을 제공합니다. SOAP에서 사용될 경우 더 많은 상호 운용성 옵션이 있습니다. 서비스되는 구성 요소는 IIS/ASP.NET에 COM+ 라이브러리 응용 프로그램으로 호스트될 수 있습니다. COM+ 서버 응용 프로그램을 사용할 경우 IIS/ASP.NET 호스트는 DCOM을 사용하여 구성 요소에 액세스할 수 있습니다.
  • 서비스되는 구성 요소를 SOAP 끝점으로 노출하는 대체 방법에 대해서는 COM+ 웹 서비스: XML 웹 서비스의 확인란 사용에 설명되어 있습니다.
  • 서비스되는 구성 요소가 Dllhost에 호스트될 경우의 DCOM. 이 옵션은 최적의 성능과 보안, 시스템 간에 서비스 컨텍스트를 전달하는 기능 등을 제공합니다. 원격 기술을 선택할 때의 주요 디자인 문제는 서비스가 시스템 간에 제공되어야 하는지 여부입니다. 예를 들어, 한 시스템에서 트랜잭션을 만들고 해당 트랜잭션을 다른 시스템에서 지속해야 하는 서버 그룹 내에서 DCOM은 이 목적으로 사용될 수 있는 유일한 프로토콜입니다. 그러나, 클라이언트가 원격 ServicedComponent만 호출하면 될 경우 HTTP 채널 또는 SOAP 종점 접근법을 사용해도 좋습니다.
  • .NET 원격 채널(예: TCP, 사용자 지정 채널). TCP 채널을 사용하려면 소켓에서 프로세스에 대해 수신 대기해야 합니다. 일반적으로 사용자 지정 프로세스는 소켓에서 수신 대기한 다음 서비스되는 구성 요소를 COM+ 라이브러리 또는 서버 응용 프로그램으로 호스트하는 데 사용됩니다. Dllhost를 수신기로 사용할 수도 있습니다. 두 접근법 모두 증명된 성능, 확장성 및 보안을 갖는 사용자 지정 소켓 수신기를 작성해야 합니다. 따라서, ASP.NET 또는 DCOM 솔루션은 대부분의 프로젝트에 가장 알맞은 접근법입니다.

서비스되는 구성 요소를 DCOM을 사용하여 원격으로 액세스하여 Dllhost에 호스트하려면 먼저 어셈블리가 COM+ 서버 응용 프로그램에 등록되고 서버 시스템의 GAC에 배치되는지 확인합니다. 그런 다음 COM+ 응용 프로그램 내보내기 기능을 사용하여 응용 프로그램 프록시에 대한 MSI 파일을 만듭니다. 클라이언트에 응용 프로그램 프록시를 설치합니다. 관리되는 어셈블리가 응용 프로그램 프록시에 포함됩니다. 설치 관리자가 어셈블리를 등록하고 클라이언트 시스템의 GAC에 배치합니다. 따라서:

  • .NET Framework는 클라이언트와 서버에 설치되어야 합니다. 관리되지 않는 클라이언트만 원격 서비스되는 구성 요소에 액세스하는 경우에도 클라이언트 시스템에 설치되어야 합니다. Windows 2000 플랫폼에서는 서비스 팩 3도 필요합니다.
  • 프록시를 제거하면 해당 어셈블리도 GAC에서 제거되어야 합니다.

그림 6에서는 서버 구성 요소가 클라이언트쪽에서 관리되는 코드로 활성화된 후의 인프라를 보여 줍니다.

DCOM을 사용하는 것은 CLR이 Dllhost에 호스트된다는 의미를 함축하고 있으며, 응용 프로그램 구성 파일인 dllhost.exe.config가 system32 디렉터리에 있다는 의미입니다. 또한 구성 파일이 시스템에 있는 모든 Dllhost 프로세스에 적용됨을 의미합니다. 다음에 릴리스되는 .NET Framework(V1.1)에서는 COM+ 응용 프로그램 루트 디렉터리를 COM+ 응용 프로그램에 설정하여 해당 응용 프로그램의 구성 파일과 어셈블리를 찾는 데 사용할 수 있습니다.

클라이언트 활성 개체의 경우 개체의 URI가 요청될 때마다 해당 개체에 대한 수명 임대가 만들어집니다. 활성화 절에서 이미 설명한 것처럼 URI는 원격 서비스되는 구성 요소 프록시에 의해 요청됩니다. 이것은 기존의 in-process 서비스되는 구성 요소가 원격 프로세스에 마샬링되는 경우에도 발생할 수 있습니다. URI는 응용 프로그램 도메인 외부에서 .NET가 MBR 개체를 마샬링할 때마다 요청됩니다. URI는 .NET에 있는 개체 ID가 고유함을 보장하고 프록시 체인을 금지하는 데 사용됩니다. 따라서, 관리되는 클라이언트가 원격 서비스되는 구성 요소를 활성화하면 서버 개체에 임대 시간이 사용됩니다. 관리되지 않는 클라이언트는 클라이언트쪽에 원격 서비스되는 구성 요소 프록시가 없기 때문에 개체의 URI를 요청하지 않는다는 사실에 주의하십시오. 대신에 관리되지 않는 클라이언트는 DCOM을 사용하여 개체 ID를 확인합니다. 따라서, 관리되지 않는 클라이언트에서 활성화될 경우에는 서비스되는 구성 요소의 임대 시간이 사용되지 않습니다.

임대 시간이 서비스되는 구성 요소에 포함될 경우 InitialLeaseTime 및 RenewOnCallTime 시간 초과 값을 작은 값(예: 10초)으로 설정하는 것이 좋습니다. 서비스되는 구성 요소를 삭제하려면 Dispose()를 사용하거나 GC에서 개체를 정리합니다. Dispose()가 호출되면 원격 서비스되는 구성 요소 프록시는 DCOM 프록시에 있는 참조를 해제한 후 다음 GC에서 사용 가능하게 만듭니다. 서버 개체는 Dispose 호출을 처리하고 (또는 원격 호출을 Dispose()에 서비스하는 새로운 서버 개체를 만듬), 관련된 COM+ 컨텍스트를 삭제한 다음 임대 시간이 초과된 경우에만 다음 GC에서 사용 가능하게 만듭니다. 클라이언트가 Dispose()를 호출하지 않을 경우 서버는 클라이언트쪽 GC가 DCOM 프록시에 대한 참조를 해제하는 동안 대기한 다음 임대 시간이 만료된 후 다음 GC에서 해당 서버와 COM+ 컨텍스트를 사용할 수 있게 만듭니다. 따라서, Dispose()를 호출하고 기본 임대 시간을 축소합니다. 클라이언트가 활성 상태인 경우에는 임대 시간이 만료되더라도 해당 서버 개체에 대한 DCOM 참조는 서버 개체를 활성 상태로 유지합니다. 그러나, DCOM 참조를 사용해도 서비스되는 구성 요소가 항상 활성 상태를 유지하는 것은 아닙니다. 클라이언트가 CLR 원격 채널 또는 COM+ SOAP 서비스를 통해 개체에 액세스할 경우 임대가 만료되는 강력한 참조만 서비스되는 구성 요소를 활성 상태로 유지합니다.

결론

이 기사에서는 관리되는 코드에 사용할 수 있는 서비스 중 일부에 대해서만 설명했습니다. 관리되는 코드에서는 트랜잭션 격리 수준, 프로세스 초기화, 구성 요소 없는 서비스, 프로세스 재활용 등과 같은 모든 COM+ 서비스를 사용할 수 있습니다. 이제 .NET Framework에서도 모든 COM+ 서비스를 일관성 있고 논리적인 방법으로 액세스할 수 있습니다. 게다가, ASP.NET, Microsoft ADO.NET 및 Messaging과 같은 .NET Framework의 많은 혁신적인 부분들이 .NET Enterprise Services에 통합되어 트랜잭션 및 개체 풀링과 같은 서비스를 사용할 수 있게 되었습니다. 이 통합은 일관성 있는 아키텍처 및 프로그래밍 모델을 위해 제공됩니다. System.EnterpriseServices 네임스페이스는 관리되는 클래스에 서비스를 추가할 수 있는 프로그래밍 모델을 제공됩니다.