チュートリアル : アドインとホスト間でのコレクションの受け渡し
このチュートリアルでは、アドインとホスト間でカスタム オブジェクトのコレクションを受け渡すパイプラインを作成する方法を説明します。 コレクション内の型はシリアル化できないため、型のフローが分離境界を越えられるように、ビューからコントラクトへのアダプターおよびコントラクトからビューへのアダプターをアダプター セグメントに追加する必要があります。
このシナリオでは、ホストに対して書籍のオブジェクトのコレクションを更新します。 それぞれの書籍オブジェクトには、書籍のタイトル、版元、価格などのデータを取得し設定するためのメソッドが含まれています。
例として、ホストで書籍のコレクションを作成します。アドインを使用して、コンピューター関連の全書籍の価格を 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 としています。
メモ チュートリアルでは、便宜上、ホスト アプリケーションをパイプラインのルート フォルダーに配置しています。アプリケーションが別の場所にある場合は、チュートリアルの該当の手順で、コードを変更する方法について説明します。
パイプライン フォルダー構造の詳細については、「パイプライン開発の必要条件」を参照してください。
コントラクトとビューの生成
このパイプラインのコントラクト セグメントで、2 つのインターフェイスが定義されます。
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 名前空間への参照を追加します。
クラス ファイル内の既定のクラス宣言を 2 つのインターフェイスに置き換えます。
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(); } }
アドイン ビューとホスト ビューは、コードが同じであるため、ビューを同時に簡単に作成できます。 この 2 つのビューの相違点は、パイプラインのこのセグメントをアクティブ化するときに使用するアドイン ビューには AddInBaseAttribute 属性が必要ですが、ホスト ビューには属性が必要ないということのみです。
このパイプラインのアドイン ビューには、2 つの抽象クラスが含まれています。 BookInfo クラスは IBookInfoContract インターフェイスのビューを提供し、LibraryManager クラスは ILibraryManagerContract インターフェイスのビューを提供します。
アドイン ビューを作成するには
BooksPipeline ソリューションに AddInViews という名前の新規プロジェクトを追加します。 それにクラス ライブラリ テンプレートを適用します。
Visual Basic で、プロジェクトの [プロパティ] を開き、[アプリケーション] タブを使用して、[ルート名前空間] に指定されている既定値を削除します。
ソリューション エクスプローラーで、AddInViews プロジェクトに System.AddIn.dll への参照を追加します。
プロジェクトの既定のクラス名を 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(); } }
ホスト ビューを作成するには
BooksPipeline ソリューションに HostViews という名前の新規プロジェクトを追加します。 それにクラス ライブラリ テンプレートを適用します。
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(); } }
アドイン側アダプターの作成
このパイプラインのアドイン側アダプター アセンブリには、4 つの抽象クラスが含まれています。
BookInfoContractToViewAddInAdapter
ホストからアドインに BookInfo オブジェクトが渡されるとき、このアドインが単独で、またはコレクションの一部として呼び出されます。 このクラスにより、BookInfo オブジェクトのコントラクトがビューに変換されます。 このクラスはアドイン ビューを継承し、クラスのコンストラクターに渡されるコントラクトを呼び出すことによって、ビューの抽象メソッドを実装します。
このアダプターのコンストラクターはコントラクトを受け取ります。そのため、ContractHandle オブジェクトをコントラクトに適用して有効期間管理を実装できるようになります。
重要 有効期間の管理には、ContractHandle が重要です。ContractHandle オブジェクトへの参照を保持していないと、ガベージ コレクションによってオブジェクトが再利用され、プログラムで予期していないタイミングでパイプラインがシャットダウンすることになります。これは、AppDomainUnloadedException のような診断が困難なエラーの原因となる可能性があります。シャットダウンはパイプラインの正常な段階の 1 つであるため、有効期間を管理するコードでこの状態をエラーとして検出する方法はありません。
BookInfoViewToContractAddInAdapter
アドインからホストに BookInfo オブジェクトが渡されるとき、このアダプターが呼び出されます。 このクラスにより、BookInfo オブジェクトのアドイン ビューがコントラクトに変換されます。 このクラスはコントラクトを継承し、クラスのコンストラクターに渡されるアドインを呼び出すことによって、コントラクトを実装します。 このアダプターはコントラクトとしてホストにマーシャリングされます。
LibraryManagerViewToContractAddInAdapter
これは、アドインをアクティブにする呼び出しによってホストに返される型です。 ホスト オブジェクトのコレクション (IList<BookInfo>) をアドインに渡す呼び出しなどで、ホストがアドインを呼び出すときに、この型が呼び出されます。 このクラスにより、ILibraryManagerContract コントラクトが LibraryManager ホスト ビューに変換されます。 このクラスはホスト ビューを継承し、コンストラクターに渡されるビューを呼び出すことで、コントラクトを実装します。
カスタム型のコレクションである BookInfo オブジェクトは、分離境界を越えてマーシャリングする必要があるため、このアダプターでは CollectionAdapters クラスが使用されます。 このクラスには、IList<T> コレクションを IListContract<T> コレクションに変換するメソッドがあります。これによって、分離境界を越えてコレクションをパイプラインの反対側に渡すことができます。
BookInfoAddInAdapter
このアダプターの static メソッド (Visual Basic では Shared メソッド) は、LibraryManagerViewToContractAddInAdapter クラスによって呼び出されて、コントラクトまたはビューを適応するか、または、既存のコントラクトまたはビューを返します。 このため、オブジェクトがホストとアドイン間でラウンド トリップを行う場合に、追加のアダプターを作成しなくて済みます。
アドイン側アダプターを作成するには
BooksPipeline ソリューションに AddInSideAdapters という名前の新規プロジェクトを追加します。 それにクラス ライブラリ テンプレートを適用します。
Visual Basic で、プロジェクトの [プロパティ] を開き、[アプリケーション] タブを使用して、[ルート名前空間] に指定されている既定値を削除します。
ソリューション エクスプローラーで、次のアセンブリへの参照を AddInSideAdapters プロジェクトに追加します。
System.AddIn.dll
System.AddIn.Contract.dll
ソリューション エクスプローラーで、次のプロジェクトへの参照を AddInSideAdapters プロジェクトに追加します。
AddInViews
LibraryContracts
参照アセンブリがローカルなビルド フォルダーにコピーされることのないように、これらの参照の [プロパティ] で [ローカルにコピーする] を [False] に設定します。 このチュートリアルの後の手順の「パイプラインの配置」で説明するように、アセンブリはパイプライン ディレクトリ構造に配置されます。
クラス ファイルに BookInfoContractToViewAddInAdapter という名前を付けます。
クラス ファイルで、System.AddIn.Pipeline 名前空間への参照を追加します。
次のコードを使用して、BookInfoContractToViewAddInAdapter クラスを追加します。 このクラスを使用してパイプラインをアクティブ化することはないため、属性は必要ありません。 BookInfoAddInAdapter クラスで internal (Visual Basic では Friend) GetSourceContract メソッドを使用することにより、オブジェクトがホストとアドイン間でラウンド トリップを行う場合に、追加のアダプターを作成しなくて済みます。
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; } } }
次のコードを使用して、AddInSideAdapters プロジェクトに BookInfoViewToContractAddInAdapter クラスを追加します。 このクラスを使用してパイプラインをアクティブ化することはないため、属性は必要ありません。 BookInfoAddInAdapter クラスで internal (Visual Basic では Friend) GetSourceView メソッドを使用することにより、オブジェクトがホストとアドイン間でラウンド トリップを行う場合に、追加のアダプターを作成しなくて済みます。
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; } } }
次のコードを使用して、AddInSideAdapters プロジェクトに LibraryManagerViewToContractAddInAdapter クラスを追加します。 このクラスを使用してパイプラインをアクティブ化するため、このクラスには AddInAdapterAttribute 属性が必要です。
ProcessBooks メソッドは、分離境界を越えて書籍の一覧を渡す方法を示します。 CollectionAdapters.ToIList メソッドを使用して一覧を変換します。 一覧内のオブジェクトを変換するには、BookInfoAddInAdapter クラスで提供されるアダプター メソッドのデリゲートを渡します。
GetBestSeller メソッドは、分離境界を越えて 1 つの 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; } } }
次のコードを使用して、AddInSideAdapters プロジェクトに BookInfoAddInAdapter クラスを追加します。 クラスには、ContractToViewAdapter および ViewToContractAdapter という、2 つの 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); } } } }
ホスト側アダプターの作成
このパイプラインのこのホスト側アダプター アセンブリには、4 つの抽象クラスが含まれています。
BookInfoContractToViewHostAdapter
アドインからホストに BookInfo オブジェクトが渡されるとき、このアダプターが単独で、またはコレクションの一部として呼び出されます。 このクラスにより、BookInfo オブジェクトのコントラクトがビューに変換されます。 このクラスはホスト ビューを継承し、クラスのコンストラクターに渡されるコントラクトを呼び出すことによって、ビューの抽象メソッドを実装します。
このアダプターのコンストラクターは、そのコンストラクター用のコントラクトを受け取ります。そのため、ContractHandle オブジェクトをコントラクトに適用して有効期間管理を実装できるようになります。
重要 有効期間の管理には、ContractHandle が重要です。ContractHandle オブジェクトへの参照を保持していないと、ガベージ コレクションによってオブジェクトが再利用され、プログラムで予期していないタイミングでパイプラインがシャットダウンすることになります。これは、AppDomainUnloadedException のような診断が困難なエラーの原因となる可能性があります。シャットダウンはパイプラインの正常な段階の 1 つであるため、有効期間を管理するコードでこの状態をエラーとして検出する方法はありません。
BookInfoViewToContractHostAdapter
ホストからアドインに BookInfo オブジェクトが渡されるとき、このアダプターが呼び出されます。 このクラスにより、BookInfo オブジェクトのホスト ビューがコントラクトに変換されます。 このクラスはコントラクトを継承し、クラスのコンストラクターに渡されるアドインを呼び出すことによって、コントラクトを実装します。 このアダプターはコントラクトとしてアドインにマーシャリングされます。
LibraryManagerContractToViewHostAdapter
このアダプターは、ホストからアドインに BookInfo オブジェクトのコレクションが渡されるときに呼び出されます。 このアドインで、このコレクションへの ProcessBooks メソッドの実装が実行されます。
このクラスにより、LibraryManager オブジェクトのホスト ビューがコントラクトに変換されます。 コントラクトを継承し、クラスのコンストラクターに渡されるホスト ビューを呼び出すことで、コントラクトを実装します。
カスタム型のコレクションである BookInfo オブジェクトは、分離境界を越えてマーシャリングする必要があるため、このアダプターでは CollectionAdapters クラスが使用されます。 このクラスには、IList<T> コレクションを IListContract<T> コレクションに変換するメソッドがあります。これによって、分離境界を越えてコレクションをパイプラインの反対側に渡すことができます。
BookInfoHostAdapter
このアダプターは、呼び出しで新しいインスタンスを作成するのではなく、LibraryManagerViewToContractHostAdapter クラスによって呼び出すことで、変換で使用される既存のコントラクトやビューをすべて返します。 このため、オブジェクトがホストとアドイン間のいずれかの方向でラウンド トリップを行う場合に、余分なアダプターを作成しなくて済みます。
ホスト側アダプターを作成するには
BooksPipeline ソリューションに HostSideAdapters という名前の新規プロジェクトを追加します。 それにクラス ライブラリ テンプレートを適用します。
Visual Basic で、プロジェクトの [プロパティ] を開き、[アプリケーション] タブを使用して、[ルート名前空間] に指定されている既定値を削除します。
ソリューション エクスプローラーで、次のアセンブリへの参照を HostSideAdapters プロジェクトに追加します。
System.AddIn.dll
System.AddIn.Contract.dll
ソリューション エクスプローラーで、次のプロジェクトへの参照を HostSideAdapters プロジェクトに追加します。
HostViews
LibraryContracts
参照アセンブリがローカルなビルド フォルダーにコピーされることのないように、これらの参照の [プロパティ] で [ローカルにコピーする] を [False] に設定します。
クラス ファイルで、System.AddIn.Pipeline 名前空間への参照を追加します。
次のコードを使用して、BookInfoContractToViewHostAdapter クラスを追加します。 このクラスを使用してパイプラインをアクティブ化することはないため、属性は必要ありません。 BookInfoAddInAdapter クラスで internal (Visual Basic では Friend) GetSourceContract メソッドを使用することにより、オブジェクトがホストとアドイン間でラウンド トリップを行う場合に、余分なアダプターを作成しなくて済みます。
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; } } }
次のコードを使用して、HostSideAdapters プロジェクトに BookInfoViewToContractHostAdapter を追加します。 このクラスを使用してパイプラインをアクティブ化することはないため、属性は必要ありません。 BookInfoAddInAdapter クラスで internal (Visual Basic では Friend) GetSourceView メソッドを使用することにより、オブジェクトがホストとアドイン間でラウンド トリップを行う場合に、余分なアダプターを作成しなくて済みます。
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; } } }
次のコードを使用して、HostSideAdapters プロジェクトに LibraryManagerContractToViewHostAdapter を追加します。 このクラスを使用してパイプラインをアクティブ化するため、このクラスには HostAdapterAttribute 属性が必要です。
ProcessBooks メソッドは、分離境界を越えて書籍の一覧を渡す方法を示します。 CollectionAdapters.ToIListContract メソッドを使用して一覧を変換します。 一覧内のオブジェクトを変換するには、BookInfoHostAdapter クラスで提供されるアダプター メソッドのデリゲートを渡します。
GetBestSeller メソッドは、分離境界を越えて 1 つの 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; } } }
次のコード例を使用して、HostSideAdapters プロジェクトに BookInfoHostAdapter クラスを追加します。 クラスには、ContractToViewAdapter および ViewToContractAdapter という、2 つの 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 型のアドインを検索する。
使用するアドインを選択するように促すメッセージを表示する。 このサンプルでは、使用できるアドインは 1 つのみです。
セキュリティ信頼レベルが指定された新しいアプリケーション ドメインで、選択されたアドインをアクティブにする。
ProcessBooks メソッドを呼び出して、BookInfo オブジェクトのコレクションをアドインに渡す。 アドインでは ProcessBooks メソッドの実装が呼び出され、たとえばコンピューター関連書籍を 20% 割り引くなどの機能を実行します。
ベストセラー書籍の情報を含む BookInfo オブジェクトを返すためにアドインで使用される GetBestSeller メソッドを呼び出す。
Data メソッドを呼び出し、アドインから現在の消費税率を取得する。 このメソッドは、シールされたシリアル化可能な参照型である文字列をやり取りします。 それにより、ビューからコントラクトへのアダプターやコントラクトからビューへのアダプターを使用することなく、分離境界を越えてこのメソッドをパイプラインの反対側に渡すことができます。
ホストには、BookInfo オブジェクトのコレクションを作成する CreateBooks メソッドがあります。 このメソッドにより、books.xml サンプル ファイルを使用したコレクションが作成されます。このファイルは、「Sample XML File (books.xml)」で入手できます。
ホストを作成するには
BooksPipeline ソリューションに BookStore という名前の新規プロジェクトを追加します。 これにコンソール アプリケーション テンプレートを適用します。
Visual Basic で、プロジェクトの [プロパティ] を開き、[アプリケーション] タブを使用して、[ルート名前空間] に指定されている既定値を削除します。
ソリューション エクスプローラーで、BookStore プロジェクトに System.AddIn.dll アセンブリへの参照を追加します。
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 データ ファイルを作成するには
新しい XML ファイルを BookStore プロジェクトに追加します。 [新しい項目の追加] ダイアログ ボックスで、ファイルに books.xml という名前を付けます。
books.xml の既定のコンテンツを、「Sample XML File (books.xml)」の XML に置き換えます。
ソリューション エクスプローラーで books.xml を選択し、[プロパティ] で、[出力ディレクトリにコピー] を [常にコピーする] に設定します。
アドインの作成
アドインにより、アドイン ビューで指定されたメソッドを実装します。 このアドインにより、ProcessBooks メソッドが実装されます。 このメソッドでは、ホストが渡す BookInfo オブジェクトのコレクションに対して、次の処理を実行します。
コンピューター関連書籍の価格をすべて 20% 引きにする。
コレクションからホラー小説をすべて削除する。
さらに、このアドインではベストセラー書籍を指定する BookInfo オブジェクトをホストに渡すことで、GetBestSeller メソッドを実装します。
アドインを作成するには
BooksPipeline ソリューションに BooksAddin という名前の新規プロジェクトを追加します。 それにクラス ライブラリ テンプレートを適用します。
Visual Basic で、プロジェクトの [プロパティ] を開き、[アプリケーション] タブを使用して、[ルート名前空間] に指定されている既定値を削除します。
ソリューション エクスプローラーで、BooksAddin プロジェクトに System.AddIn.dll アセンブリへの参照を追加します。
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 つのみであるため、「1」を入力します。
ホストはアドインをアクティブ化し、それを使用して書籍の一覧に対してさまざまな処理を実行します。
任意のキーを押してアプリケーションを閉じます。