逐步解說:在主應用程式和增益集之間傳遞集合

本逐步解說將說明如何建立可在增益集 (Add-In) 和主應用程式 (Host) 之間傳遞自訂物件集合的管線。 由於集合內的型別不可序列化,所以配接器區段內必須加入可定義檢視至合約配接器和合約至檢視配接器的其他類別 (Class),才能使型別的流向跨越隔離界限。

在此案例中,增益集會更新主應用程式的書籍物件集合。 每一個書籍物件都包含可取得及設定書籍之標題、出版商、價格和其他資料的方法。

在範例中的主應用程式會建立一個書籍集合,而增益集會使所有電腦書籍價格下降 20%,並將所有恐怖類書籍從該集合中移除。 接著,增益集會為銷售量最高書籍建立新的書籍物件,並將該物件以單一物件形式傳遞至主應用程式。

這個逐步解說將說明下列工作:

  • 建立 Visual Studio 方案。

  • 建立管線目錄結構。

  • 為必須跨隔離界限來回傳遞的物件建立合約或檢視。

  • 建立跨隔離界限傳遞物件所需的增益集端和主應用程式端配接器。

  • 建立主應用程式。

  • 建立增益集。

  • 部署管線。

  • 執行主應用程式。

注意事項注意事項

本逐步解說中顯示的部分程式碼包含沒有直接關聯的命名空間參考。逐步解說的步驟會精確反映出 Visual Studio 中所需的參考。

您可以在 CodePlex 上的 Managed 擴充性及增益集架構網站 (英文) 找到更多範例程式碼,以及可用來建置增益集管線之工具的客戶技術預覽。

必要條件

您需要下列元件才能完成此逐步解說:

建立 Visual Studio 方案

使用 Visual Studio 內的方案來包含管線區段的專案。

若要建立管線方案

  1. 在 Visual Studio 中建立新的專案,並將該專案命名為 LibraryContracts。 以 [類別庫] 範本為基礎。

  2. 將方案命名為 BooksPipeline。

建立管線目錄結構

增益集模型會要求將管線區段組件 (Assembly) 放置在指定的目錄結構中。

若要建立管線目錄結構

  • 在電腦上建立下列資料夾結構。 您可以在任何位置找到這個資料夾結構,包括 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 命名空間 (Namespace) 的參考。

  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 屬性,主應用程式檢視則不需要任何屬性。

這個管線的增益集檢視包含了兩個抽象類別 (Abstract Class)。 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 物件的合約轉換為檢視。 這個類別繼承自增益集檢視,並會藉由呼叫已傳遞至類別之建構函式 (Constructor) 的合約來實作該檢視的抽象方法。

    此配接器的建構函式會接受合約,因此 ContractHandle 物件可以套用至該合約,以便實作存留期 (Lifetime) 管理。

    重要

    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 物件的合約轉換為檢視。 這個類別繼承自主應用程式檢視,並會藉由呼叫已傳遞至類別之建構函式 (Constructor) 的合約來實作該檢視的抽象方法。

    此配接器的建構函式會接受合約,因此 ContractHandle 物件可以套用至該合約,以便實作存留期管理。

    重要

    ContractHandle 對於存留期管理來說非常重要。如果您無法保留 ContractHandle 物件的參考,記憶體回收將會回收該物件,而且若程式不接受管線,管線將隨即關閉。這可能會造成難以診斷的錯誤,例如 AppDomainUnloadedException。關機是管線週期的正常階段,因此存留期管理程式碼無法將這種狀況偵測為錯誤。

  • BookInfoViewToContractHostAdapter

    當主應用程式傳遞 BookInfo 物件至增益集時,便會呼叫這個配接器。 這個類別會將 BookInfo 物件的主應用程式檢視轉換為合約。 這個類別繼承自合約,並會藉由呼叫已傳遞至類別之建構函式的增益集檢視來實作該合約。 這個配接器會封送處理至增益集來做為合約。

  • LibraryManagerContractToViewHostAdapter

    當主應用程式傳遞 BookInfo 物件集合至增益集時,便會呼叫這個配接器。 增益集會在這個集合上執行其 ProcessBooks 方法的實作。

    這個類別會將 LibraryManager 物件的主應用程式檢視轉換為合約。 這個類別繼承自合約,並會藉由呼叫已傳遞至類別之建構函式的主應用程式檢視來實作該合約。

    由於自訂型別的集合 (即 BookInfo 物件) 必須跨越隔離界限進行封送處理,因此這個配接器會使用 CollectionAdapters 類別。 這個類別會提供將 IList<T> 集合轉換為 IListContract<T> 集合的方法,此方法可讓集合跨越隔離界限地傳遞至管線的另一端。

  • BookInfoHostAdapter

    這個配接器會被 LibraryManagerViewToContractHostAdapter 類別呼叫來傳回該配接的任何現有合約或檢視,而不是建立該呼叫的新執行個體 (Instance)。 如此可避免物件在主應用程式和增益集之間來回傳送時,建立額外的配接器。

若要建立主應用程式端配接器

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

建立主應用程式

主應用程式 (Host Application) 會透過主應用程式檢視與增益集互動。 它會使用 AddInStoreAddInToken 類別所提供的增益集探索 (Discovery) 和啟動方法,以執行下列動作:

  • 重建管線的快取和增益集資訊。

  • 在指定的管線根目錄下,找出型別 LibraryManager 的增益集。

  • 提示使用者選取要使用的增益集。 在此範例中,只有一個可用的增益集。

  • 以指定的安全信任層級,啟動新的應用程式定義域中選取的增益集。

  • 呼叫 ProcessBooks 方法,將 BookInfo 物件的集合傳遞至該增益集。 此增益集會呼叫其 ProcessBooks 方法的實作,並執行像是電腦書籍價格下降 20% 的函式。

  • 呼叫增益集會用來傳回 BookInfo 物件的 GetBestSeller 方法,而該物件包含了銷售量最高書籍的相關資訊。

  • 呼叫 Data 方法,從增益集取得目前的消費稅率。 這個方法會接受並傳回表示密封序列化參考型別 (Reference Type) 的字串。 如此一來,此方法便可跨越隔離界限地傳遞至管線的另一端,而不需要使用檢視至合約配接器或合約至檢視配接器。

主應用程式內含一個 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 中的 [建置輸出路徑]) 的值,如下表中所示。

    專案

    路徑

    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. 按下任意鍵關閉應用程式。

請參閱

工作

逐步解說:建立可延伸應用程式

逐步解說:在主應用程式變更時確保回溯相容性

概念

管線開發需求

合約、檢視和配接器

管線開發