演练:在宿主和外接程序之间传递集合
本演练介绍如何创建在外接程序和宿主之间传递自定义对象集合的管线。 因为集合中的类型是不可序列化的,因此必须将定义“视图到协定”和“协定到视图”适配器的其他类添加到适配器段,以使类型流可跨隔离边界。
在此方案中,外接程序为宿主更新书籍对象的集合。 每个书籍对象包含获取和设置书籍的标题、出版商、价格及其他数据的方法。
作为演示,宿主将创建一个书籍的集合,外接程序将所有计算机书籍的价格降低 20%,并从集合中移除所有恐怖书籍。 然后,外接程序为最畅销的图书新建一个书籍对象,并将其作为单个对象传递给宿主。
本演练阐释了以下任务:
创建 Visual Studio 解决方案。
创建管线目录结构。
为必须跨隔离边界来回传递的对象创建协定和视图。
创建跨隔离边界传递对象所必需的外接程序端适配器和宿主端适配器。
创建宿主。
创建外接程序。
部署管线。
运行宿主应用程序。
注意 |
---|
本演练中所示的某些代码包含外部命名空间引用。演练步骤准确反映了 Visual Studio 中所需要的引用。 |
可以在 Managed Extensibility and Add-In Framework site on CodePlex(CodePlex 上的托管扩展性和外接程序框架站点)上找到更多代码示例,以及有关用于生成外接程序管线的工具的客户技术预览。
系统必备
您需要以下组件来完成本演练:
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
此适配器的 static 方法(在 Visual Basic 中为 Shared 方法)由 LibraryManagerViewToContractAddInAdapter 类调用,以适配协定或视图,或者返回现有协定或视图。 这样可防止在对象往返于宿主和外接程序时另外创建适配器。
创建外接程序端适配器
将名为 AddInSideAdapters 的新项目添加到 BooksPipeline 解决方案中。 使该项目基于**“类库”**模板。
在 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; } } }
使用以下代码将 BookInfoViewToContractAddInAdapter 类添加到 AddInSideAdapters 项目中。 该类不用于激活管线,因此不需要特性。 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; } } }
使用以下代码将 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 项目中。 该类包含两个 static 方法(在 Visual Basic 中为 Shared 方法):ContractToViewAdapter 和 ViewToContractAdapter。 这些方法只供其他适配器类使用,因此是 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 类。 该类不用于激活管线,因此不需要特性。 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; } } }
使用以下代码将 BookInfoViewToContractHostAdapter 添加到 HostSideAdapters 项目中。 该类不用于激活管线,因此不需要特性。 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; } } }
使用以下代码将 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 项目中。 该类包含两个 static 方法(在 Visual Basic 中为 Shared 方法):ContractToViewAdapter 和 ViewToContractAdapter。 这些方法只供其他适配器类使用,因此是 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% 等功能。
调用 GetBestSeller 方法,外接程序使用该方法返回包含最畅销图书信息的 BookInfo 对象。
调用 Data 方法来从外接程序中获取当前销售税率。 此方法采用并返回一个属于密封的可序列化引用类型的字符串。 因此,在不使用“视图到协定”或“协定到视图”适配器的情况下,可以将此方法跨隔离边界传递到管线的另一端。
宿主有一个 CreateBooks 方法,用于创建 BookInfo 对象的集合。 此方法使用示例 books.xml 文件来创建集合,该示例文件可从Sample XML File (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 数据文件
将一个新的 XML 文件添加到 BookStore 项目中。 在**“添加新项”**对话框中,将该文件命名为 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 中为“生成输出路径”**)的值,如下表所示。
Project
路径
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。
宿主激活外接程序,并使用外接程序对书籍列表执行多个操作。
按任意键关闭应用程序。