演练:在宿主和外接程序之间传递集合

本演练介绍如何创建在外接程序和宿主之间传递自定义对象集合的管线。 因为集合中的类型是不可序列化的,因此必须将定义“视图到协定”和“协定到视图”适配器的其他类添加到适配器段,以使类型流可跨隔离边界。

在此方案中,外接程序为宿主更新书籍对象的集合。 每个书籍对象包含获取和设置书籍的标题、出版商、价格及其他数据的方法。

作为演示,宿主将创建一个书籍的集合,外接程序将所有计算机书籍的价格降低 20%,并从集合中移除所有恐怖书籍。 然后,外接程序为最畅销的图书新建一个书籍对象,并将其作为单个对象传递给宿主。

本演练阐释了以下任务:

  • 创建 Visual Studio 解决方案。

  • 创建管线目录结构。

  • 为必须跨隔离边界来回传递的对象创建协定和视图。

  • 创建跨隔离边界传递对象所必需的外接程序端适配器和宿主端适配器。

  • 创建宿主。

  • 创建外接程序。

  • 部署管线。

  • 运行宿主应用程序。

注意注意

本演练中所示的某些代码包含外部命名空间引用。演练步骤准确反映了 Visual Studio 中所需要的引用。

可以在 Managed Extensibility and Add-In Framework site on CodePlex(CodePlex 上的托管扩展性和外接程序框架站点)上找到更多代码示例,以及有关用于生成外接程序管线的工具的客户技术预览。

系统必备

您需要以下组件来完成本演练:

创建 Visual Studio 解决方案

使用 Visual Studio 中的解决方案来包含您的管线段项目。

创建管线解决方案

  1. 在 Visual Studio 中新建一个名为 LibraryContracts 的项目。 使该项目基于**“类库”**模板。

  2. 将解决方案命名为 BooksPipeline。

创建管线目录结构

外接程序模型要求管线段程序集放置在指定的目录结构中。

创建管线目录结构

  • 在计算机上创建以下文件夹结构。 可以将其置于任何位置,包括放在 Visual Studio 解决方案的文件夹内。

    Pipeline
      AddIns
        BooksAddIn
      AddInSideAdapters
      AddInViews
      Contracts
      HostSideAdapters
    

    除了根文件夹名称和各外接程序文件夹的名称之外,必须严格按照此处所示指定所有文件夹名称。 此示例使用 Pipeline 作为根文件夹名称,使用 BooksAddIn 作为外接程序文件夹名称。

    注意注意

    为方便起见,本演练将宿主应用程序放在管线根文件夹中。如果应用程序位于其他位置,本演练会在相应的步骤中说明如何更改代码。

    有关管线文件夹结构的更多信息,请参见管线开发要求

创建协定和视图

此管线的协定段定义两个接口:

  • IBookInfoContract 接口。

    此接口包含的方法(例如 Author)包含有关书籍的信息。

  • ILibraryManagerContract 接口。

    此接口包含外接程序用于处理书籍集合的 ProcessBooks 方法。 每本书表示一个 IBookInfoContract 协定。 该接口还包含外接程序用于将书籍对象(表示最畅销的书籍)提供给宿主的 GetBestSeller 方法。

创建协定

  1. 在名为 BooksPipeline 的 Visual Studio 解决方案中,打开 LibraryContracts 项目。

  2. 在 Visual Basic 中,打开 LibraryContracts 项目的**“属性”,然后使用“应用程序”选项卡删除为“根命名空间”提供的默认值。 默认情况下,“根命名空间”**设置为项目名称。

  3. 在**“解决方案资源管理器”**中,将对下面程序集的引用添加到该项目中:

    Sytem.AddIn.Contract.dll

    System.AddIn.dll

  4. 在类文件中,添加对 System.AddIn.ContractSystem.AddIn.Pipeline 命名空间的引用。

  5. 在类文件中,将默认的类声明替换为两个接口:

    • ILibraryManagerContract 接口用于激活外接程序,因此它必须具有 AddInContractAttribute 特性。

    • IBookInfoContract 接口表示在宿主和外接程序之间传递的对象,因此它不需要该特性。

    两个接口都必须继承 IContract 接口。

  6. 使用以下代码添加 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 接口提供视图。

