Пошаговое руководство. Передача коллекций между основными приложениями и надстройками

В этом пошаговом руководстве описано, как создать конвейер для передачи коллекции пользовательских объектов между надстройкой и основным приложением. Так как типы в коллекции не могут быть сериализованы, дополнительные классы, которые определяют адаптеры "представление-контракт" и "контракт-представления, должны быть добавлены в сегменты адаптера, чтобы поток типов мог пересекать границы изоляции.

В этом сценарии надстройка обновляет коллекцию объектов книг для основного приложения. В каждом объекте книг содержатся методы, которые получают и задают название книги, издателя, стоимость и другие данные.

В качестве примера основное приложение создает коллекцию книг; надстройка уменьшает стоимость всех книг, посвященных компьютерам, на 20 процентов и удаляет из коллекции все книги жанра ужасов. Затем надстройка создает новый объект книги для наиболее продаваемой книги и передает его в основное приложение как один объект.

В данном пошаговом руководстве рассмотрены следующие задачи:

  • Создание решения в Visual Studio.

  • Создание структуры каталогов конвейера.

  • Создание контрактов и представлений для объектов, которые должны передаваться в обе стороны через границу изоляции.

  • Создание адаптеров на стороне надстройки и на стороне основного приложения, которые необходимы для передачи объектов через границы изоляции.

  • Создание основного приложения.

  • Создание надстройки.

  • Развертывание конвейера.

  • Запуск основного приложения.

ПримечаниеПримечание

В коде, приведенном в этом пошаговом руководстве, содержатся ссылки на внешние пространства имен.Действия в этом пошаговом руководстве с точностью отражают ссылки, необходимые в Visual Studio.

Дополнительные примеры кода и CTP-версии средств построения конвейеров надстроек см. на веб-узле 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. В решении Visual Studio BooksPipeline откройте проект LibraryContracts.

  2. В Visual Basic откройте Свойства для проекта LibraryContracts и на вкладке Приложение удалите значение по умолчанию, представленное для корневого пространства имен. По умолчанию значение корневого пространства имен равняется имени проекта.

  3. В Обозревателе решений добавьте в проект ссылки на следующие сборки:

    System.AddIn.Contract.dll

    System.AddIn.dll

  4. В файле класса добавьте ссылки на пространства имен System.AddIn.Contract и System.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. В обозревателе решений в проекте AddInViews добавьте ссылку на System.AddIn.dll.

  4. Переименуйте класс по умолчанию LibraryManager для этого проекта и создайте класс abstract (MustInherit в Visual Basic).

  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 (класс MustInherit в Visual Basic) и назовите его 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 (MustInherit в Visual Basic).

  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 (класс MustInherit в Visual Basic) и назовите его 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 (методы Shared в Visual Basic) этого адаптера вызываются классом LibraryManagerViewToContractAddInAdapter для адаптации контракта или представления, а также для возврата существующего контракта или представления. Это препятствует созданию дополнительного адаптера при циклическом прохождении объекта между основным приложением и надстройкой.

