연습: 호스트와 추가 기능 간의 컬렉션 전달
업데이트: 2008년 7월
이 연습에서는 추가 기능과 호스트 간에 사용자 지정 개체 컬렉션을 전달하는 파이프라인을 만드는 방법에 대해 설명합니다. 컬렉션의 형식이 serialize 가능한 형식이 아니므로 뷰에서 계약으로 변환하는 어댑터 및 계약에서 뷰로 변환하는 어댑터를 정의하는 추가 클래스를 어댑터 세그먼트에 추가하여 형식 흐름이 격리 경계를 지날 수 있도록 해야 합니다.
이 시나리오에서 추가 기능은 호스트의 서적 개체 컬렉션을 업데이트합니다. 각 서적 개체에는 서적의 제목, 발행자, 가격 및 기타 데이터를 가져오고 설정하는 메서드가 들어 있습니다.
데모를 위해 호스트에서 서적 컬렉션을 만들고, 추가 기능은 모든 컴퓨터 서적의 가격을 20% 할인하고 모든 공포 서적을 컬렉션에서 제거합니다. 그런 다음 추가 기능은 베스트셀러 서적에 대한 새 서적 개체를 만들어 단일 개체로 호스트에 전달합니다.
이 연습에서는 다음 작업을 수행합니다.
Visual Studio 솔루션 만들기
파이프라인 디렉터리 구조 만들기
격리 경계 사이에 전달해야 하는 개체에 대한 계약 및 뷰 만들기
격리 경계를 넘어 개체를 전달하는 데 필요한 추가 기능측 어댑터 및 호스트측 어댑터 만들기
호스트 만들기
추가 기능 만들기
파이프라인 배포
호스트 응용 프로그램 실행
참고: |
---|
이 연습에 제공된 일부 코드에는 불필요한 네임스페이스 참조가 포함되어 있습니다. 연습 단계에는 Visual Studio에 필요한 참조가 정확하게 반영됩니다. |
추가 코드 샘플을 얻고 추가 기능 파이프라인 개발용 도구에 대한 고객 기술을 미리 보려면 CodePlex의 Managed Extensibility and Add-In Framework 사이트를 참조하십시오.
사전 요구 사항
이 연습을 완료하려면 다음 구성 요소가 필요합니다.
Visual Studio.
books.xml 샘플 파일. 이 파일은 Sample XML File (books.xml)에서 복사할 수 있습니다.
Visual Studio 솔루션 만들기
Visual Studio의 솔루션을 사용하여 파이프라인 세그먼트의 프로젝트를 포함할 수 있습니다.
파이프라인 솔루션을 만들려면
Visual Studio에서 LibraryContracts라는 새 프로젝트를 만듭니다. 클래스 라이브러리 템플릿을 기반으로 합니다.
솔루션 이름을 BooksPipeline으로 지정합니다.
파이프라인 디렉터리 구조 만들기
추가 기능 모델에서는 파이프라인 세그먼트 어셈블리를 지정한 디렉터리 구조에 넣어야 합니다.
파이프라인 디렉터리 구조를 만들려면
컴퓨터에 다음 폴더 구조를 만듭니다. Visual Studio 솔루션의 폴더 안을 비롯하여 아무 곳에나 이 폴더 구조를 배치할 수 있습니다.
Pipeline AddIns BooksAddIn AddInSideAdapters AddInViews Contracts HostSideAdapters
루트 폴더 이름과 개별 추가 기능 폴더의 이름을 제외한 모든 폴더 이름을 여기에 나와 있는 대로 정확하게 지정해야 합니다. 이 예제에서는 Pipeline을 루트 폴더 이름으로 사용하고 BooksAddIn을 추가 기능 폴더 이름으로 사용합니다.
참고: 이 연습에서는 편의를 위해 호스트 응용 프로그램을 파이프라인 루트 폴더에 배치합니다. 또한 호스트 응용 프로그램이 다른 위치에 포함되어 있는 경우 코드를 변경하는 방법을 설명하는 단계를 제공합니다.
파이프라인 폴더 구조에 대한 자세한 내용은 파이프라인 개발 요구 사항을 참조하십시오.
계약 및 뷰 만들기
이 파이프라인의 계약 세그먼트에서는 다음 두 개의 인터페이스를 정의합니다.
IBookInfoContract 인터페이스
이 인터페이스에는 서적에 대한 정보를 포함하는 Author 등의 메서드가 들어 있습니다.
ILibraryManagerContract 인터페이스
이 인터페이스에는 추가 기능이 서적 컬렉션을 처리하는 데 사용하는 ProcessBooks 메서드가 들어 있습니다. 각 서적은 IBookInfoContract 계약을 나타냅니다. 또한 베스트셀러 서적을 나타내는 서적 개체를 호스트에 제공하기 위해 추가 기능에서 사용하는 GetBestSeller 메서드도 이 인터페이스에 포함되어 있습니다.
계약을 만들려면
BooksPipeline이라는 Visual Studio 솔루션에서 LibraryContracts 프로젝트를 엽니다.
Visual Basic에서는 LibraryContracts 프로젝트의 속성을 연 다음 응용 프로그램 탭을 사용하여 루트 네임스페이스에 지정된 기본값을 삭제합니다. 기본적으로 루트 네임스페이스는 프로젝트 이름으로 설정되어 있습니다.
솔루션 탐색기에서 다음 어셈블리에 대한 참조를 프로젝트에 추가합니다.
Sytem.AddIn.Contract.dll
System.AddIn.dll
클래스 파일에서 System.AddIn.Contract 및 System.AddIn.Pipeline 네임스페이스에 대한 참조를 추가합니다.
클래스 파일에서 기본 클래스 선언을 다음과 같은 두 인터페이스로 바꿉니다.
ILibraryManagerContract 인터페이스. 추가 기능을 활성화하는 데 사용되므로 AddInContractAttribute 특성을 포함해야 합니다.
IBookInfoContract 인터페이스. 호스트와 추가 기능 간에 전달되는 개체를 나타내므로 특성이 필요하지 않습니다.
두 인터페이스 모두 IContract 인터페이스를 상속해야 합니다.
다음 코드를 사용하여 IBookInfoContract 및 ILibraryManagerContract 인터페이스를 추가합니다.
Imports Microsoft.VisualBasic Imports System Imports System.Collections.Generic Imports System.Text Imports System.AddIn.Pipeline Imports System.AddIn.Contract Namespace Library <AddInContract> _ Public Interface ILibraryManagerContract Inherits IContract ' Pass a collection of books, ' of type IBookInfoContract ' to the add-in for processing. Sub ProcessBooks(ByVal books As IListContract(Of IBookInfoContract)) ' Get a IBookInfoContract object ' from the add-in of the ' the best selling book. Function GetBestSeller() As IBookInfoContract ' This method has has arbitrary ' uses and shows how you can ' mix serializable and custom types. Function Data(ByVal txt As String) As String End Interface ' Contains infomration about a book. Public Interface IBookInfoContract Inherits IContract Function ID() As String Function Author() As String Function Title() As String Function Genre() As String Function Price() As String Function Publish_Date() As String Function Description() As String End Interface End Namespace
using System; using System.Collections.Generic; using System.Text; using System.AddIn.Pipeline; using System.AddIn.Contract; namespace Library { [AddInContract] public interface ILibraryManagerContract : IContract { // Pass a collection of books, // of type IBookInfoContract // to the add-in for processing. void ProcessBooks(IListContract<IBookInfoContract> books); // Get a IBookInfoContract object // from the add-in of the // the best selling book. IBookInfoContract GetBestSeller(); // This method has has arbitrary // uses and shows how you can // mix serializable and custom types. string Data(string txt); } // Contains infomration about a book. public interface IBookInfoContract : IContract { string ID(); string Author(); string Title(); string Genre(); string Price(); string Publish_Date(); string Description(); } }
추가 기능 뷰와 호스트 뷰의 코드가 같으므로 간단한 방법으로 동시에 뷰를 만들 수 있습니다. 단, 파이프라인의 이 세그먼트를 활성화하는 데 사용되는 추가 기능 뷰에는 AddInBaseAttribute 특성이 필요하고 호스트 뷰에는 특성이 필요하지 않다는 차이점이 있습니다.
이 파이프라인의 추가 기능 뷰에는 두 개의 추상 클래스가 있습니다. BookInfo 클래스는 IBookInfoContract 인터페이스에 대한 뷰를 제공하고 LibraryManager 클래스는 ILibraryManagerContract 인터페이스에 대한 뷰를 제공합니다.
추가 기능 뷰를 만들려면
AddInViews라는 새 프로젝트를 BooksPipeline 솔루션에 추가합니다. 클래스 라이브러리 템플릿을 기반으로 합니다.
Visual Basic에서는 프로젝트의 속성을 연 다음 응용 프로그램 탭을 사용하여 루트 네임스페이스에 지정된 기본값을 삭제합니다.
솔루션 탐색기에서 System.AddIn.dll에 대한 참조를 AddInViews 프로젝트에 추가합니다.
프로젝트의 기본 클래스인 LibraryManager의 이름을 바꾸고 클래스를 abstract(Visual Basic의 경우 MustInherit)로 설정합니다.
클래스 파일에서 System.AddIn.Pipeline 네임스페이스에 대한 참조를 추가합니다.
LibraryManager 클래스는 파이프라인을 활성화하는 데 사용되므로 AddInBaseAttribute 특성을 적용해야 합니다.
다음 코드를 사용하여 LibraryManager 추상 클래스를 완료합니다.
Imports Microsoft.VisualBasic Imports System Imports System.Collections.Generic Imports System.AddIn.Pipeline Namespace LibraryContractsBase ' The AddInBaseAttribute ' identifes this pipeline ' segment as an add-in view. <AddInBase> _ Public MustInherit Class LibraryManager Public MustOverride Sub ProcessBooks(ByVal books As IList(Of BookInfo)) Public MustOverride Function GetBestSeller() As BookInfo Public MustOverride Function Data(ByVal txt As String) As String End Class End Namespace
using System; using System.Collections.Generic; using System.AddIn.Pipeline; namespace LibraryContractsBase { // The AddInBaseAttribute // identifes this pipeline // segment as an add-in view. [AddInBase] public abstract class LibraryManager { public abstract void ProcessBooks(IList<BookInfo> books); public abstract BookInfo GetBestSeller(); public abstract string Data(string txt); } }
abstract 클래스(Visual Basic의 경우 MustInherit 클래스)를 프로젝트에 추가하고 클래스 이름을 BookInfo로 지정합니다. BookInfo 클래스는 호스트와 추가 기능 간에 전달되는 개체를 나타냅니다. 이 클래스는 파이프라인을 활성화하는 데 사용되지 않으므로 특성이 필요하지 않습니다.
다음 코드를 사용하여 BookInfo 추상 클래스를 완료합니다.
Imports Microsoft.VisualBasic Imports System Namespace LibraryContractsBase Public MustInherit Class BookInfo Public MustOverride Function ID() As String Public MustOverride Function Author() As String Public MustOverride Function Title() As String Public MustOverride Function Genre() As String Public MustOverride Function Price() As String Public MustOverride Function Publish_Date() As String Public MustOverride Function Description() As String End Class End Namespace
using System; namespace LibraryContractsBase { public abstract class BookInfo { public abstract string ID(); public abstract string Author(); public abstract string Title(); public abstract string Genre(); public abstract string Price(); public abstract string Publish_Date(); public abstract string Description(); } }
호스트 뷰를 만들려면
HostViews라는 새 프로젝트를 BooksPipeline 솔루션에 추가합니다. 클래스 라이브러리 템플릿을 기반으로 합니다.
Visual Basic에서는 프로젝트의 속성을 연 다음 응용 프로그램 탭을 사용하여 루트 네임스페이스에 지정된 기본값을 삭제합니다.
프로젝트의 기본 클래스인 LibraryManager의 이름을 바꾸고 클래스를 abstract(Visual Basic의 경우 MustInherit)로 설정합니다.
다음 코드를 사용하여 LibraryManager 추상 클래스를 완료합니다.
Imports Microsoft.VisualBasic Imports System.Collections.Generic Namespace LibraryContractsHAV Public MustInherit Class LibraryManager Public MustOverride Sub ProcessBooks(ByVal books As System.Collections.Generic.IList(Of BookInfo)) Public MustOverride Function GetBestSeller() As BookInfo Public MustOverride Function Data(ByVal txt As String) As String End Class End Namespace
using System.Collections.Generic; namespace LibraryContractsHAV { public abstract class LibraryManager { public abstract void ProcessBooks(System.Collections.Generic.IList<BookInfo> books); public abstract BookInfo GetBestSeller(); public abstract string Data(string txt); } }
abstract 클래스(Visual Basic의 경우 MustInherit 클래스)를 프로젝트에 추가하고 클래스 이름을 BookInfo로 지정합니다.
다음 코드를 사용하여 BookInfo 추상 클래스를 완료합니다.
Imports Microsoft.VisualBasic Imports System Namespace LibraryContractsHAV Public MustInherit Class BookInfo Public MustOverride Function ID() As String Public MustOverride Function Author() As String Public MustOverride Function Title() As String Public MustOverride Function Genre() As String Public MustOverride Function Price() As String Public MustOverride Function Publish_Date() As String Public MustOverride Function Description() As String End Class End Namespace
namespace LibraryContractsHAV { public abstract class BookInfo { public abstract string ID(); public abstract string Author(); public abstract string Title(); public abstract string Genre(); public abstract string Price(); public abstract string Publish_Date(); public abstract string Description(); } }
추가 기능측 어댑터 만들기
이 파이프라인에 대한 추가 기능측 어댑터 어셈블리에는 다음 네 가지 어댑터 클래스가 들어 있습니다.
BookInfoContractToViewAddInAdapter
이 어댑터는 호스트가 BookInfo 개체를 개별적으로 또는 컬렉션의 일부로 추가 기능에 전달할 때 호출됩니다. 이 클래스는 BookInfo 개체의 계약을 뷰로 변환합니다. 클래스는 추가 기능 뷰에서 상속되며 해당 클래스의 생성자로 전달되는 계약을 호출하여 뷰의 추상 메서드를 구현합니다.
이 어댑터의 생성자는 계약을 받으므로 ContractHandle 개체를 계약에 적용하여 수명 관리를 구현할 수 있습니다.
중요: ContractHandle은 수명 관리에 필수적입니다. ContractHandle 개체에 대한 참조를 유지하지 못하는 경우 가비지 수집을 통해 이를 회수하게 되며 프로그램에서 더 이상 필요로 하지 않는 파이프라인은 종료됩니다. 이로 인해 AppDomainUnloadedException 같이 진단하기 어려운 오류가 발생할 수 있습니다. 종료는 파이프라인 수명의 정상적인 단계이므로 수명 관리 코드에서 이 상태가 오류인지 여부를 감지할 수는 없습니다.
BookInfoViewToContractAddInAdapter
이 어댑터는 추가 기능이 BookInfo 개체를 호스트로 전달할 때 호출됩니다. 이 클래스는 BookInfo 개체의 추가 기능 뷰를 계약으로 변환합니다. 클래스는 계약에서 상속되며 해당 클래스의 생성자로 전달되는 추가 기능 뷰를 호출하여 계약을 구현합니다. 이 어댑터는 호스트에 계약으로 마샬링됩니다.
LibraryManagerViewToContractAddInAdapter
추가 기능을 활성화하는 호출에서 호스트에 반환되는 형식입니다. 이 형식은 호스트 개체 컬렉션(IList<BookInfo>)을 추가 기능으로 전달하는 호출을 비롯하여 호스트가 추가 기능을 호출할 때 호출됩니다. 이 클래스는 ILibraryManagerContract 계약을 호스트 뷰 LibraryManager로 변환합니다. 이 클래스는 호스트 뷰에서 상속되며 해당 생성자에 전달되는 뷰를 호출하여 계약을 구현합니다.
사용자 지정 형식 컬렉션인 BookInfo 개체가 격리 경계를 넘어서 마샬링되어야 하므로 이 어댑터는 CollectionAdapters 클래스를 사용합니다. 이 클래스는 IList<T> 컬렉션을 IListContract<T> 컬렉션으로 변환하는 메서드를 제공합니다. 그러면 격리 경계를 넘어서 컬렉션을 파이프라인 반대쪽으로 전달할 수 있습니다.
BookInfoAddInAdapter
LibraryManagerViewToContractAddInAdapter 클래스에서 이 어댑터의 static 메서드(Visual Basic의 경우 Shared 메서드)를 호출하여 계약 또는 뷰를 적절하게 변경하거나 기존 계약 또는 뷰를 반환합니다. 이렇게 하면 개체가 호스트와 추가 기능 사이에서 라운드트립할 때 어댑터가 추가로 만들어지지 않습니다.
추가 기능측 어댑터를 만들려면
AddInSideAdapters라는 새 프로젝트를 BooksPipeline 솔루션에 추가합니다. 클래스 라이브러리 템플릿을 기반으로 합니다.
Visual Basic에서는 프로젝트의 속성을 연 다음 응용 프로그램 탭을 사용하여 루트 네임스페이스에 지정된 기본값을 삭제합니다.
솔루션 탐색기에서 다음 어셈블리에 대한 참조를 AddInSideAdapters 프로젝트에 추가합니다.
System.AddIn.dll
System.AddIn.Contract.dll
솔루션 탐색기에서 다음 프로젝트에 대한 참조를 AddInSideAdapters 프로젝트에 추가합니다.
AddInViews
LibraryContracts
이러한 참조에 대해 참조 속성에서 로컬 복사를 False로 설정하여 참조된 어셈블리가 로컬 빌드 폴더에 복사되지 않게 합니다. 어셈블리는 이 연습의 뒷부분에 있는 "파이프라인 배포" 절차의 설명과 같이 파이프라인 디렉터리 구조에 배치됩니다.
클래스 파일의 이름을 BookInfoContractToViewAddInAdapter로 지정합니다.
클래스 파일에서 System.AddIn.Pipeline 네임스페이스에 대한 참조를 추가합니다.
다음 코드를 사용하여 BookInfoContractToViewAddInAdapter 클래스를 추가합니다. 이 클래스는 파이프라인을 활성화하는 데 사용되지 않으므로 특성이 필요 없습니다. internal(Visual Basic의 경우 Friend) GetSourceContract 메서드는 개체가 호스트와 추가 기능 사이에서 라운드트립할 때 어댑터가 추가로 만들어지지 않도록 하기 위해 BookInfoAddInAdapter 클래스에서 사용됩니다.
Imports Microsoft.VisualBasic Imports System Imports System.AddIn.Pipeline Namespace LibraryContractsAddInAdapters Public Class BookInfoContractToViewAddInAdapter Inherits LibraryContractsBase.BookInfo Private _contract As Library.IBookInfoContract Private _handle As System.AddIn.Pipeline.ContractHandle Public Sub New(ByVal contract As Library.IBookInfoContract) _contract = contract _handle = New ContractHandle(contract) End Sub Public Overrides Function ID() As String Return _contract.ID() End Function Public Overrides Function Author() As String Return _contract.Author() End Function Public Overrides Function Title() As String Return _contract.Title() End Function Public Overrides Function Genre() As String Return _contract.Genre() End Function Public Overrides Function Price() As String Return _contract.Price() End Function Public Overrides Function Publish_Date() As String Return _contract.Publish_Date() End Function Public Overrides Function Description() As String Return _contract.Description() End Function Friend Function GetSourceContract() As Library.IBookInfoContract Return _contract End Function End Class End Namespace
using System; using System.AddIn.Pipeline; namespace LibraryContractsAddInAdapters { public class BookInfoContractToViewAddInAdapter : LibraryContractsBase.BookInfo { private Library.IBookInfoContract _contract; private System.AddIn.Pipeline.ContractHandle _handle; public BookInfoContractToViewAddInAdapter(Library.IBookInfoContract contract) { _contract = contract; _handle = new ContractHandle(contract); } public override string ID() { return _contract.ID(); } public override string Author() { return _contract.Author(); } public override string Title() { return _contract.Title(); } public override string Genre() { return _contract.Genre(); } public override string Price() { return _contract.Price(); } public override string Publish_Date() { return _contract.Publish_Date(); } public override string Description() { return _contract.Description(); } internal Library.IBookInfoContract GetSourceContract() { return _contract; } } }
다음 코드를 사용하여 BookInfoViewToContractAddInAdapter 클래스를 AddInSideAdapters 프로젝트에 추가합니다. 이 클래스는 파이프라인을 활성화하는 데 사용되지 않으므로 특성이 필요 없습니다. internal(Visual Basic의 경우 Friend) GetSourceView 메서드는 개체가 호스트와 추가 기능 사이에서 라운드트립할 때 어댑터가 추가로 만들어지지 않도록 하기 위해 BookInfoAddInAdapter 클래스에서 사용됩니다.
Imports Microsoft.VisualBasic Imports System Namespace LibraryContractsAddInAdapters Public Class BookInfoViewToContractAddInAdapter Inherits System.AddIn.Pipeline.ContractBase Implements Library.IBookInfoContract Private _view As LibraryContractsBase.BookInfo Public Sub New(ByVal view As LibraryContractsBase.BookInfo) _view = view End Sub Public Overridable Function ID() As String Implements Library.IBookInfoContract.ID Return _view.ID() End Function Public Overridable Function Author() As String Implements Library.IBookInfoContract.Author Return _view.Author() End Function Public Overridable Function Title() As String Implements Library.IBookInfoContract.Title Return _view.Title() End Function Public Overridable Function Genre() As String Implements Library.IBookInfoContract.Genre Return _view.Genre() End Function Public Overridable Function Price() As String Implements Library.IBookInfoContract.Price Return _view.Price() End Function Public Overridable Function Publish_Date() As String Implements Library.IBookInfoContract.Publish_Date Return _view.Publish_Date() End Function Public Overridable Function Description() As String Implements Library.IBookInfoContract.Description Return _view.Description() End Function Friend Function GetSourceView() As LibraryContractsBase.BookInfo Return _view End Function End Class End Namespace
using System; namespace LibraryContractsAddInAdapters { public class BookInfoViewToContractAddInAdapter : System.AddIn.Pipeline.ContractBase, Library.IBookInfoContract { private LibraryContractsBase.BookInfo _view; public BookInfoViewToContractAddInAdapter(LibraryContractsBase.BookInfo view) { _view = view; } public virtual string ID() { return _view.ID(); } public virtual string Author() { return _view.Author(); } public virtual string Title() { return _view.Title(); } public virtual string Genre() { return _view.Genre(); } public virtual string Price() { return _view.Price(); } public virtual string Publish_Date() { return _view.Publish_Date(); } public virtual string Description() { return _view.Description(); } internal LibraryContractsBase.BookInfo GetSourceView() { return _view; } } }
다음 코드를 사용하여 LibraryManagerViewToContractAddInAdapter 클래스를 AddInSideAdapters 프로젝트에 추가합니다. 이 클래스는 파이프라인을 활성화하는 데 사용되므로 AddInAdapterAttribute 특성이 필요합니다.
ProcessBooks 메서드는 격리 경계를 넘어 서적 목록을 전달하는 방법을 보여 줍니다. 이 메서드는 CollectionAdapters.ToIList 메서드를 사용하여 목록을 변환합니다. 또한 목록의 개체를 변환하기 위해 BookInfoAddInAdapter 클래스에서 제공하는 어댑터 메서드의 대리자를 전달합니다.
GetBestSeller 메서드는 격리 경계를 넘어 단일 BookInfo 개체를 전달하는 방법을 보여 줍니다.
Imports Microsoft.VisualBasic Imports System.AddIn.Pipeline Imports System.AddIn.Contract Imports System.Collections.Generic Namespace LibraryContractsAddInAdapters ' The AddInAdapterAttribute ' identifes this pipeline ' segment as an add-in-side adapter. <AddInAdapter> _ Public Class LibraryManagerViewToContractAddInAdapter Inherits System.AddIn.Pipeline.ContractBase Implements Library.ILibraryManagerContract Private _view As LibraryContractsBase.LibraryManager Public Sub New(ByVal view As LibraryContractsBase.LibraryManager) _view = view End Sub Public Overridable Sub ProcessBooks(ByVal books As IListContract(Of Library.IBookInfoContract)) Implements Library.ILibraryManagerContract.ProcessBooks _view.ProcessBooks(CollectionAdapters.ToIList(Of Library.IBookInfoContract, _ LibraryContractsBase.BookInfo)(books, _ AddressOf LibraryContractsAddInAdapters.BookInfoAddInAdapter.ContractToViewAdapter, _ AddressOf LibraryContractsAddInAdapters.BookInfoAddInAdapter.ViewToContractAdapter)) End Sub Public Overridable Function GetBestSeller() As Library.IBookInfoContract Implements Library.ILibraryManagerContract.GetBestSeller Return BookInfoAddInAdapter.ViewToContractAdapter(_view.GetBestSeller()) End Function Public Overridable Function Data(ByVal txt As String) As String Implements Library.ILibraryManagerContract.Data Dim rtxt As String = _view.Data(txt) Return rtxt End Function Friend Function GetSourceView() As LibraryContractsBase.LibraryManager Return _view End Function End Class End Namespace
using System.IO; using System.AddIn.Pipeline; using System.AddIn.Contract; using System.Collections.Generic; namespace LibraryContractsAddInAdapters { // The AddInAdapterAttribute // identifes this pipeline // segment as an add-in-side adapter. [AddInAdapter] public class LibraryManagerViewToContractAddInAdapter : System.AddIn.Pipeline.ContractBase, Library.ILibraryManagerContract { private LibraryContractsBase.LibraryManager _view; public LibraryManagerViewToContractAddInAdapter(LibraryContractsBase.LibraryManager view) { _view = view; } public virtual void ProcessBooks(IListContract<Library.IBookInfoContract> books) { _view.ProcessBooks(CollectionAdapters.ToIList<Library.IBookInfoContract, LibraryContractsBase.BookInfo>(books, LibraryContractsAddInAdapters.BookInfoAddInAdapter.ContractToViewAdapter, LibraryContractsAddInAdapters.BookInfoAddInAdapter.ViewToContractAdapter)); } public virtual Library.IBookInfoContract GetBestSeller() { return BookInfoAddInAdapter.ViewToContractAdapter(_view.GetBestSeller()); } public virtual string Data(string txt) { string rtxt = _view.Data(txt); return rtxt; } internal LibraryContractsBase.LibraryManager GetSourceView() { return _view; } } }
다음 코드를 사용하여 BookInfoAddInAdapter 클래스를 AddInSideAdapters 프로젝트에 추가합니다. 이 클래스에는 ContractToViewAdapter와 ViewToContractAdapter라는 두 개의 static 메서드(Visual Basic의 경우 Shared 메서드)가 포함되어 있습니다. 이러한 메서드는 다른 어댑터 클래스에서만 사용되므로 internal(Visual Basic의 경우 Friend)입니다. 이러한 메서드는 개체가 호스트와 추가 기능 사이에서 라운드트립할 때 어댑터가 추가로 만들어지지 않게 하는 데 사용됩니다. 격리 경계를 넘어 개체를 전달하는 어댑터에 이러한 메서드를 제공해야 합니다.
Imports Microsoft.VisualBasic Imports System Namespace LibraryContractsAddInAdapters Public Class BookInfoAddInAdapter Friend Shared Function ContractToViewAdapter(ByVal contract As Library.IBookInfoContract) As LibraryContractsBase.BookInfo If (Not System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(contract)) AndAlso _ CType(contract, Object).GetType().Equals(GetType(BookInfoViewToContractAddInAdapter)) Then Return (CType(contract, BookInfoViewToContractAddInAdapter)).GetSourceView() Else Return New BookInfoContractToViewAddInAdapter(contract) End If End Function Friend Shared Function ViewToContractAdapter(ByVal view As LibraryContractsBase.BookInfo) As Library.IBookInfoContract If (Not System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(view)) AndAlso _ view.GetType().Equals(GetType(BookInfoContractToViewAddInAdapter)) Then Return (CType(view, BookInfoContractToViewAddInAdapter)).GetSourceContract() Else Return New BookInfoViewToContractAddInAdapter(view) End If End Function End Class End Namespace
using System; namespace LibraryContractsAddInAdapters { public class BookInfoAddInAdapter { internal static LibraryContractsBase.BookInfo ContractToViewAdapter(Library.IBookInfoContract contract) { if (!System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(contract) && (contract.GetType().Equals(typeof(BookInfoViewToContractAddInAdapter)))) { return ((BookInfoViewToContractAddInAdapter)(contract)).GetSourceView(); } else { return new BookInfoContractToViewAddInAdapter(contract); } } internal static Library.IBookInfoContract ViewToContractAdapter(LibraryContractsBase.BookInfo view) { if (!System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(view) && (view.GetType().Equals(typeof(BookInfoContractToViewAddInAdapter)))) { return ((BookInfoContractToViewAddInAdapter)(view)).GetSourceContract(); } else { return new BookInfoViewToContractAddInAdapter(view); } } } }
호스트측 어댑터 만들기
이 파이프라인에 대한 호스트측 어댑터 어셈블리에는 다음 네 가지 어댑터 클래스가 들어 있습니다.
BookInfoContractToViewHostAdapter
이 어댑터는 추가 기능이 BookInfo 개체를 개별적으로 또는 컬렉션의 일부로 호스트에 전달할 때 호출됩니다. 이 클래스는 BookInfo 개체의 계약을 뷰로 변환합니다. 클래스는 호스트 뷰에서 상속되며 해당 클래스의 생성자로 전달되는 계약을 호출하여 뷰의 추상 메서드를 구현합니다.
이 어댑터의 생성자는 계약을 받으므로 ContractHandle 개체를 계약에 적용하여 수명 관리를 구현할 수 있습니다.
중요: ContractHandle은 수명 관리에 필수적입니다. ContractHandle 개체에 대한 참조를 유지하지 못하는 경우 가비지 수집을 통해 이를 회수하게 되며 프로그램에서 더 이상 필요로 하지 않는 파이프라인은 종료됩니다. 이로 인해 AppDomainUnloadedException 같이 진단하기 어려운 오류가 발생할 수 있습니다. 종료는 파이프라인 수명의 정상적인 단계이므로 수명 관리 코드에서 이 상태가 오류인지 여부를 감지할 수는 없습니다.
BookInfoViewToContractHostAdapter
이 어댑터는 호스트가 BookInfo 개체를 추가 기능으로 전달할 때 호출됩니다. 이 클래스는 BookInfo 개체의 호스트 뷰를 계약으로 변환합니다. 클래스는 계약에서 상속되며 해당 클래스의 생성자로 전달되는 추가 기능 뷰를 호출하여 계약을 구현합니다. 이 어댑터는 추가 기능에 계약으로 마샬링됩니다.
LibraryManagerContractToViewHostAdapter
이 어댑터는 호스트가 BookInfo 개체 컬렉션을 추가 기능으로 전달할 때 호출됩니다. 추가 기능은 이 컬렉션에 대해 ProcessBooks 메서드 구현을 수행합니다.
이 클래스는 LibraryManager 개체의 호스트 뷰를 계약으로 변환합니다. 이 클래스는 계약에서 상속되며 클래스 생성자로 전달되는 호스트 뷰를 호출하여 계약을 구현합니다.
사용자 지정 형식 컬렉션인 BookInfo 개체가 격리 경계를 넘어서 마샬링되어야 하므로 이 어댑터는 CollectionAdapters 클래스를 사용합니다. 이 클래스는 IList<T> 컬렉션을 IListContract<T> 컬렉션으로 변환하는 메서드를 제공합니다. 그러면 격리 경계를 넘어서 컬렉션을 파이프라인 반대쪽으로 전달할 수 있습니다.
BookInfoHostAdapter
이 어댑터는 LibraryManagerViewToContractHostAdapter 클래스에서 호출되어 호출에 대한 새 인스턴스를 만드는 대신 조정을 위해 기존 계약 또는 뷰를 반환합니다. 이렇게 하면 개체가 호스트와 추가 기능 사이에서 라운드트립할 때 어댑터가 추가로 만들어지지 않습니다.
호스트측 어댑터를 만들려면
HostSideAdapters라는 새 프로젝트를 BooksPipeline 솔루션에 추가합니다. 클래스 라이브러리 템플릿을 기반으로 합니다.
Visual Basic에서는 프로젝트의 속성을 연 다음 응용 프로그램 탭을 사용하여 루트 네임스페이스에 지정된 기본값을 삭제합니다.
솔루션 탐색기에서 다음 어셈블리에 대한 참조를 HostSideAdapters 프로젝트에 추가합니다.
System.AddIn.dll
System.AddIn.Contract.dll
솔루션 탐색기에서 다음 프로젝트에 대한 참조를 HostSideAdapters 프로젝트에 추가합니다.
HostViews
LibraryContracts
이러한 참조에 대해 참조 속성에서 로컬 복사를 False로 설정하여 참조된 어셈블리가 로컬 빌드 폴더에 복사되지 않게 합니다.
클래스 파일에서 System.AddIn.Pipeline 네임스페이스에 대한 참조를 추가합니다.
다음 코드를 사용하여 BookInfoContractToViewHostAdapter 클래스를 추가합니다. 이 클래스는 파이프라인을 활성화하는 데 사용되지 않으므로 특성이 필요 없습니다. internal(Visual Basic의 경우 Friend) GetSourceContract 메서드는 개체가 호스트와 추가 기능 사이에서 라운드트립할 때 어댑터가 추가로 만들어지지 않도록 하기 위해 BookInfoAddInAdapter 클래스에서 사용됩니다.
Imports Microsoft.VisualBasic Imports System.AddIn.Pipeline Namespace LibraryContractsHostAdapters Public Class BookInfoContractToViewHostAdapter Inherits LibraryContractsHAV.BookInfo Private _contract As Library.IBookInfoContract Private _handle As ContractHandle Public Sub New(ByVal contract As Library.IBookInfoContract) _contract = contract _handle = New ContractHandle(contract) End Sub Public Overrides Function ID() As String Return _contract.ID() End Function Public Overrides Function Author() As String Return _contract.Author() End Function Public Overrides Function Title() As String Return _contract.Title() End Function Public Overrides Function Genre() As String Return _contract.Genre() End Function Public Overrides Function Price() As String Return _contract.Price() End Function Public Overrides Function Publish_Date() As String Return _contract.Publish_Date() End Function Public Overrides Function Description() As String Return _contract.Description() End Function Friend Function GetSourceContract() As Library.IBookInfoContract Return _contract End Function End Class End Namespace
using System.AddIn.Pipeline; namespace LibraryContractsHostAdapters { public class BookInfoContractToViewHostAdapter : LibraryContractsHAV.BookInfo { private Library.IBookInfoContract _contract; private ContractHandle _handle; public BookInfoContractToViewHostAdapter(Library.IBookInfoContract contract) { _contract = contract; _handle = new ContractHandle(contract); } public override string ID() { return _contract.ID(); } public override string Author() { return _contract.Author(); } public override string Title() { return _contract.Title(); } public override string Genre() { return _contract.Genre(); } public override string Price() { return _contract.Price(); } public override string Publish_Date() { return _contract.Publish_Date(); } public override string Description() { return _contract.Description(); } internal Library.IBookInfoContract GetSourceContract() { return _contract; } } }
다음 코드를 사용하여 BookInfoViewToContractHostAdapter를 HostSideAdapters 프로젝트에 추가합니다. 이 클래스는 파이프라인을 활성화하는 데 사용되지 않으므로 특성이 필요 없습니다. internal(Visual Basic의 경우 Friend) GetSourceView 메서드는 개체가 호스트와 추가 기능 사이에서 라운드트립할 때 어댑터가 추가로 만들어지지 않도록 하기 위해 BookInfoAddInAdapter 클래스에서 사용됩니다.
Imports Microsoft.VisualBasic Imports System.AddIn.Pipeline Namespace LibraryContractsHostAdapters Public Class BookInfoViewToContractHostAdapter Inherits ContractBase Implements Library.IBookInfoContract Private _view As LibraryContractsHAV.BookInfo Public Sub New(ByVal view As LibraryContractsHAV.BookInfo) _view = view End Sub Public Overridable Function ID() As String Implements Library.IBookInfoContract.ID Return _view.ID() End Function Public Overridable Function Author() As String Implements Library.IBookInfoContract.Author Return _view.Author() End Function Public Overridable Function Title() As String Implements Library.IBookInfoContract.Title Return _view.Title() End Function Public Overridable Function Genre() As String Implements Library.IBookInfoContract.Genre Return _view.Genre() End Function Public Overridable Function Price() As String Implements Library.IBookInfoContract.Price Return _view.Price() End Function Public Overridable Function Publish_Date() As String Implements Library.IBookInfoContract.Publish_Date Return _view.Publish_Date() End Function Public Overridable Function Description() As String Implements Library.IBookInfoContract.Description Return _view.Description() End Function Friend Function GetSourceView() As LibraryContractsHAV.BookInfo Return _view End Function End Class End Namespace
using System.AddIn.Pipeline; namespace LibraryContractsHostAdapters { public class BookInfoViewToContractHostAdapter : ContractBase, Library.IBookInfoContract { private LibraryContractsHAV.BookInfo _view; public BookInfoViewToContractHostAdapter(LibraryContractsHAV.BookInfo view) { _view = view; } public virtual string ID() { return _view.ID(); } public virtual string Author() { return _view.Author(); } public virtual string Title() { return _view.Title(); } public virtual string Genre() { return _view.Genre(); } public virtual string Price() { return _view.Price(); } public virtual string Publish_Date() { return _view.Publish_Date(); } public virtual string Description() { return _view.Description(); } internal LibraryContractsHAV.BookInfo GetSourceView() { return _view; } } }
다음 코드를 사용하여 LibraryManagerContractToViewHostAdapter를 HostSideAdapters 프로젝트에 추가합니다. 이 클래스는 파이프라인을 활성화하는 데 사용되므로 HostAdapterAttribute 특성이 필요합니다.
ProcessBooks 메서드는 격리 경계를 넘어 서적 목록을 전달하는 방법을 보여 줍니다. 이 메서드는 CollectionAdapters.ToIListContract 메서드를 사용하여 목록을 변환합니다. 또한 목록의 개체를 변환하기 위해 BookInfoHostAdapter 클래스에서 제공하는 어댑터 메서드의 대리자를 전달합니다.
GetBestSeller 메서드는 격리 경계를 넘어 단일 BookInfo 개체를 전달하는 방법을 보여 줍니다.
Imports Microsoft.VisualBasic Imports System.Collections.Generic Imports System.AddIn.Pipeline Namespace LibraryContractsHostAdapters <HostAdapterAttribute()> _ Public Class LibraryManagerContractToViewHostAdapter Inherits LibraryContractsHAV.LibraryManager Private _contract As Library.ILibraryManagerContract Private _handle As System.AddIn.Pipeline.ContractHandle Public Sub New(ByVal contract As Library.ILibraryManagerContract) _contract = contract _handle = New System.AddIn.Pipeline.ContractHandle(contract) End Sub Public Overrides Sub ProcessBooks(ByVal books As IList(Of LibraryContractsHAV.BookInfo)) _contract.ProcessBooks(CollectionAdapters.ToIListContract(Of LibraryContractsHAV.BookInfo, _ Library.IBookInfoContract)(books, _ AddressOf LibraryContractsHostAdapters.BookInfoHostAdapter.ViewToContractAdapter, _ AddressOf LibraryContractsHostAdapters.BookInfoHostAdapter.ContractToViewAdapter)) End Sub Public Overrides Function GetBestSeller() As LibraryContractsHAV.BookInfo Return BookInfoHostAdapter.ContractToViewAdapter(_contract.GetBestSeller()) End Function Friend Function GetSourceContract() As Library.ILibraryManagerContract Return _contract End Function Public Overrides Function Data(ByVal txt As String) As String Dim rtxt As String = _contract.Data(txt) Return rtxt End Function End Class End Namespace
using System.Collections.Generic; using System.AddIn.Pipeline; namespace LibraryContractsHostAdapters { [HostAdapterAttribute()] public class LibraryManagerContractToViewHostAdapter : LibraryContractsHAV.LibraryManager { private Library.ILibraryManagerContract _contract; private System.AddIn.Pipeline.ContractHandle _handle; public LibraryManagerContractToViewHostAdapter(Library.ILibraryManagerContract contract) { _contract = contract; _handle = new System.AddIn.Pipeline.ContractHandle(contract); } public override void ProcessBooks(IList<LibraryContractsHAV.BookInfo> books) { _contract.ProcessBooks(CollectionAdapters.ToIListContract<LibraryContractsHAV.BookInfo, Library.IBookInfoContract>(books, LibraryContractsHostAdapters.BookInfoHostAdapter.ViewToContractAdapter, LibraryContractsHostAdapters.BookInfoHostAdapter.ContractToViewAdapter)); } public override LibraryContractsHAV.BookInfo GetBestSeller() { return BookInfoHostAdapter.ContractToViewAdapter(_contract.GetBestSeller()); } internal Library.ILibraryManagerContract GetSourceContract() { return _contract; } public override string Data(string txt) { string rtxt = _contract.Data(txt); return rtxt; } } }
다음 코드를 사용하여 BookInfoHostAdapter 클래스를 HostSideAdapters 프로젝트에 추가합니다. 이 클래스에는 ContractToViewAdapter와 ViewToContractAdapter라는 두 개의 static 메서드(Visual Basic의 경우 Shared 메서드)가 포함되어 있습니다. 이러한 메서드는 다른 어댑터 클래스에서만 사용되므로 internal(Visual Basic의 경우 Friend)입니다. 이러한 메서드는 개체가 호스트와 추가 기능 사이에서 라운드트립할 때 어댑터가 추가로 만들어지지 않게 하는 데 사용됩니다. 격리 경계를 넘어 개체를 전달하는 어댑터에 이러한 메서드를 제공해야 합니다.
Imports Microsoft.VisualBasic Imports System Namespace LibraryContractsHostAdapters Public Class BookInfoHostAdapter Friend Shared Function ContractToViewAdapter(ByVal contract As Library.IBookInfoContract) As LibraryContractsHAV.BookInfo If Not System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(contract) AndAlso _ CType(contract, Object).GetType().Equals(GetType(BookInfoViewToContractHostAdapter)) Then Return (CType(contract, BookInfoViewToContractHostAdapter)).GetSourceView() Else Return New BookInfoContractToViewHostAdapter(contract) End If End Function Friend Shared Function ViewToContractAdapter(ByVal view As LibraryContractsHAV.BookInfo) As Library.IBookInfoContract If Not System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(view) AndAlso _ view.GetType().Equals(GetType(BookInfoContractToViewHostAdapter)) Then Return (CType(view, BookInfoContractToViewHostAdapter)).GetSourceContract() Else Return New BookInfoViewToContractHostAdapter(view) End If End Function End Class End Namespace
using System; namespace LibraryContractsHostAdapters { public class BookInfoHostAdapter { internal static LibraryContractsHAV.BookInfo ContractToViewAdapter(Library.IBookInfoContract contract) { if (!System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(contract) && (contract.GetType().Equals(typeof(BookInfoViewToContractHostAdapter)))) { return ((BookInfoViewToContractHostAdapter)(contract)).GetSourceView(); } else { return new BookInfoContractToViewHostAdapter(contract); } } internal static Library.IBookInfoContract ViewToContractAdapter(LibraryContractsHAV.BookInfo view) { if (!System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(view) && (view.GetType().Equals(typeof(BookInfoContractToViewHostAdapter)))) { return ((BookInfoContractToViewHostAdapter)(view)).GetSourceContract(); } else { return new BookInfoViewToContractHostAdapter(view); } } } }
호스트 만들기
호스트 응용 프로그램은 호스트 뷰를 통해 추가 기능과 상호 작용합니다. AddInStore 및 AddInToken 클래스에서 제공하는 추가 기능 검색 및 활성화 메서드를 사용하여 다음을 수행합니다.
파이프라인 및 추가 기능 정보의 캐시를 다시 빌드합니다.
지정한 파이프라인 루트 디렉터리에서 LibraryManager 형식의 추가 기능을 찾습니다.
사용할 추가 기능을 선택하라는 메시지를 사용자에게 표시합니다. 이 예제에서는 하나의 추가 기능만 사용할 수 있습니다.
새 응용 프로그램 도메인에서 지정된 보안 신뢰 수준으로 선택한 추가 기능을 활성화합니다.
ProcessBooks 메서드를 호출하여 BookInfo 개체 컬렉션을 추가 기능으로 전달합니다. 추가 기능은 ProcessBooks 메서드의 구현을 호출하고 컴퓨터 서적 20% 할인 등의 함수를 수행합니다.
베스트셀러 서적에 대한 정보가 포함된 BookInfo 개체를 반환하기 위해 추가 기능에서 사용하는 GetBestSeller 메서드를 호출합니다.
Data 메서드를 호출하여 추가 기능에서 현재 판매세율을 가져옵니다. 이 메서드는 serialize 가능한 봉인 참조 형식인 문자열을 사용하고 반환합니다. 따라서 뷰에서 계약으로 변환하는 어댑터 및 계약에서 뷰로 변환하는 어댑터를 사용하지 않고도 격리 경계를 넘어서 파이프라인의 반대쪽으로 메서드를 전달할 수 있습니다.
호스트에는 BookInfo 개체 컬렉션을 만드는 CreateBooks 메서드가 있습니다. 이 메서드는 Sample XML File (books.xml)에 있는 books.xml 샘플 파일을 사용하여 컬렉션을 만듭니다.
호스트를 만들려면
BookStore라는 새 프로젝트를 BooksPipeline 솔루션에 추가합니다. 콘솔 응용 프로그램 템플릿을 기반으로 합니다.
Visual Basic에서는 프로젝트의 속성을 연 다음 응용 프로그램 탭을 사용하여 루트 네임스페이스에 지정된 기본값을 삭제합니다.
솔루션 탐색기에서 System.AddIn.dll 어셈블리에 대한 참조를 BookStore 프로젝트에 추가합니다.
프로젝트 참조를 HostViews 프로젝트에 추가합니다. 이 참조에 대해 참조 속성에서 로컬 복사를 False로 설정하여 참조된 어셈블리가 로컬 빌드 폴더에 복사되지 않게 합니다.
Visual Basic에서는 다음과 같이 모듈을 클래스로 변경합니다.
프로젝트에서 기본 모듈을 제외하고 Program 클래스를 추가합니다.
Public 키워드를 Friend 키워드로 바꿉니다.
클래스에 Shared Sub Main() 프로시저를 추가합니다.
프로젝트 속성 대화 상자의 응용 프로그램 탭을 사용하여 시작 개체를 Sub Main으로 설정합니다.
클래스 파일에서 System.AddIn.Pipeline 및 호스트 뷰 세그먼트 네임스페이스에 대한 참조를 추가합니다.
솔루션 탐색기에서 솔루션을 선택하고 프로젝트 메뉴에서 속성을 선택한 다음, 솔루션 속성 페이지 대화 상자에서 한 개의 시작 프로젝트를 이 호스트 응용 프로그램 프로젝트로 설정합니다.
호스트 응용 프로그램에 대해 다음 코드를 사용합니다.
참고: books.xml 파일이 응용 프로그램 폴더에서 로드되도록 코드에서 이 파일이 로드되는 위치를 "books.xml"로 변경하십시오. 응용 프로그램을 Pipeline 폴더 이외의 다른 위치에 배치하려는 경우에는 addInRoot 변수에 파이프라인 디렉터리 구조의 경로가 포함되도록 이 변수를 설정하는 코드 줄을 변경해야 합니다.
Imports Microsoft.VisualBasic Imports System Imports System.Collections.Generic Imports System.Collections.ObjectModel Imports System.Text Imports LibraryContractsHAV Imports System.AddIn.Hosting Imports System.Xml Namespace ListAdaptersHost Friend Class Program Shared Sub Main(ByVal args As String()) ' In this example, the pipeline root is the current directory. Dim pipeRoot As String = Environment.CurrentDirectory ' Rebuild the cache of pipeline and add-in information. Dim warnings As String() = AddInStore.Update(pipeRoot) If warnings.Length > 0 Then For Each one As String In warnings Console.WriteLine(one) Next one End If ' Find add-ins of type LibraryManager under the specified pipeline root directory. Dim tokens As Collection(Of AddInToken) = AddInStore.FindAddIns(GetType(LibraryManager), pipeRoot) ' Determine which add-in to use. Dim selectedToken As AddInToken = ChooseAddIn(tokens) ' Activate the selected AddInToken in a new ' application domain with a specified security trust level. Dim manager As LibraryManager = selectedToken.Activate(Of LibraryManager)(AddInSecurityLevel.FullTrust) ' Create a collection of books. Dim books As IList(Of BookInfo) = CreateBooks() ' Show the collection count. Console.WriteLine("Number of books: {0}",books.Count.ToString()) ' Have the add-in process the books. ' The add-in will discount computer books by $20 ' and list their before and after prices. It ' will also remove all horror books. manager.ProcessBooks(books) ' List the genre of each book. There ' should be no horror books. For Each bk As BookInfo In books Console.WriteLine(bk.Genre()) Next bk Console.WriteLine("Number of books: {0}", books.Count.ToString()) Console.WriteLine() ' Have the add-in pass a BookInfo object ' of the best selling book. Dim bestBook As BookInfo = manager.GetBestSeller() Console.WriteLine("Best seller is {0} by {1}", bestBook.Title(), bestBook.Author()) ' Have the add-in show the sales tax rate. manager.Data("sales tax") Dim ctrl As AddInController = AddInController.GetAddInController(manager) ctrl.Shutdown() Console.WriteLine("Press any key to exit.") Console.ReadLine() End Sub Private Shared Function ChooseAddIn(ByVal tokens As Collection(Of AddInToken)) As AddInToken If tokens.Count = 0 Then Console.WriteLine("No add-ins of this type are available") Return Nothing End If Console.WriteLine("{0} Available add-in(s):",tokens.Count.ToString()) For i As Integer = 0 To tokens.Count - 1 ' Show AddInToken properties. Console.WriteLine("[{0}] - {1}, Publisher: {2}, Version: {3}, Description: {4}", (i + 1).ToString(), tokens(i).Name, tokens(i).Publisher, tokens(i).Version, tokens(i).Description) Next i Console.WriteLine("Select add-in by number:") Dim line As String = Console.ReadLine() Dim selection As Integer If Int32.TryParse(line, selection) Then If selection <= tokens.Count Then Return tokens(selection - 1) End If End If Console.WriteLine("Invalid selection: {0}. Please choose again.", line) Return ChooseAddIn(tokens) End Function Friend Shared Function CreateBooks() As IList(Of BookInfo) Dim books As List(Of BookInfo) = New List(Of BookInfo)() Dim ParamId As String = "" Dim ParamAuthor As String = "" Dim ParamTitle As String = "" Dim ParamGenre As String = "" Dim ParamPrice As String = "" Dim ParamPublish_Date As String = "" Dim ParamDescription As String = "" Dim xDoc As XmlDocument = New XmlDocument() xDoc.Load("c:\Books.xml") Dim xRoot As XmlNode = xDoc.DocumentElement If xRoot.Name = "catalog" Then Dim bklist As XmlNodeList = xRoot.ChildNodes For Each bk As XmlNode In bklist ParamId = bk.Attributes(0).Value Dim dataItems As XmlNodeList = bk.ChildNodes Dim items As Integer = dataItems.Count For Each di As XmlNode In dataItems Select Case di.Name Case "author" ParamAuthor = di.InnerText Case "title" ParamTitle = di.InnerText Case "genre" ParamGenre = di.InnerText Case "price" ParamPrice = di.InnerText Case "publish_date" ParamAuthor = di.InnerText Case "description" ParamDescription = di.InnerText Case Else End Select Next di books.Add(New MyBookInfo(ParamId, ParamAuthor, ParamTitle, ParamGenre, ParamPrice, ParamPublish_Date, ParamDescription)) Next bk End If Return books End Function End Class Friend Class MyBookInfo Inherits BookInfo Private _id As String Private _author As String Private _title As String Private _genre As String Private _price As String Private _publish_date As String Private _description As String Public Sub New(ByVal id As String, ByVal author As String, ByVal title As String, ByVal genre As String, ByVal price As String, ByVal publish_date As String, ByVal description As String) _id = id _author = author _title = title _genre = genre _price = price _publish_date = publish_date _description = description End Sub Public Overrides Function ID() As String Return _id End Function Public Overrides Function Title() As String Return _title End Function Public Overrides Function Author() As String Return _author End Function Public Overrides Function Genre() As String Return _genre End Function Public Overrides Function Price() As String Return _price End Function Public Overrides Function Publish_Date() As String Return _publish_date End Function Public Overrides Function Description() As String Return _description End Function End Class End Namespace
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; using LibraryContractsHAV; using System.AddIn.Hosting; using System.Xml; namespace ListAdaptersHost { class Program { static void Main(string[] args) { // In this example, the pipeline root is the current directory. String pipeRoot = Environment.CurrentDirectory; // Rebuild the cache of pipeline and add-in information. string[] warnings = AddInStore.Update(pipeRoot); if (warnings.Length > 0) { foreach (string one in warnings) { Console.WriteLine(one); } } // Find add-ins of type LibraryManager under the specified pipeline root directory. Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(LibraryManager), pipeRoot); // Determine which add-in to use. AddInToken selectedToken = ChooseAddIn(tokens); // Activate the selected AddInToken in a new // application domain with a specified security trust level. LibraryManager manager = selectedToken.Activate<LibraryManager>(AddInSecurityLevel.FullTrust); // Create a collection of books. IList<BookInfo> books = CreateBooks(); // Show the collection count. Console.WriteLine("Number of books: {0}",books.Count.ToString()); // Have the add-in process the books. // The add-in will discount computer books by $20 // and list their before and after prices. It // will also remove all horror books. manager.ProcessBooks(books); // List the genre of each book. There // should be no horror books. foreach (BookInfo bk in books) { Console.WriteLine(bk.Genre()); } Console.WriteLine("Number of books: {0}", books.Count.ToString()); Console.WriteLine(); // Have the add-in pass a BookInfo object // of the best selling book. BookInfo bestBook = manager.GetBestSeller(); Console.WriteLine("Best seller is {0} by {1}", bestBook.Title(), bestBook.Author()); // Have the add-in show the sales tax rate. manager.Data("sales tax"); AddInController ctrl = AddInController.GetAddInController(manager); ctrl.Shutdown(); Console.WriteLine("Press any key to exit."); Console.ReadLine(); } private static AddInToken ChooseAddIn(Collection<AddInToken> tokens) { if (tokens.Count == 0) { Console.WriteLine("No add-ins of this type are available"); return null; } Console.WriteLine("{0} Available add-in(s):",tokens.Count.ToString()); for (int i = 0; i < tokens.Count; i++) { // Show AddInToken properties. Console.WriteLine("[{0}] - {1}, Publisher: {2}, Version: {3}, Description: {4}", (i + 1).ToString(), tokens[i].Name, tokens[i].Publisher, tokens[i].Version, tokens[i].Description); } Console.WriteLine("Select add-in by number:"); String line = Console.ReadLine(); int selection; if (Int32.TryParse(line, out selection)) { if (selection <= tokens.Count) { return tokens[selection - 1]; } } Console.WriteLine("Invalid selection: {0}. Please choose again.", line); return ChooseAddIn(tokens); } internal static IList<BookInfo> CreateBooks() { List<BookInfo> books = new List<BookInfo>(); string ParamId = ""; string ParamAuthor = ""; string ParamTitle = ""; string ParamGenre = ""; string ParamPrice = ""; string ParamPublish_Date = ""; string ParamDescription = ""; XmlDocument xDoc = new XmlDocument(); xDoc.Load(@"c:\Books.xml"); XmlNode xRoot = xDoc.DocumentElement; if (xRoot.Name == "catalog") { XmlNodeList bklist = xRoot.ChildNodes; foreach (XmlNode bk in bklist) { ParamId = bk.Attributes[0].Value; XmlNodeList dataItems = bk.ChildNodes; int items = dataItems.Count; foreach (XmlNode di in dataItems) { switch (di.Name) { case "author": ParamAuthor = di.InnerText; break; case "title": ParamTitle = di.InnerText; break; case "genre": ParamGenre = di.InnerText; break; case "price": ParamPrice = di.InnerText; break; case "publish_date": ParamAuthor = di.InnerText; break; case "description": ParamDescription = di.InnerText; break; default: break; } } books.Add(new MyBookInfo(ParamId, ParamAuthor, ParamTitle, ParamGenre, ParamPrice, ParamPublish_Date, ParamDescription)); } } return books; } } class MyBookInfo : BookInfo { private string _id; private string _author; private string _title; private string _genre; private string _price; private string _publish_date; private string _description; public MyBookInfo(string id, string author, string title, string genre, string price, string publish_date, string description) { _id = id; _author = author; _title = title; _genre = genre; _price = price; _publish_date = publish_date; _description = description; } public override string ID() { return _id; } public override string Title() { return _title; } public override string Author() { return _author; } public override string Genre() { return _genre; } public override string Price() { return _price; } public override string Publish_Date() { return _publish_date; } public override string Description() { return _description; } } }
books.xml 데이터 파일을 만들려면
BookStore 프로젝트에 새 XML 파일을 추가합니다. 새 항목 추가 대화 상자가 나타나면 파일 이름을 books.xml로 지정합니다.
books.xml의 기본 콘텐츠를 Sample XML File (books.xml)의 XML로 바꿉니다.
솔루션 탐색기에서 books.xml을 선택하고 속성에서 출력 디렉터리로 복사를 항상 복사로 설정합니다.
추가 기능 만들기
추가 기능은 추가 기능 뷰에서 지정한 메서드를 구현합니다. 이 추가 기능은 ProcessBooks 메서드를 구현합니다. 이 메서드는 호스트에서 전달하는 BookInfo 개체 컬렉션에 대해 다음 작업을 수행합니다.
모든 컴퓨터 서적의 가격을 20% 할인합니다.
모든 공포 서적을 컬렉션에서 제거합니다.
또한 이 추가 기능은 베스트셀러 서적에 대해 설명하는 BookInfo 개체를 호스트에 전달하여 GetBestSeller 메서드를 구현합니다.
추가 기능을 만들려면
BooksAddin이라는 새 프로젝트를 BooksPipeline 솔루션에 추가합니다. 클래스 라이브러리 템플릿을 기반으로 합니다.
Visual Basic에서는 프로젝트의 속성을 연 다음 응용 프로그램 탭을 사용하여 루트 네임스페이스에 지정된 기본값을 삭제합니다.
솔루션 탐색기에서 System.AddIn.dll 어셈블리에 대한 참조를 BooksAddin 프로젝트에 추가합니다.
AddInViews 프로젝트에 프로젝트 참조를 추가합니다. 이 참조에 대해 참조 속성에서 로컬 복사를 False로 설정하여 참조된 어셈블리가 로컬 빌드 폴더에 복사되지 않게 합니다.
클래스 파일에서 System.AddIn 및 추가 기능 뷰 세그먼트 네임스페이스에 대한 참조를 추가합니다.
추가 기능 응용 프로그램에 대해 다음 코드를 사용합니다.
Imports Microsoft.VisualBasic Imports System Imports System.Collections.Generic Imports System.Text Imports LibraryContractsBase Imports System.AddIn Imports System.IO Namespace SampleAddIn <AddIn("Books AddIn",Version:="1.0.0.0")> _ Public Class BooksAddIn Inherits LibraryManager ' Calls methods that updates book data ' and removes books by genre. Public Overrides Sub ProcessBooks(ByVal books As IList(Of BookInfo)) For i As Integer = 0 To books.Count - 1 books(i) = UpdateBook(books(i)) Next i RemoveGenre("horror", books) End Sub Public Overrides Function Data(ByVal txt As String) As String ' assumes txt = "sales tax" Dim rtxt As String = txt & "= 8.5%" Return rtxt End Function Friend Shared Function RemoveGenre(ByVal genre As String, ByVal books As IList(Of BookInfo)) As IList(Of BookInfo) ' Remove all horror books from the collection. Dim i As Integer = 0 Do While i < books.Count If books(i).Genre().ToLower() = "horror" Then books.RemoveAt(i) End If i += 1 Loop Return books End Function ' Populate a BookInfo object with data ' about the best selling book. Public Overrides Function GetBestSeller() As BookInfo Dim ParamId As String = "bk999" Dim ParamAuthor As String = "Corets, Eva" Dim ParamTitle As String = "Cooking with Oberon" Dim ParamGenre As String = "Cooking" Dim ParamPrice As String = "7.95" Dim ParamPublish_Date As String = "2006-12-01" Dim ParamDescription As String = "Recipes for a post-apocalyptic society." Dim bestBook As MyBookInfo = New MyBookInfo(ParamId, ParamAuthor, ParamTitle, ParamGenre, ParamPrice, ParamPublish_Date, ParamDescription) Return bestBook End Function Friend Shared Function UpdateBook(ByVal bk As BookInfo) As BookInfo ' Discounts the price of all ' computer books by 20 percent. Dim ParamId As String = bk.ID() Dim ParamAuthor As String = bk.Author() Dim ParamTitle As String = bk.Title() Dim ParamGenre As String = bk.Genre() Dim ParamPrice As String = bk.Price() If ParamGenre.ToLower() = "computer" Then Dim oldprice As Double = Convert.ToDouble(ParamPrice) Dim newprice As Double = oldprice - (oldprice *.20) ParamPrice = newprice.ToString() If ParamPrice.IndexOf(".") = ParamPrice.Length - 4 Then ParamPrice = ParamPrice.Substring(1, ParamPrice.Length - 1) End If Console.WriteLine("{0} - Old Price: {1}, New Price: {2}",ParamTitle,oldprice.ToString(),ParamPrice) End If Dim ParamPublish_Date As String = bk.Publish_Date() Dim ParamDescription As String = bk.Description() Dim bookUpdated As BookInfo = New MyBookInfo(ParamId, ParamAuthor, ParamTitle, ParamGenre, ParamPrice, ParamPublish_Date, ParamDescription) Return bookUpdated End Function End Class ' Creates a BookInfo object. Friend Class MyBookInfo Inherits BookInfo Private _id As String Private _author As String Private _title As String Private _genre As String Private _price As String Private _publish_date As String Private _description As String Public Sub New(ByVal id As String, ByVal author As String, ByVal title As String, ByVal genre As String, ByVal price As String, ByVal publish_date As String, ByVal description As String) _id = id _author = author _title = title _genre = genre _price = price _publish_date = publish_date _description = description End Sub Public Overrides Function ID() As String Return _id End Function Public Overrides Function Title() As String Return _title End Function Public Overrides Function Author() As String Return _author End Function Public Overrides Function Genre() As String Return _genre End Function Public Overrides Function Price() As String Return _price End Function Public Overrides Function Publish_Date() As String Return _publish_date End Function Public Overrides Function Description() As String Return _description End Function End Class End Namespace
using System; using System.Collections.Generic; using System.Text; using LibraryContractsBase; using System.AddIn; using System.IO; namespace BooksAddIn { [AddIn("Books AddIn",Description="Book Store Data", Publisher="Microsoft",Version="1.0.0.0")] public class BooksAddIn : LibraryManager { // Calls methods that updates book data // and removes books by their genre. public override void ProcessBooks(IList<BookInfo> books) { for (int i = 0; i < books.Count; i++) { books[i] = UpdateBook(books[i]); } RemoveGenre("horror", books); } public override string Data(string txt) { // assumes txt = "sales tax" string rtxt = txt + "= 8.5%"; return rtxt; } internal static IList<BookInfo> RemoveGenre(string genre, IList<BookInfo> books) { // Remove all horror books from the collection. for (int i = 0; i < books.Count; i++) { if (books[i].Genre().ToLower() == "horror") books.RemoveAt(i); } return books; } // Populate a BookInfo object with data // about the best selling book. public override BookInfo GetBestSeller() { string ParamId = "bk999"; string ParamAuthor = "Corets, Eva"; string ParamTitle = "Cooking with Oberon"; string ParamGenre = "Cooking"; string ParamPrice = "7.95"; string ParamPublish_Date = "2006-12-01"; string ParamDescription = "Recipes for a post-apocalyptic society."; MyBookInfo bestBook = new MyBookInfo(ParamId, ParamAuthor, ParamTitle, ParamGenre, ParamPrice, ParamPublish_Date, ParamDescription); return bestBook; } internal static BookInfo UpdateBook(BookInfo bk) { // Discounts the price of all // computer books by 20 percent. string ParamId = bk.ID(); string ParamAuthor = bk.Author(); string ParamTitle = bk.Title(); string ParamGenre = bk.Genre(); string ParamPrice = bk.Price(); if (ParamGenre.ToLower() == "computer") { double oldprice = Convert.ToDouble(ParamPrice); double newprice = oldprice - (oldprice * .20); ParamPrice = newprice.ToString(); if (ParamPrice.IndexOf(".") == ParamPrice.Length - 4) ParamPrice = ParamPrice.Substring(1, ParamPrice.Length - 1); Console.WriteLine("{0} - Old Price: {1}, New Price: {2}",ParamTitle,oldprice.ToString(),ParamPrice); } string ParamPublish_Date = bk.Publish_Date(); string ParamDescription = bk.Description(); BookInfo bookUpdated = new MyBookInfo(ParamId, ParamAuthor, ParamTitle, ParamGenre, ParamPrice, ParamPublish_Date, ParamDescription); return bookUpdated; } } // Creates a BookInfo object. class MyBookInfo : BookInfo { private string _id; private string _author; private string _title; private string _genre; private string _price; private string _publish_date; private string _description; public MyBookInfo(string id, string author, string title, string genre, string price, string publish_date, string description) { _id = id; _author = author; _title = title; _genre = genre; _price = price; _publish_date = publish_date; _description = description; } public override string ID() { return _id; } public override string Title() { return _title; } public override string Author() { return _author; } public override string Genre() { return _genre; } public override string Price() { return _price; } public override string Publish_Date() { return _publish_date; } public override string Description() { return _description; } } }
파이프라인 배포
이제 추가 기능 세그먼트를 빌드하고 필요한 파이프라인 디렉터리 구조에 배포할 준비가 되었습니다.
세그먼트를 파이프라인에 배포하려면
솔루션의 각 프로젝트에 대해 프로젝트 속성의 빌드 탭(Visual Basic의 경우 컴파일 탭)을 사용하여 다음 표와 같이 출력 경로(Visual Basic의 경우 빌드 출력 경로) 값을 설정합니다.
프로젝트
경로
BooksAddIn
Pipeline\AddIns\CalcV1
AddInSideAdapters
Pipeline\AddInSideAdapters
AddInViews
Pipeline\AddInViews
LibraryContracts
Pipeline\Contracts
BookStore
Pipeline 또는 사용자가 정의한 응용 프로그램 디렉터리
HostSideAdapters
Pipeline\HostSideAdapters
HostViews
Pipeline 또는 사용자가 정의한 응용 프로그램 디렉터리
참고: 응용 프로그램을 Pipeline 폴더 이외의 다른 위치에 추가하려면 파이프라인 루트 디렉터리 위치를 지정하는 호스트 코드를 변경해야 합니다.
Visual Studio 솔루션을 빌드합니다.
파이프라인에 배포하는 방법에 대한 자세한 내용은 파이프라인 개발 요구 사항을 참조하십시오.
호스트 응용 프로그램 실행
이제 호스트를 실행하고 추가 기능과 상호 작용할 준비가 되었습니다.
호스트 응용 프로그램을 실행하려면
명령 프롬프트에서 파이프라인 루트 디렉터리로 이동하여 호스트 응용 프로그램을 실행합니다. 이 예제에서 호스트 응용 프로그램은 BookStore.exe입니다.
호스트는 해당 형식의 사용 가능한 모든 추가 기능을 찾고 추가 기능을 선택하라는 메시지를 표시합니다. 사용 가능한 추가 기능이 하나뿐이면 1을 입력합니다.
호스트에서 추가 기능을 활성화한 후 사용하여 서적 목록에 대해 몇 가지 작업을 수행합니다.
아무 키나 눌러 응용 프로그램을 닫습니다.
참고 항목
작업
연습: 호스트 변경으로 인한 이전 버전과의 호환성 활성화
개념
변경 기록
날짜 |
변경 내용 |
이유 |
---|---|---|
2008년 7월 |
텍스트의 오류가 수정되었습니다. 계약에 대한 참조를 유지하는 방법과 관련된 참고 사항이 추가되었습니다. |
고객 의견 |