创建外接程序视图

  1. 将名为 AddInViews 的新项目添加到 BooksPipeline 解决方案中。 使该项目基于**“类库”**模板。

  2. 在 Visual Basic 中,打开项目的**“属性”,然后使用“应用程序”选项卡删除为“根命名空间”**提供的默认值。

  3. 在**“解决方案资源管理器”**中,将对 System.AddIn.dll 的引用添加到 AddInViews 项目中。

  4. 将项目的默认类重命名为 LibraryManager,并使它成为 abstract(在 Visual Basic 中为 MustInherit)类。

  5. 在类文件中,添加对 System.AddIn.Pipeline 命名空间的引用。

  6. LibraryManager 类用于激活管线,因此必须应用 AddInBaseAttribute 特性。

  7. 使用下面的代码完成 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);
        }
    }
    
  8. 将一个 abstract 类(在 Visual Basic 中为 MustInherit 类)添加到项目,然后将其命名为 BookInfo。 BookInfo 类表示在宿主和外接程序之间传递的对象。 此类不用于激活管线,因此不需要任何特性。

  9. 使用下面的代码完成 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();
        }
    }
    

创建宿主视图

  1. 将名为 HostViews 的新项目添加到 BooksPipeline 解决方案中。 使该项目基于**“类库”**模板。

  2. 在 Visual Basic 中,打开项目的**“属性”,然后使用“应用程序”选项卡删除为“根命名空间”**提供的默认值。

  3. 将项目的默认类重命名为 LibraryManager,并使它成为 abstract(在 Visual Basic 中为 MustInherit)类。

  4. 使用下面的代码完成 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);
    }
    }
    
  5. 将一个 abstract 类(在 Visual Basic 中为 MustInherit 类)添加到项目,然后将其命名为 BookInfo。

  6. 使用下面的代码完成 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 类调用,以适配协定或视图,或者返回现有协定或视图。 这样可防止在对象往返于宿主和外接程序时另外创建适配器。

创建外接程序端适配器

  1. 将名为 AddInSideAdapters 的新项目添加到 BooksPipeline 解决方案中。 使该项目基于**“类库”**模板。

  2. 在 Visual Basic 中,打开项目的**“属性”,然后使用“应用程序”选项卡删除为“根命名空间”**提供的默认值。

  3. 在**“解决方案资源管理器”**中,将对下面程序集的引用添加到 AddInSideAdapters 项目:

    System.AddIn.dll

    System.AddIn.Contract.dll

  4. 在**“解决方案资源管理器”**中,将对下面项目的引用添加到 AddInSideAdapters 项目中:

    AddInViews

    LibraryContracts

    在引用**“属性”中,为这些引用将“复制本地”设置为“False”**,以禁止将引用的程序集复制到本地生成文件夹。 这些程序集将放置于管线目录结构中,如本演练后面部分中的“部署管线”过程中所述。

  5. 将类文件命名为 BookInfoContractToViewAddInAdapter。

  6. 在类文件中,添加对 System.AddIn.Pipeline 命名空间的引用。

  7. 使用以下代码添加 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;
        }
    }
    }
    
  8. 使用以下代码将 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;
        }
    }
    }
    
  9. 使用以下代码将 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;
        }
    }
    }
    
  10. 使用以下代码将 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 类调用此适配器以返回适配的任何现有协定或视图,而不创建调用的新实例。 这样可防止在对象往返于宿主和外接程序时另外创建适配器。

创建宿主端适配器

  1. 将名为 HostSideAdapters 的新项目添加到 BooksPipeline 解决方案中。 使该项目基于**“类库”**模板。

  2. 在 Visual Basic 中,打开项目的**“属性”,然后使用“应用程序”选项卡删除为“根命名空间”**提供的默认值。

  3. 在**“解决方案资源管理器”**中,将对下面程序集的引用添加到 HostSideAdapters 项目中:

    System.AddIn.dll

    System.AddIn.Contract.dll

  4. 在**“解决方案资源管理器”**中,将对下面项目的引用添加到 HostSideAdapters 项目中:

    HostViews

    LibraryContracts

    在引用**“属性”中,为这些引用将“复制本地”设置为“False”**,以禁止将引用的程序集复制到本地生成文件夹。

  5. 在类文件中,添加对 System.AddIn.Pipeline 命名空间的引用。

  6. 使用以下代码添加 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;
            }
        }
    }
    
  7. 使用以下代码将 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;
        }
    }
    }
    
  8. 使用以下代码将 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;
        }
    }
    }
    
  9. 使用以下代码将 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);
            }
        }
    }
    }
    

创建宿主

宿主应用程序通过宿主视图与外接程序交互。 它使用由 AddInStoreAddInToken 类提供的外接程序发现和激活方法执行以下操作:

  • 重新生成管线和外接程序信息的缓存。

  • 在指定管线根目录下找到 LibraryManager 类型的外接程序。

  • 提示用户选择要使用的外接程序。 在本示例中,只提供一个外接程序。

  • 激活具有指定安全信任级别的新应用程序域中选定的外接程序。

  • 调用 ProcessBooks 方法来将 BookInfo 对象的集合传递给外接程序。 外接程序调用其 ProcessBooks 方法的实现,并执行诸如将计算机书籍降价 20% 等功能。

  • 调用 GetBestSeller 方法,外接程序使用该方法返回包含最畅销图书信息的 BookInfo 对象。

  • 调用 Data 方法来从外接程序中获取当前销售税率。 此方法采用并返回一个属于密封的可序列化引用类型的字符串。 因此,在不使用“视图到协定”或“协定到视图”适配器的情况下,可以将此方法跨隔离边界传递到管线的另一端。