Чтобы создать адаптер на стороне надстройки

  1. Добавьте в решение BooksPipeline новый проект под названием AddInSideAdapters. Он должен быть основан на шаблоне Библиотека классов.

  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. Этот класс не нуждается в атрибуте, потому что он не используется для активации конвейера. Метод internal (Friend в Visual Basic) GetSourceContract используется классом BookInfoAddInAdapter, чтобы избежать создания дополнительного адаптера при циклическом прохождении объекта между основным приложением и надстройкой.

    
    Imports Microsoft.VisualBasic
    Imports System
    Imports System.AddIn.Pipeline
    Namespace LibraryContractsAddInAdapters
    
    Public Class BookInfoContractToViewAddInAdapter
        Inherits LibraryContractsBase.BookInfo
        Private _contract As Library.IBookInfoContract
        Private _handle As System.AddIn.Pipeline.ContractHandle
        Public Sub New(ByVal contract As Library.IBookInfoContract)
            _contract = contract
            _handle = New ContractHandle(contract)
        End Sub
    
        Public Overrides Function ID() As String
            Return _contract.ID()
        End Function
        Public Overrides Function Author() As String
            Return _contract.Author()
        End Function
        Public Overrides Function Title() As String
            Return _contract.Title()
        End Function
        Public Overrides Function Genre() As String
            Return _contract.Genre()
        End Function
        Public Overrides Function Price() As String
            Return _contract.Price()
        End Function
        Public Overrides Function Publish_Date() As String
            Return _contract.Publish_Date()
        End Function
        Public Overrides Function Description() As String
            Return _contract.Description()
        End Function
    
        Friend Function GetSourceContract() As Library.IBookInfoContract
            Return _contract
        End Function
    End Class
    End Namespace
    
    using System;
    using System.AddIn.Pipeline;
    namespace LibraryContractsAddInAdapters 
    {
    
    public class BookInfoContractToViewAddInAdapter : LibraryContractsBase.BookInfo 
    {
        private Library.IBookInfoContract _contract;
        private System.AddIn.Pipeline.ContractHandle _handle;
        public BookInfoContractToViewAddInAdapter(Library.IBookInfoContract contract) 
        {
            _contract = contract;
            _handle = new ContractHandle(contract);
        }
    
        public override string ID()
        {
            return _contract.ID();
        }
        public override string Author()
        {
            return _contract.Author();
        }
        public override string Title()
        {
            return _contract.Title();
        }
        public override string Genre()
        {
            return _contract.Genre();
        }
        public override string Price()
        {
            return _contract.Price();
        }
        public override string Publish_Date()
        {
            return _contract.Publish_Date();
        }
        public override string Description()
        {
            return _contract.Description();
        }
    
        internal Library.IBookInfoContract GetSourceContract() {
            return _contract;
        }
    }
    }
    
  8. Используйте следующий код для добавления класса BookInfoViewToContractAddInAdapter в проект AddInSideAdapters. Этот класс не нуждается в атрибуте, потому что он не используется для активации конвейера. Метод internal (Friend в Visual Basic) GetSourceView используется классом BookInfoAddInAdapter, чтобы избежать создания дополнительного адаптера при циклическом прохождении объекта между основным приложением и надстройкой.

    
    Imports Microsoft.VisualBasic
    Imports System
    
    Namespace LibraryContractsAddInAdapters
    Public Class BookInfoViewToContractAddInAdapter
        Inherits System.AddIn.Pipeline.ContractBase
        Implements Library.IBookInfoContract
        Private _view As LibraryContractsBase.BookInfo
        Public Sub New(ByVal view As LibraryContractsBase.BookInfo)
            _view = view
        End Sub
        Public Overridable Function ID() As String Implements Library.IBookInfoContract.ID
            Return _view.ID()
        End Function
        Public Overridable Function Author() As String Implements Library.IBookInfoContract.Author
            Return _view.Author()
        End Function
        Public Overridable Function Title() As String Implements Library.IBookInfoContract.Title
            Return _view.Title()
        End Function
        Public Overridable Function Genre() As String Implements Library.IBookInfoContract.Genre
            Return _view.Genre()
        End Function
        Public Overridable Function Price() As String Implements Library.IBookInfoContract.Price
            Return _view.Price()
        End Function
        Public Overridable Function Publish_Date() As String Implements Library.IBookInfoContract.Publish_Date
            Return _view.Publish_Date()
        End Function
        Public Overridable Function Description() As String Implements Library.IBookInfoContract.Description
            Return _view.Description()
        End Function
    
        Friend Function GetSourceView() As LibraryContractsBase.BookInfo
            Return _view
        End Function
    End Class
    End Namespace
    
    using System;
    
    namespace LibraryContractsAddInAdapters 
    {
    public class BookInfoViewToContractAddInAdapter : System.AddIn.Pipeline.ContractBase, Library.IBookInfoContract 
    {
        private LibraryContractsBase.BookInfo _view;
        public BookInfoViewToContractAddInAdapter(LibraryContractsBase.BookInfo view) 
        {
            _view = view;
        }
        public virtual string ID()
        {
            return _view.ID();
        }
        public virtual string Author()
        {
            return _view.Author();
        }
        public virtual string Title()
        {
            return _view.Title();
        }
        public virtual string Genre()
        {
            return _view.Genre();
        }
        public virtual string Price()
        {
            return _view.Price();
        }
        public virtual string Publish_Date()
        {
            return _view.Publish_Date();
        }
        public virtual string Description()
        {
            return _view.Description();
        }
    
        internal LibraryContractsBase.BookInfo GetSourceView() {
            return _view;
        }
    }
    }
    
  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 (методы Shared в Visual Basic): ContractToViewAdapter и ViewToContractAdapter. Эти методы являются internal (Friend в Visual Basic), потому что они используется только другими классами адаптера. Эти методы предназначены для того, чтобы избежать создания дополнительного адаптера при циклическом прохождении объекта в любом направлении между основным приложением и надстройкой. Эти методы должны быть предоставлены для тех адаптеров, которые передают объекты через границы изоляции.

    
    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. Этот класс не нуждается в атрибуте, потому что он не используется для активации конвейера. Метод internal (Friend в Visual Basic) GetSourceContract используется классом BookInfoAddInAdapter, чтобы избежать создания дополнительного адаптера при циклическом прохождении объекта между основным приложением и надстройкой.

    
    Imports Microsoft.VisualBasic
    Imports System.AddIn.Pipeline
    Namespace LibraryContractsHostAdapters
    Public Class BookInfoContractToViewHostAdapter
        Inherits LibraryContractsHAV.BookInfo
        Private _contract As Library.IBookInfoContract
    
        Private _handle As ContractHandle
    
        Public Sub New(ByVal contract As Library.IBookInfoContract)
            _contract = contract
            _handle = New ContractHandle(contract)
        End Sub
    
        Public Overrides Function ID() As String
            Return _contract.ID()
        End Function
        Public Overrides Function Author() As String
            Return _contract.Author()
        End Function
        Public Overrides Function Title() As String
            Return _contract.Title()
        End Function
        Public Overrides Function Genre() As String
            Return _contract.Genre()
        End Function
        Public Overrides Function Price() As String
            Return _contract.Price()
        End Function
        Public Overrides Function Publish_Date() As String
            Return _contract.Publish_Date()
        End Function
        Public Overrides Function Description() As String
            Return _contract.Description()
        End Function
    
    
        Friend Function GetSourceContract() As Library.IBookInfoContract
            Return _contract
        End Function
    End Class
    End Namespace
    
    using System.AddIn.Pipeline;
    namespace LibraryContractsHostAdapters
    {
        public class BookInfoContractToViewHostAdapter : LibraryContractsHAV.BookInfo
        {
            private Library.IBookInfoContract _contract;
    
            private ContractHandle _handle;
    
            public BookInfoContractToViewHostAdapter(Library.IBookInfoContract contract)
            {
                _contract = contract;
                _handle = new ContractHandle(contract);
            }
    
            public override string ID()
            {
                return _contract.ID();
            }
            public override string Author()
            {
                return _contract.Author();
            }
            public override string Title()
            {
                return _contract.Title();
            }
            public override string Genre()
            {
                return _contract.Genre();
            }
            public override string Price()
            {
                return _contract.Price();
            }
            public override string Publish_Date()
            {
                return _contract.Publish_Date();
            }
            public override string Description()
            {
                return _contract.Description();
            }
    
    
            internal Library.IBookInfoContract GetSourceContract() {
                return _contract;
            }
        }
    }
    
  7. Используйте приведенный ниже код для добавления BookInfoViewToContractHostAdapter в проект HostSideAdapters. Этот класс не нуждается в атрибуте, потому что он не используется для активации конвейера. Метод internal (Friend в Visual Basic) GetSourceView используется классом BookInfoAddInAdapter, чтобы избежать создания дополнительного адаптера при циклическом прохождении объекта между основным приложением и надстройкой.

    
    Imports Microsoft.VisualBasic
    Imports System.AddIn.Pipeline
    Namespace LibraryContractsHostAdapters
    Public Class BookInfoViewToContractHostAdapter
        Inherits ContractBase
        Implements Library.IBookInfoContract
        Private _view As LibraryContractsHAV.BookInfo
    
        Public Sub New(ByVal view As LibraryContractsHAV.BookInfo)
            _view = view
        End Sub
    
        Public Overridable Function ID() As String Implements Library.IBookInfoContract.ID
            Return _view.ID()
        End Function
        Public Overridable Function Author() As String Implements Library.IBookInfoContract.Author
            Return _view.Author()
        End Function
        Public Overridable Function Title() As String Implements Library.IBookInfoContract.Title
            Return _view.Title()
        End Function
        Public Overridable Function Genre() As String Implements Library.IBookInfoContract.Genre
            Return _view.Genre()
        End Function
        Public Overridable Function Price() As String Implements Library.IBookInfoContract.Price
            Return _view.Price()
        End Function
        Public Overridable Function Publish_Date() As String Implements Library.IBookInfoContract.Publish_Date
            Return _view.Publish_Date()
        End Function
        Public Overridable Function Description() As String Implements Library.IBookInfoContract.Description
            Return _view.Description()
        End Function
        Friend Function GetSourceView() As LibraryContractsHAV.BookInfo
            Return _view
        End Function
    End Class
    End Namespace
    
    using System.AddIn.Pipeline;
    namespace LibraryContractsHostAdapters
    {
    public class BookInfoViewToContractHostAdapter : ContractBase, Library.IBookInfoContract
    {
        private LibraryContractsHAV.BookInfo _view;
    
        public BookInfoViewToContractHostAdapter(LibraryContractsHAV.BookInfo view)
        {
            _view = view;
        }
    
        public virtual string ID()
        {
            return _view.ID();
        }
        public virtual string Author()
        {
            return _view.Author();
        }
        public virtual string Title()
        {
            return _view.Title();
        }
        public virtual string Genre()
        {
            return _view.Genre();
        }
        public virtual string Price()
        {
            return _view.Price();
        }
        public virtual string Publish_Date()
        {
            return _view.Publish_Date();
        }
        public virtual string Description()
        {
            return _view.Description();
        }
        internal LibraryContractsHAV.BookInfo GetSourceView()
        {
            return _view;
        }
    }
    }
    
  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 (методы Shared в Visual Basic): ContractToViewAdapter и ViewToContractAdapter. Эти методы являются internal (Friend в Visual Basic), потому что они используется только другими классами адаптера. Эти методы предназначены для того, чтобы избежать создания дополнительного адаптера при циклическом прохождении объекта в любом направлении между основным приложением и надстройкой. Эти методы должны быть предоставлены для тех адаптеров, которые передают объекты через границы изоляции.

    
    Imports Microsoft.VisualBasic
    Imports System
    Namespace LibraryContractsHostAdapters
    Public Class BookInfoHostAdapter
    
    Friend Shared Function ContractToViewAdapter(ByVal contract As Library.IBookInfoContract) As LibraryContractsHAV.BookInfo
        If Not System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(contract) AndAlso _
            CType(contract, Object).GetType().Equals(GetType(BookInfoViewToContractHostAdapter)) Then
            Return (CType(contract, BookInfoViewToContractHostAdapter)).GetSourceView()
        Else
            Return New BookInfoContractToViewHostAdapter(contract)
        End If
    End Function
    
    Friend Shared Function ViewToContractAdapter(ByVal view As LibraryContractsHAV.BookInfo) As Library.IBookInfoContract
        If Not System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(view) AndAlso _
            view.GetType().Equals(GetType(BookInfoContractToViewHostAdapter)) Then
            Return (CType(view, BookInfoContractToViewHostAdapter)).GetSourceContract()
        Else
            Return New BookInfoViewToContractHostAdapter(view)
        End If
    End Function
    End Class
    End Namespace
    
    using System;
    namespace LibraryContractsHostAdapters
    {
    public class BookInfoHostAdapter
    {
    
        internal static LibraryContractsHAV.BookInfo ContractToViewAdapter(Library.IBookInfoContract contract)
        {
            if (!System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(contract) &&
                (contract.GetType().Equals(typeof(BookInfoViewToContractHostAdapter))))
            {
                return ((BookInfoViewToContractHostAdapter)(contract)).GetSourceView();
    
            }
            else {
                return new BookInfoContractToViewHostAdapter(contract);
            }
        }
    
        internal static Library.IBookInfoContract ViewToContractAdapter(LibraryContractsHAV.BookInfo view)
        {
            if (!System.Runtime.Remoting.RemotingServices.IsObjectOutOfAppDomain(view) &&
                (view.GetType().Equals(typeof(BookInfoContractToViewHostAdapter))))
            {
                return ((BookInfoContractToViewHostAdapter)(view)).GetSourceContract();
            }
            else {
                return new BookInfoViewToContractHostAdapter(view);
            }
        }
    }
    }
    