宿主有一个 CreateBooks 方法,用于创建 BookInfo 对象的集合。 此方法使用示例 books.xml 文件来创建集合,该示例文件可从Sample XML File (books.xml) 获得。

创建宿主

  1. 将名为 BookStore 的新项目添加到 BooksPipeline 解决方案中。 使该项目基于**“控制台应用程序”**模板。

  2. 在 Visual Basic 中,打开项目的**“属性”,然后使用“应用程序”选项卡删除为“根命名空间”**提供的默认值。

  3. 在**“解决方案资源管理器”**中,将对 System.AddIn.dll 程序集的引用添加到 BookStore 项目。

  4. 添加对 HostViews 项目的项目引用。 在引用**“属性”中,为此引用将“复制本地”设置为“False”**,以禁止将引用的程序集复制到本地生成文件夹。

  5. 在 Visual Basic 中,将该模块更改为类:

    • 从项目中排除默认模块,然后添加一个名为 Program 的类。

    • 将 Public 关键字替换为 Friend 关键字。

    • 将 Shared Sub Main() 过程添加到该类。

    • 使用**“项目属性”对话框的“应用程序”选项卡将“启动对象”设置为“Sub Main”**。

  6. 在类文件中,添加对 System.AddIn.Pipeline 和宿主视图段命名空间的引用。

  7. 在**“解决方案资源管理器”中选择该解决方案,然后从“项目”菜单上选择“属性”。 在“解决方案属性页”对话框中,将“单启动项目”**设置为此宿主应用程序项目。

  8. 对宿主应用程序使用以下代码。

    注意注意

    在代码中,请将 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 数据文件

  1. 将一个新的 XML 文件添加到 BookStore 项目中。 在**“添加新项”**对话框中,将该文件命名为 books.xml。

  2. 将 books.xml 的默认内容替换为Sample XML File (books.xml) 中的 XML。

  3. 在解决方案资源管理器中,选择 books.xml,然后在**“属性”中将“复制到输出目录”设置为“始终复制”**。

创建外接程序

外接程序实现由外接程序视图指定的方法。 此外接程序实现 ProcessBooks 方法。 该方法对由宿主传递给它的 BookInfo 对象的集合执行以下操作:

  • 将所有计算机书籍降价 20%。

  • 从集合中移除所有恐怖书籍。

此外接程序还通过将介绍最畅销书籍的 BookInfo 对象传递给宿主来实现 GetBestSeller 方法。

创建外接程序

  1. 将名为 BooksAddin 的新项目添加到 BooksPipeline 解决方案中。 使该项目基于**“类库”**模板。

  2. 在 Visual Basic 中,打开项目的**“属性”,然后使用“应用程序”选项卡删除为“根命名空间”**提供的默认值。

  3. 在**“解决方案资源管理器”**中,将对 System.AddIn.dll 程序集的引用添加到 BooksAddin 项目。

  4. 添加对 AddInViews 项目的项目引用。 在引用**“属性”中,为此引用将“复制本地”设置为“False”**,以禁止将引用的程序集复制到本地生成文件夹。

  5. 在类文件中,添加对 System.AddIn 和外接程序视图段命名空间的引用。

  6. 对外接程序应用程序使用以下代码。

    
    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;
        }
    }
    
    }
    

部署管线

现在已准备好生成外接程序段并将其部署到所需的管线目录结构。

将段部署到管线

  1. 对于解决方案中的每个项目,使用**“项目属性”“生成”选项卡(在 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 文件夹之外的其他位置,请确保更改用于指定管线根目录位置的宿主代码。

  2. 生成 Visual Studio 解决方案。

    有关部署到管线的信息,请参见管线开发要求

运行宿主应用程序

现已准备好运行宿主并将其与外接程序交互。

运行宿主应用程序

  1. 在命令提示符下,转至管线根目录并运行宿主应用程序。 在本例中,宿主应用程序为 BookStore.exe。

  2. 宿主找到其类型的所有可用外接程序,并提示您选择一个外接程序。 若只有一个可用的外接程序,则输入 1。

    宿主激活外接程序,并使用外接程序对书籍列表执行多个操作。

  3. 按任意键关闭应用程序。

请参见

任务

演练:创建可扩展的应用程序

演练:在宿主发生变化时启用向后兼容性

概念

管线开发要求

协定、视图和适配器

管线开发