Создание основного приложения

Основное приложение взаимодействует с надстройкой посредством представления основного приложения. При этом используются методы обнаружения и активации надстройки, которые предоставляются классами AddInStore и AddInToken для выполнения следующих действий:

  • Повторное создание кэша конвейера и сведений надстройки.

  • Обнаружение надстроек типа LibraryManager в указанном корневом каталоге конвейера.

  • Запрос пользователя о выборе используемой надстройки. В этом примере доступна только одна надстройка.

  • Активация выбранной надстройки в новом домене приложения с выбранным уровнем доверия.

  • Вызов метода ProcessBooks для передачи коллекции объектов BookInfo в надстройку. Надстройка вызывает свою реализацию метода ProcessBooks и выполняет функции, такие как снижение стоимости литературы о компьютерах на 20 процентов.

  • Вызов метода GetBestSeller который используется надстройкой для возвращения объекта BookInfo со сведениями о лидере продаж.

  • Вызов метода Data для получения текущей налоговой ставки из надстройки. Этот метод принимает и возвращает строку, которая является запечатанным, сериализованным ссылочным типом. В результате метод может быть передан через границы изоляции на другую сторону конвейера без использования адаптеров "представление-контракт" и "контракт-представление".

Основное приложение содержит метод CreateBooks, который создает коллекцию объектов BookInfo. Этот метод создает коллекцию посредством использования файла примера books.xml file, который доступен в 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 XML-кодом из Sample XML File (books.xml).

  3. В обозревателе решений выберите books.xml, затем в свойствах для пункта Копировать в выходной каталог задайте значение Всегда копировать.

Создание надстройки

Надстройка реализует методы, указанные в представлении надстройки. Эта надстройка реализует метод ProcessBooks. Метод выполняет приведенные ниже действия с коллекцией объектов BookInfo, которая передаются в него основным приложением:

  • Снижает цены на все книги, посвященные компьютерной тематике, на 20 процентов.

  • Удаляет все книги жанра ужасов из коллекции.

Эта надстройка также реализует метод GetBestSeller путем передачи в основное приложение объекта BookInfo, которое описывает книгу, являющуюся лидером продаж.

Чтобы создать надстройку

  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. Чтобы закрыть приложение, нажмите любую клавишу.

См. также

Задачи

Пошаговое руководство. Создание расширяемого приложения

Пошаговое руководство. Включение обратной совместимости при изменении основного приложения

Основные понятия

Требования к разработке конвейера

Контракты, представления и адаптеры

Разработка конвейера