Пошаговое руководство. Передача коллекций между основными приложениями и надстройками
В этом пошаговом руководстве описано, как создать конвейер для передачи коллекции пользовательских объектов между надстройкой и основным приложением. Так как типы в коллекции не могут быть сериализованы, дополнительные классы, которые определяют адаптеры "представление-контракт" и "контракт-представления, должны быть добавлены в сегменты адаптера, чтобы поток типов мог пересекать границы изоляции.
В этом сценарии надстройка обновляет коллекцию объектов книг для основного приложения. В каждом объекте книг содержатся методы, которые получают и задают название книги, издателя, стоимость и другие данные.
В качестве примера основное приложение создает коллекцию книг; надстройка уменьшает стоимость всех книг, посвященных компьютерам, на 20 процентов и удаляет из коллекции все книги жанра ужасов. Затем надстройка создает новый объект книги для наиболее продаваемой книги и передает его в основное приложение как один объект.
В данном пошаговом руководстве рассмотрены следующие задачи:
Создание решения в Visual Studio.
Создание структуры каталогов конвейера.
Создание контрактов и представлений для объектов, которые должны передаваться в обе стороны через границу изоляции.
Создание адаптеров на стороне надстройки и на стороне основного приложения, которые необходимы для передачи объектов через границы изоляции.
Создание основного приложения.
Создание надстройки.
Развертывание конвейера.
Запуск основного приложения.
Примечание |
---|
В коде, приведенном в этом пошаговом руководстве, содержатся ссылки на внешние пространства имен.Действия в этом пошаговом руководстве с точностью отражают ссылки, необходимые в Visual Studio. |
Дополнительные примеры кода и CTP-версии средств построения конвейеров надстроек см. на веб-узле CodePlex в разделе, посвященном управлению расширениями и надстройками.
Обязательные компоненты
Ниже приведены компоненты, необходимые для выполнения данного пошагового руководства.
Visual Studio.
Файл примера books.xml, который можно скопировать из Sample XML File (books.xml).
Создание решения в Visual Studio.
Используйте решение в Visual Studio, чтобы в нем содержались проекты сегментов конвейера.
Чтобы создать решение конвейера
В Visual Studio создайте новый проект с именем LibraryContracts. Он должен быть основан на шаблоне Библиотека классов.
Назовите решение BooksPipeline.
Создание структуры каталогов конвейера
Модель надстройки требует размещения сборок сегмента конвейера в определенной структуре каталогов.
Чтобы создать структуру каталогов конвейера
Создайте на своем компьютере приведенную ниже структуру папок. Эту структуру можно разместить где угодно, включая папки собственного решения Visual Studio.
Pipeline AddIns BooksAddIn AddInSideAdapters AddInViews Contracts HostSideAdapters
Все имена папок должны быть указаны так, как показано здесь, кроме имени корневой папки и имен отдельных папок надстройки. В этом примере в качестве имени корневой папки используется Pipeline, а в качестве имени папки надстройки — BooksAddIn.
Примечание Для удобства в этом пошаговом руководстве основное приложение размещено в корневой папке конвейера.На определенном этапе этого пошагового руководства объясняется, как изменить код, если приложение находится в другом месте.
Дополнительные сведения о структуре папок конвейера см. в разделе Требования к разработке конвейера.
Создание контракта и представлений
В сегменте контракта для этого конвейера определяются два интерфейса:
Интерфейс IBookInfoContract.
Этот интерфейс содержит методы, такие как Author, в которых присутствуют сведения о книге.
Интерфейс ILibraryManagerContract.
В этом интерфейсе содержится метод ProcessBooks, который используется надстройкой для обработки коллекции книг. Каждая книга представляет контракт IBookInfoContract. Также в этом интерфейсе содержится метод GetBestSeller, который используется надстройкой для предоставления основному приложению объекта книги, представляющего лидера продаж.
Чтобы создать контракт
В решении Visual Studio BooksPipeline откройте проект LibraryContracts.
В Visual Basic откройте Свойства для проекта LibraryContracts и на вкладке Приложение удалите значение по умолчанию, представленное для корневого пространства имен. По умолчанию значение корневого пространства имен равняется имени проекта.
В Обозревателе решений добавьте в проект ссылки на следующие сборки:
System.AddIn.Contract.dll
System.AddIn.dll
В файле класса добавьте ссылки на пространства имен System.AddIn.Contract и System.AddIn.Pipeline.
В файле класса замените объявление класса по умолчанию двумя интерфейсами:
Интерфейс ILibraryManagerContract используется для активации надстройки, поэтому в нем должен содержаться атрибут AddInContractAttribute.
Интерфейс IBookInfoContract представляет объект, который передается между основным приложением и надстройкой, поэтому для этого интерфейса не требуется атрибута.
Оба интерфейса должны наследовать интерфейс IContract.
С помощью следующего кода добавьте интерфейсы IBookInfoContract и ILibraryManagerContract.
Imports Microsoft.VisualBasic Imports System Imports System.Collections.Generic Imports System.Text Imports System.AddIn.Pipeline Imports System.AddIn.Contract Namespace Library <AddInContract> _ Public Interface ILibraryManagerContract Inherits IContract ' Pass a collection of books, ' of type IBookInfoContract ' to the add-in for processing. Sub ProcessBooks(ByVal books As IListContract(Of IBookInfoContract)) ' Get a IBookInfoContract object ' from the add-in of the ' the best selling book. Function GetBestSeller() As IBookInfoContract ' This method has has arbitrary ' uses and shows how you can ' mix serializable and custom types. Function Data(ByVal txt As String) As String End Interface ' Contains infomration about a book. Public Interface IBookInfoContract Inherits IContract Function ID() As String Function Author() As String Function Title() As String Function Genre() As String Function Price() As String Function Publish_Date() As String Function Description() As String End Interface End Namespace
using System; using System.Collections.Generic; using System.Text; using System.AddIn.Pipeline; using System.AddIn.Contract; namespace Library { [AddInContract] public interface ILibraryManagerContract : IContract { // Pass a collection of books, // of type IBookInfoContract // to the add-in for processing. void ProcessBooks(IListContract<IBookInfoContract> books); // Get a IBookInfoContract object // from the add-in of the // the best selling book. IBookInfoContract GetBestSeller(); // This method has has arbitrary // uses and shows how you can // mix serializable and custom types. string Data(string txt); } // Contains infomration about a book. public interface IBookInfoContract : IContract { string ID(); string Author(); string Title(); string Genre(); string Price(); string Publish_Date(); string Description(); } }
Так как в представлении надстройки и в представлении основного приложения используется один и тот же код, можно без труда создать оба представления одновременно. Они отличаются только в одном: представление настройки, используемое для активации этого сегмента конвейера, нуждается в атрибуте AddInBaseAttribute; представление основного приложения не нуждается ни в каких атрибутах.
Представление надстройки для этого конвейера содержит два абстрактных класса. Класс BookInfo обеспечивает представление для интерфейса IBookInfoContract, а класс LibraryManager обеспечивает представление для интерфейса ILibraryManagerContract.
Чтобы создать представление надстройки
Добавьте новый проект AddInViews в решение BooksPipeline. Он должен быть основан на шаблоне Библиотека классов.
В Visual Basic откройте Свойства для этого проекта и на вкладке Приложение удалите значение по умолчанию, приведенное для корневого пространства имен.
В обозревателе решений в проекте AddInViews добавьте ссылку на System.AddIn.dll.
Переименуйте класс по умолчанию LibraryManager для этого проекта и создайте класс abstract (MustInherit в Visual Basic).
В файле класса добавьте ссылку на пространство имен System.AddIn.Pipeline.
Класс LibraryManager используется для активации конвейера, поэтому следует использовать атрибут AddInBaseAttribute.
С помощью следующего кода завершите абстрактный класс LibraryManager.
Imports Microsoft.VisualBasic Imports System Imports System.Collections.Generic Imports System.AddIn.Pipeline Namespace LibraryContractsBase ' The AddInBaseAttribute ' identifes this pipeline ' segment as an add-in view. <AddInBase> _ Public MustInherit Class LibraryManager Public MustOverride Sub ProcessBooks(ByVal books As IList(Of BookInfo)) Public MustOverride Function GetBestSeller() As BookInfo Public MustOverride Function Data(ByVal txt As String) As String End Class End Namespace
using System; using System.Collections.Generic; using System.AddIn.Pipeline; namespace LibraryContractsBase { // The AddInBaseAttribute // identifes this pipeline // segment as an add-in view. [AddInBase] public abstract class LibraryManager { public abstract void ProcessBooks(IList<BookInfo> books); public abstract BookInfo GetBestSeller(); public abstract string Data(string txt); } }
Добавьте в проект класс abstract (класс MustInherit в Visual Basic) и назовите его BookInfo. Класс BookInfo представляет объект, который передается между основным приложением и надстройкой. Этот класс не используется для активации конвейера, поэтому он не нуждается в атрибутах.
Используйте следующий код для завершения абстрактного класса BookInfo.
Imports Microsoft.VisualBasic Imports System Namespace LibraryContractsBase Public MustInherit Class BookInfo Public MustOverride Function ID() As String Public MustOverride Function Author() As String Public MustOverride Function Title() As String Public MustOverride Function Genre() As String Public MustOverride Function Price() As String Public MustOverride Function Publish_Date() As String Public MustOverride Function Description() As String End Class End Namespace
using System; namespace LibraryContractsBase { public abstract class BookInfo { public abstract string ID(); public abstract string Author(); public abstract string Title(); public abstract string Genre(); public abstract string Price(); public abstract string Publish_Date(); public abstract string Description(); } }
Чтобы создать представление основного приложения
Добавьте новый проект под названием HostViews в решение BooksPipeline. Он должен быть основан на шаблоне Библиотека классов.
В Visual Basic откройте Свойства для этого проекта и на вкладке Приложение удалите значение по умолчанию, приведенное для корневого пространства имен.
Переименуйте класс по умолчанию LibraryManager для этого проекта и создайте класс abstract (MustInherit в Visual Basic).
С помощью следующего кода завершите абстрактный класс LibraryManager.
Imports Microsoft.VisualBasic Imports System.Collections.Generic Namespace LibraryContractsHAV Public MustInherit Class LibraryManager Public MustOverride Sub ProcessBooks(ByVal books As System.Collections.Generic.IList(Of BookInfo)) Public MustOverride Function GetBestSeller() As BookInfo Public MustOverride Function Data(ByVal txt As String) As String End Class End Namespace
using System.Collections.Generic; namespace LibraryContractsHAV { public abstract class LibraryManager { public abstract void ProcessBooks(System.Collections.Generic.IList<BookInfo> books); public abstract BookInfo GetBestSeller(); public abstract string Data(string txt); } }
Добавьте в проект класс abstract (класс MustInherit в Visual Basic) и назовите его BookInfo.
С помощью следующего кода завершите абстрактный класс BookInfo.
Imports Microsoft.VisualBasic Imports System Namespace LibraryContractsHAV Public MustInherit Class BookInfo Public MustOverride Function ID() As String Public MustOverride Function Author() As String Public MustOverride Function Title() As String Public MustOverride Function Genre() As String Public MustOverride Function Price() As String Public MustOverride Function Publish_Date() As String Public MustOverride Function Description() As String End Class End Namespace
namespace LibraryContractsHAV { public abstract class BookInfo { public abstract string ID(); public abstract string Author(); public abstract string Title(); public abstract string Genre(); public abstract string Price(); public abstract string Publish_Date(); public abstract string Description(); } }
Создание адаптера на стороне надстройки
Сборка адаптера надстройки для этого конвейера содержит четыре класса адаптера:
BookInfoContractToViewAddInAdapter
Этот адаптер вызывается при передаче основным приложением объекта BookInfo в надстройку или отдельно, или как часть коллекции. Этот класс преобразует контракт объекта BookInfo в представление. Класс наследует из представления надстройки и реализует абстрактные методы представления посредством вызова контракта, который передается в конструктор класса.
Конструктор этого адаптера принимает контракт таким образом, что объект ContractHandle может быть применен к контракту для реализации управления временем существования.
Важно Дескриптор ContractHandle играет ключевую роль в управлении временем существования.Если не сохранить ссылку на объект ContractHandle, он будет удален при сборке мусора, и конвейер закроется неожиданно для программы.Это может вызвать ошибки, которые сложно диагностировать, например AppDomainUnloadedException.Завершение работы является неотъемлемым этапом жизненного цикла конвейера, поэтому с помощью кода управления временем существования невозможно определить такое состояние как ошибку.
BookInfoViewToContractAddInAdapter
Этот адаптер вызывается при передаче надстройкой объекта BookInfo в основное приложение. Этот класс преобразует представление надстройки объекта BookInfo в контракт. Класс наследует из контракта и реализует контракт посредством вызова представления надстройки, которое передается в конструктор класса. Этот адаптер маршалируется в основное приложение в качестве контракта.
LibraryManagerViewToContractAddInAdapter
Этот тип возвращается в основное приложение после вызова для активации надстройки. Этот тип вызывается при вызове основного приложения в надстройку, включая вызов, который передает коллекцию объектов основного приложения (IList<BookInfo>) в надстройку. Этот класс преобразует контракт ILibraryManagerContract в представление основного приложения LibraryManager. Этот класс наследует из представления основного приложения и реализует контракт посредством вызова в представлении, которое передается в конструктор этого класса.
Так как коллекция пользовательских типов (объектов BookInfo) должна быть маршалирована через границы изоляции, этот адаптер использует класс CollectionAdapters. Этот класс предоставляет методы для преобразования коллекции IList<T> в коллекцию IListContract<T>, что позволяет передачу этой коллекцию через границы изоляции на другую сторону конвейера.
BookInfoAddInAdapter
Методы static (методы Shared в Visual Basic) этого адаптера вызываются классом LibraryManagerViewToContractAddInAdapter для адаптации контракта или представления, а также для возврата существующего контракта или представления. Это препятствует созданию дополнительного адаптера при циклическом прохождении объекта между основным приложением и надстройкой.
Чтобы создать адаптер на стороне надстройки
Добавьте в решение BooksPipeline новый проект под названием AddInSideAdapters. Он должен быть основан на шаблоне Библиотека классов.
В Visual Basic откройте Свойства для этого проекта и на вкладке Приложение удалите значение по умолчанию, приведенное для корневого пространства имен.
В Обозревателе решений в проект AddInSideAdapters добавьте ссылку на приведенные ниже сборки:
System.AddIn.dll
System.AddIn.Contract.dll
В Обозревателе решений добавьте ссылки на указанные ниже проекты в проект AddInSideAdapters.
AddInViews
LibraryContracts
Для этих ссылок выберите Свойства и установите для параметра Копировать локально значение False, чтобы сборки, на которые указывают ссылки, не копировались в папку локального построения. Сборки будут расположены в структуре каталога конвейера, как описано в разделе "Развертывание конвейера" далее в этом руководстве.
Назовите файл класса BookInfoContractToViewAddInAdapter.
В файле класса добавьте ссылку на пространство имен System.AddIn.Pipeline.
Используйте приведенный ниже код для добавления класса 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; } } }
Используйте следующий код для добавления класса 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; } } }
Используйте следующий код для добавления класса LibraryManagerViewToContractAddInAdapter в проект AddInSideAdapters. Этот класс нуждается в атрибуте AddInAdapterAttribute, так как он используется для активации конвейера.
Метод ProcessBooks показывает, как передавать список книг через границы изоляции. Для преобразования этого списка используется метод CollectionAdapters.ToIList. Чтобы преобразовать объекты в списке, передаются делегаты для методов адаптера, предоставленные классом BookInfoAddInAdapter.
Метод GetBestSeller показывает, как следует передать отдельный объект BookInfo через границы изоляции.
Imports Microsoft.VisualBasic Imports System.AddIn.Pipeline Imports System.AddIn.Contract Imports System.Collections.Generic Namespace LibraryContractsAddInAdapters ' The AddInAdapterAttribute ' identifes this pipeline ' segment as an add-in-side adapter. <AddInAdapter> _ Public Class LibraryManagerViewToContractAddInAdapter Inherits System.AddIn.Pipeline.ContractBase Implements Library.ILibraryManagerContract Private _view As LibraryContractsBase.LibraryManager Public Sub New(ByVal view As LibraryContractsBase.LibraryManager) _view = view End Sub Public Overridable Sub ProcessBooks(ByVal books As IListContract(Of Library.IBookInfoContract)) Implements Library.ILibraryManagerContract.ProcessBooks _view.ProcessBooks(CollectionAdapters.ToIList(Of Library.IBookInfoContract, _ LibraryContractsBase.BookInfo)(books, _ AddressOf LibraryContractsAddInAdapters.BookInfoAddInAdapter.ContractToViewAdapter, _ AddressOf LibraryContractsAddInAdapters.BookInfoAddInAdapter.ViewToContractAdapter)) End Sub Public Overridable Function GetBestSeller() As Library.IBookInfoContract Implements Library.ILibraryManagerContract.GetBestSeller Return BookInfoAddInAdapter.ViewToContractAdapter(_view.GetBestSeller()) End Function Public Overridable Function Data(ByVal txt As String) As String Implements Library.ILibraryManagerContract.Data Dim rtxt As String = _view.Data(txt) Return rtxt End Function Friend Function GetSourceView() As LibraryContractsBase.LibraryManager Return _view End Function End Class End Namespace
using System.IO; using System.AddIn.Pipeline; using System.AddIn.Contract; using System.Collections.Generic; namespace LibraryContractsAddInAdapters { // The AddInAdapterAttribute // identifes this pipeline // segment as an add-in-side adapter. [AddInAdapter] public class LibraryManagerViewToContractAddInAdapter : System.AddIn.Pipeline.ContractBase, Library.ILibraryManagerContract { private LibraryContractsBase.LibraryManager _view; public LibraryManagerViewToContractAddInAdapter(LibraryContractsBase.LibraryManager view) { _view = view; } public virtual void ProcessBooks(IListContract<Library.IBookInfoContract> books) { _view.ProcessBooks(CollectionAdapters.ToIList<Library.IBookInfoContract, LibraryContractsBase.BookInfo>(books, LibraryContractsAddInAdapters.BookInfoAddInAdapter.ContractToViewAdapter, LibraryContractsAddInAdapters.BookInfoAddInAdapter.ViewToContractAdapter)); } public virtual Library.IBookInfoContract GetBestSeller() { return BookInfoAddInAdapter.ViewToContractAdapter(_view.GetBestSeller()); } public virtual string Data(string txt) { string rtxt = _view.Data(txt); return rtxt; } internal LibraryContractsBase.LibraryManager GetSourceView() { return _view; } } }
Используйте следующий код для добавления класса BookInfoAddInAdapter в проект AddInSideAdapters. Этот класс содержит два метода static (методы 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 для возвращения любых существующих контрактов или представлений из адаптации вместо создания новых экземпляров для вызова. Это предотвращает создание дополнительного адаптера при циклическом прохождении объекта в любую сторону между основным приложением и надстройкой.
Чтобы создать адаптер на стороне основного приложения
Добавьте новый проект с названием HostSideAdapters в решение BooksPipeline. Он должен быть основан на шаблоне Библиотека классов.
В Visual Basic откройте Свойства для этого проекта и на вкладке Приложение удалите значение по умолчанию, приведенное для корневого пространства имен.
В Обозревателе решений добавьте ссылки на указанные ниже сборки в проект HostSideAdapters.
System.AddIn.dll
System.AddIn.Contract.dll
В Обозревателе решений добавьте ссылки на следующие проекты в проект HostSideAdapters:
HostViews
LibraryContracts
Для этих ссылок выберите Свойства и установите для параметра Копировать локально значение False, чтобы сборки, на которые указывают ссылки, не копировались в папку локального построения.
В файле класса добавьте ссылку на пространство имен System.AddIn.Pipeline.
Используйте приведенный ниже код для добавления класса 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; } } }
Используйте приведенный ниже код для добавления 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; } } }
Используйте приведенный ниже код для добавления LibraryManagerContractToViewHostAdapter в проект HostSideAdapters. Этот класс нуждается в атрибуте HostAdapterAttribute, так как он используется для активации конвейера.
Метод ProcessBooks показывает, как передавать список книг через границы изоляции. Для преобразования этого списка используется метод CollectionAdapters.ToIListContract. Чтобы преобразовать объекты в списке, передаются делегаты для методов адаптера, предоставленные классом BookInfoHostAdapter.
Метод GetBestSeller показывает, как следует передать отдельный объект BookInfo через границы изоляции.
Imports Microsoft.VisualBasic Imports System.Collections.Generic Imports System.AddIn.Pipeline Namespace LibraryContractsHostAdapters <HostAdapterAttribute()> _ Public Class LibraryManagerContractToViewHostAdapter Inherits LibraryContractsHAV.LibraryManager Private _contract As Library.ILibraryManagerContract Private _handle As System.AddIn.Pipeline.ContractHandle Public Sub New(ByVal contract As Library.ILibraryManagerContract) _contract = contract _handle = New System.AddIn.Pipeline.ContractHandle(contract) End Sub Public Overrides Sub ProcessBooks(ByVal books As IList(Of LibraryContractsHAV.BookInfo)) _contract.ProcessBooks(CollectionAdapters.ToIListContract(Of LibraryContractsHAV.BookInfo, _ Library.IBookInfoContract)(books, _ AddressOf LibraryContractsHostAdapters.BookInfoHostAdapter.ViewToContractAdapter, _ AddressOf LibraryContractsHostAdapters.BookInfoHostAdapter.ContractToViewAdapter)) End Sub Public Overrides Function GetBestSeller() As LibraryContractsHAV.BookInfo Return BookInfoHostAdapter.ContractToViewAdapter(_contract.GetBestSeller()) End Function Friend Function GetSourceContract() As Library.ILibraryManagerContract Return _contract End Function Public Overrides Function Data(ByVal txt As String) As String Dim rtxt As String = _contract.Data(txt) Return rtxt End Function End Class End Namespace
using System.Collections.Generic; using System.AddIn.Pipeline; namespace LibraryContractsHostAdapters { [HostAdapterAttribute()] public class LibraryManagerContractToViewHostAdapter : LibraryContractsHAV.LibraryManager { private Library.ILibraryManagerContract _contract; private System.AddIn.Pipeline.ContractHandle _handle; public LibraryManagerContractToViewHostAdapter(Library.ILibraryManagerContract contract) { _contract = contract; _handle = new System.AddIn.Pipeline.ContractHandle(contract); } public override void ProcessBooks(IList<LibraryContractsHAV.BookInfo> books) { _contract.ProcessBooks(CollectionAdapters.ToIListContract<LibraryContractsHAV.BookInfo, Library.IBookInfoContract>(books, LibraryContractsHostAdapters.BookInfoHostAdapter.ViewToContractAdapter, LibraryContractsHostAdapters.BookInfoHostAdapter.ContractToViewAdapter)); } public override LibraryContractsHAV.BookInfo GetBestSeller() { return BookInfoHostAdapter.ContractToViewAdapter(_contract.GetBestSeller()); } internal Library.ILibraryManagerContract GetSourceContract() { return _contract; } public override string Data(string txt) { string rtxt = _contract.Data(txt); return rtxt; } } }
Используйте приведенный ниже код для добавления класса BookInfoHostAdapter в проект HostSideAdapters. Этот класс содержит два метода static (методы 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).
Чтобы создать основное приложение
Добавьте новый проект с названием BookStore в решение BooksPipeline. Он должен быть основан на шаблоне Консольное приложение.
В Visual Basic откройте Свойства для этого проекта и на вкладке Приложение удалите значение по умолчанию, приведенное для корневого пространства имен.
В Обозревателе решений добавьте ссылку на сборку System.AddIn.dll в проект BookStore.
Добавьте ссылку проекта в проект HostViews. Для данной ссылки выберите Свойства и установите для параметра Копировать локально значение False, чтобы сборка, на которую указывает ссылка, не копировалась в папку локального построения.
В Visual Basic измените модуль на класс:
Исключите модуль по умолчанию из проекта, затем добавьте класс с именем Program.
Замените ключевое слово Public на ключевое слово Friend.
Добавьте процедуру Shared Sub Main() в класс.
На вкладке Приложение в диалоговом окне Свойства проекта можно задать для пункта Автоматически запускаемый объект значение Sub Main.
В файле класса добавьте ссылки на пространства имен System.AddIn.Pipeline и сегмента представления основного приложения.
В Обозревателе решений выберите решение, затем в меню Проект выберите Свойства. В диалоговом окне Страницы свойств решения задайте Один запускаемый объект как этот проект основного приложения.
Используйте приведенный ниже код для основного приложения.
Примечание В этом коде измените расположение, из которого будет загружаться файл books.xml загружается в "books.xml", чтобы файл загружался из папки приложения.Если следует разместить приложение в расположении, отличном от папки Pipeline, измените строку кода, в которой задается значение переменной addInRoot, чтобы эта переменная содержала путь к структуре каталогов конвейера.
Imports Microsoft.VisualBasic Imports System Imports System.Collections.Generic Imports System.Collections.ObjectModel Imports System.Text Imports LibraryContractsHAV Imports System.AddIn.Hosting Imports System.Xml Namespace ListAdaptersHost Friend Class Program Shared Sub Main(ByVal args As String()) ' In this example, the pipeline root is the current directory. Dim pipeRoot As String = Environment.CurrentDirectory ' Rebuild the cache of pipeline and add-in information. Dim warnings As String() = AddInStore.Update(pipeRoot) If warnings.Length > 0 Then For Each one As String In warnings Console.WriteLine(one) Next one End If ' Find add-ins of type LibraryManager under the specified pipeline root directory. Dim tokens As Collection(Of AddInToken) = AddInStore.FindAddIns(GetType(LibraryManager), pipeRoot) ' Determine which add-in to use. Dim selectedToken As AddInToken = ChooseAddIn(tokens) ' Activate the selected AddInToken in a new ' application domain with a specified security trust level. Dim manager As LibraryManager = selectedToken.Activate(Of LibraryManager)(AddInSecurityLevel.FullTrust) ' Create a collection of books. Dim books As IList(Of BookInfo) = CreateBooks() ' Show the collection count. Console.WriteLine("Number of books: {0}",books.Count.ToString()) ' Have the add-in process the books. ' The add-in will discount computer books by $20 ' and list their before and after prices. It ' will also remove all horror books. manager.ProcessBooks(books) ' List the genre of each book. There ' should be no horror books. For Each bk As BookInfo In books Console.WriteLine(bk.Genre()) Next bk Console.WriteLine("Number of books: {0}", books.Count.ToString()) Console.WriteLine() ' Have the add-in pass a BookInfo object ' of the best selling book. Dim bestBook As BookInfo = manager.GetBestSeller() Console.WriteLine("Best seller is {0} by {1}", bestBook.Title(), bestBook.Author()) ' Have the add-in show the sales tax rate. manager.Data("sales tax") Dim ctrl As AddInController = AddInController.GetAddInController(manager) ctrl.Shutdown() Console.WriteLine("Press any key to exit.") Console.ReadLine() End Sub Private Shared Function ChooseAddIn(ByVal tokens As Collection(Of AddInToken)) As AddInToken If tokens.Count = 0 Then Console.WriteLine("No add-ins of this type are available") Return Nothing End If Console.WriteLine("{0} Available add-in(s):",tokens.Count.ToString()) For i As Integer = 0 To tokens.Count - 1 ' Show AddInToken properties. Console.WriteLine("[{0}] - {1}, Publisher: {2}, Version: {3}, Description: {4}", (i + 1).ToString(), tokens(i).Name, tokens(i).Publisher, tokens(i).Version, tokens(i).Description) Next i Console.WriteLine("Select add-in by number:") Dim line As String = Console.ReadLine() Dim selection As Integer If Int32.TryParse(line, selection) Then If selection <= tokens.Count Then Return tokens(selection - 1) End If End If Console.WriteLine("Invalid selection: {0}. Please choose again.", line) Return ChooseAddIn(tokens) End Function Friend Shared Function CreateBooks() As IList(Of BookInfo) Dim books As List(Of BookInfo) = New List(Of BookInfo)() Dim ParamId As String = "" Dim ParamAuthor As String = "" Dim ParamTitle As String = "" Dim ParamGenre As String = "" Dim ParamPrice As String = "" Dim ParamPublish_Date As String = "" Dim ParamDescription As String = "" Dim xDoc As XmlDocument = New XmlDocument() xDoc.Load("c:\Books.xml") Dim xRoot As XmlNode = xDoc.DocumentElement If xRoot.Name = "catalog" Then Dim bklist As XmlNodeList = xRoot.ChildNodes For Each bk As XmlNode In bklist ParamId = bk.Attributes(0).Value Dim dataItems As XmlNodeList = bk.ChildNodes Dim items As Integer = dataItems.Count For Each di As XmlNode In dataItems Select Case di.Name Case "author" ParamAuthor = di.InnerText Case "title" ParamTitle = di.InnerText Case "genre" ParamGenre = di.InnerText Case "price" ParamPrice = di.InnerText Case "publish_date" ParamAuthor = di.InnerText Case "description" ParamDescription = di.InnerText Case Else End Select Next di books.Add(New MyBookInfo(ParamId, ParamAuthor, ParamTitle, ParamGenre, ParamPrice, ParamPublish_Date, ParamDescription)) Next bk End If Return books End Function End Class Friend Class MyBookInfo Inherits BookInfo Private _id As String Private _author As String Private _title As String Private _genre As String Private _price As String Private _publish_date As String Private _description As String Public Sub New(ByVal id As String, ByVal author As String, ByVal title As String, ByVal genre As String, ByVal price As String, ByVal publish_date As String, ByVal description As String) _id = id _author = author _title = title _genre = genre _price = price _publish_date = publish_date _description = description End Sub Public Overrides Function ID() As String Return _id End Function Public Overrides Function Title() As String Return _title End Function Public Overrides Function Author() As String Return _author End Function Public Overrides Function Genre() As String Return _genre End Function Public Overrides Function Price() As String Return _price End Function Public Overrides Function Publish_Date() As String Return _publish_date End Function Public Overrides Function Description() As String Return _description End Function End Class End Namespace
using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Text; using LibraryContractsHAV; using System.AddIn.Hosting; using System.Xml; namespace ListAdaptersHost { class Program { static void Main(string[] args) { // In this example, the pipeline root is the current directory. String pipeRoot = Environment.CurrentDirectory; // Rebuild the cache of pipeline and add-in information. string[] warnings = AddInStore.Update(pipeRoot); if (warnings.Length > 0) { foreach (string one in warnings) { Console.WriteLine(one); } } // Find add-ins of type LibraryManager under the specified pipeline root directory. Collection<AddInToken> tokens = AddInStore.FindAddIns(typeof(LibraryManager), pipeRoot); // Determine which add-in to use. AddInToken selectedToken = ChooseAddIn(tokens); // Activate the selected AddInToken in a new // application domain with a specified security trust level. LibraryManager manager = selectedToken.Activate<LibraryManager>(AddInSecurityLevel.FullTrust); // Create a collection of books. IList<BookInfo> books = CreateBooks(); // Show the collection count. Console.WriteLine("Number of books: {0}",books.Count.ToString()); // Have the add-in process the books. // The add-in will discount computer books by $20 // and list their before and after prices. It // will also remove all horror books. manager.ProcessBooks(books); // List the genre of each book. There // should be no horror books. foreach (BookInfo bk in books) { Console.WriteLine(bk.Genre()); } Console.WriteLine("Number of books: {0}", books.Count.ToString()); Console.WriteLine(); // Have the add-in pass a BookInfo object // of the best selling book. BookInfo bestBook = manager.GetBestSeller(); Console.WriteLine("Best seller is {0} by {1}", bestBook.Title(), bestBook.Author()); // Have the add-in show the sales tax rate. manager.Data("sales tax"); AddInController ctrl = AddInController.GetAddInController(manager); ctrl.Shutdown(); Console.WriteLine("Press any key to exit."); Console.ReadLine(); } private static AddInToken ChooseAddIn(Collection<AddInToken> tokens) { if (tokens.Count == 0) { Console.WriteLine("No add-ins of this type are available"); return null; } Console.WriteLine("{0} Available add-in(s):",tokens.Count.ToString()); for (int i = 0; i < tokens.Count; i++) { // Show AddInToken properties. Console.WriteLine("[{0}] - {1}, Publisher: {2}, Version: {3}, Description: {4}", (i + 1).ToString(), tokens[i].Name, tokens[i].Publisher, tokens[i].Version, tokens[i].Description); } Console.WriteLine("Select add-in by number:"); String line = Console.ReadLine(); int selection; if (Int32.TryParse(line, out selection)) { if (selection <= tokens.Count) { return tokens[selection - 1]; } } Console.WriteLine("Invalid selection: {0}. Please choose again.", line); return ChooseAddIn(tokens); } internal static IList<BookInfo> CreateBooks() { List<BookInfo> books = new List<BookInfo>(); string ParamId = ""; string ParamAuthor = ""; string ParamTitle = ""; string ParamGenre = ""; string ParamPrice = ""; string ParamPublish_Date = ""; string ParamDescription = ""; XmlDocument xDoc = new XmlDocument(); xDoc.Load(@"c:\Books.xml"); XmlNode xRoot = xDoc.DocumentElement; if (xRoot.Name == "catalog") { XmlNodeList bklist = xRoot.ChildNodes; foreach (XmlNode bk in bklist) { ParamId = bk.Attributes[0].Value; XmlNodeList dataItems = bk.ChildNodes; int items = dataItems.Count; foreach (XmlNode di in dataItems) { switch (di.Name) { case "author": ParamAuthor = di.InnerText; break; case "title": ParamTitle = di.InnerText; break; case "genre": ParamGenre = di.InnerText; break; case "price": ParamPrice = di.InnerText; break; case "publish_date": ParamAuthor = di.InnerText; break; case "description": ParamDescription = di.InnerText; break; default: break; } } books.Add(new MyBookInfo(ParamId, ParamAuthor, ParamTitle, ParamGenre, ParamPrice, ParamPublish_Date, ParamDescription)); } } return books; } } class MyBookInfo : BookInfo { private string _id; private string _author; private string _title; private string _genre; private string _price; private string _publish_date; private string _description; public MyBookInfo(string id, string author, string title, string genre, string price, string publish_date, string description) { _id = id; _author = author; _title = title; _genre = genre; _price = price; _publish_date = publish_date; _description = description; } public override string ID() { return _id; } public override string Title() { return _title; } public override string Author() { return _author; } public override string Genre() { return _genre; } public override string Price() { return _price; } public override string Publish_Date() { return _publish_date; } public override string Description() { return _description; } } }
Чтобы создать файл данных books.xml
Добавьте новый XML-файл в проект BookStore. В диалоговом окне Добавление нового элемента назовите этот файл books.xml.
Замените содержимое по умолчанию файла books.xml XML-кодом из Sample XML File (books.xml).
В обозревателе решений выберите books.xml, затем в свойствах для пункта Копировать в выходной каталог задайте значение Всегда копировать.
Создание надстройки
Надстройка реализует методы, указанные в представлении надстройки. Эта надстройка реализует метод ProcessBooks. Метод выполняет приведенные ниже действия с коллекцией объектов BookInfo, которая передаются в него основным приложением:
Снижает цены на все книги, посвященные компьютерной тематике, на 20 процентов.
Удаляет все книги жанра ужасов из коллекции.
Эта надстройка также реализует метод GetBestSeller путем передачи в основное приложение объекта BookInfo, которое описывает книгу, являющуюся лидером продаж.
Чтобы создать надстройку
Добавьте новый проект с названием BooksAddin в решение BooksPipeline. Он должен быть основан на шаблоне Библиотека классов.
В Visual Basic откройте Свойства для этого проекта и на вкладке Приложение удалите значение по умолчанию, приведенное для корневого пространства имен.
В Обозревателе решений добавьте ссылку на сборку System.AddIn.dll в проект BooksAddin.
Добавьте ссылку на проект в проект AddInViews. Для данной ссылки выберите Свойства и установите для параметра Копировать локально значение False, чтобы сборка, на которую указывает ссылка, не копировалась в папку локального построения.
В файле класса добавьте ссылки на пространства имен System.AddIn и сегмента представления надстройки.
Для приложения надстройки используйте следующий код:
Imports Microsoft.VisualBasic Imports System Imports System.Collections.Generic Imports System.Text Imports LibraryContractsBase Imports System.AddIn Imports System.IO Namespace SampleAddIn <AddIn("Books AddIn",Version:="1.0.0.0")> _ Public Class BooksAddIn Inherits LibraryManager ' Calls methods that updates book data ' and removes books by genre. Public Overrides Sub ProcessBooks(ByVal books As IList(Of BookInfo)) For i As Integer = 0 To books.Count - 1 books(i) = UpdateBook(books(i)) Next i RemoveGenre("horror", books) End Sub Public Overrides Function Data(ByVal txt As String) As String ' assumes txt = "sales tax" Dim rtxt As String = txt & "= 8.5%" Return rtxt End Function Friend Shared Function RemoveGenre(ByVal genre As String, ByVal books As IList(Of BookInfo)) As IList(Of BookInfo) ' Remove all horror books from the collection. Dim i As Integer = 0 Do While i < books.Count If books(i).Genre().ToLower() = "horror" Then books.RemoveAt(i) End If i += 1 Loop Return books End Function ' Populate a BookInfo object with data ' about the best selling book. Public Overrides Function GetBestSeller() As BookInfo Dim ParamId As String = "bk999" Dim ParamAuthor As String = "Corets, Eva" Dim ParamTitle As String = "Cooking with Oberon" Dim ParamGenre As String = "Cooking" Dim ParamPrice As String = "7.95" Dim ParamPublish_Date As String = "2006-12-01" Dim ParamDescription As String = "Recipes for a post-apocalyptic society." Dim bestBook As MyBookInfo = New MyBookInfo(ParamId, ParamAuthor, ParamTitle, ParamGenre, ParamPrice, ParamPublish_Date, ParamDescription) Return bestBook End Function Friend Shared Function UpdateBook(ByVal bk As BookInfo) As BookInfo ' Discounts the price of all ' computer books by 20 percent. Dim ParamId As String = bk.ID() Dim ParamAuthor As String = bk.Author() Dim ParamTitle As String = bk.Title() Dim ParamGenre As String = bk.Genre() Dim ParamPrice As String = bk.Price() If ParamGenre.ToLower() = "computer" Then Dim oldprice As Double = Convert.ToDouble(ParamPrice) Dim newprice As Double = oldprice - (oldprice *.20) ParamPrice = newprice.ToString() If ParamPrice.IndexOf(".") = ParamPrice.Length - 4 Then ParamPrice = ParamPrice.Substring(1, ParamPrice.Length - 1) End If Console.WriteLine("{0} - Old Price: {1}, New Price: {2}",ParamTitle,oldprice.ToString(),ParamPrice) End If Dim ParamPublish_Date As String = bk.Publish_Date() Dim ParamDescription As String = bk.Description() Dim bookUpdated As BookInfo = New MyBookInfo(ParamId, ParamAuthor, ParamTitle, ParamGenre, ParamPrice, ParamPublish_Date, ParamDescription) Return bookUpdated End Function End Class ' Creates a BookInfo object. Friend Class MyBookInfo Inherits BookInfo Private _id As String Private _author As String Private _title As String Private _genre As String Private _price As String Private _publish_date As String Private _description As String Public Sub New(ByVal id As String, ByVal author As String, ByVal title As String, ByVal genre As String, ByVal price As String, ByVal publish_date As String, ByVal description As String) _id = id _author = author _title = title _genre = genre _price = price _publish_date = publish_date _description = description End Sub Public Overrides Function ID() As String Return _id End Function Public Overrides Function Title() As String Return _title End Function Public Overrides Function Author() As String Return _author End Function Public Overrides Function Genre() As String Return _genre End Function Public Overrides Function Price() As String Return _price End Function Public Overrides Function Publish_Date() As String Return _publish_date End Function Public Overrides Function Description() As String Return _description End Function End Class End Namespace
using System; using System.Collections.Generic; using System.Text; using LibraryContractsBase; using System.AddIn; using System.IO; namespace BooksAddIn { [AddIn("Books AddIn",Description="Book Store Data", Publisher="Microsoft",Version="1.0.0.0")] public class BooksAddIn : LibraryManager { // Calls methods that updates book data // and removes books by their genre. public override void ProcessBooks(IList<BookInfo> books) { for (int i = 0; i < books.Count; i++) { books[i] = UpdateBook(books[i]); } RemoveGenre("horror", books); } public override string Data(string txt) { // assumes txt = "sales tax" string rtxt = txt + "= 8.5%"; return rtxt; } internal static IList<BookInfo> RemoveGenre(string genre, IList<BookInfo> books) { // Remove all horror books from the collection. for (int i = 0; i < books.Count; i++) { if (books[i].Genre().ToLower() == "horror") books.RemoveAt(i); } return books; } // Populate a BookInfo object with data // about the best selling book. public override BookInfo GetBestSeller() { string ParamId = "bk999"; string ParamAuthor = "Corets, Eva"; string ParamTitle = "Cooking with Oberon"; string ParamGenre = "Cooking"; string ParamPrice = "7.95"; string ParamPublish_Date = "2006-12-01"; string ParamDescription = "Recipes for a post-apocalyptic society."; MyBookInfo bestBook = new MyBookInfo(ParamId, ParamAuthor, ParamTitle, ParamGenre, ParamPrice, ParamPublish_Date, ParamDescription); return bestBook; } internal static BookInfo UpdateBook(BookInfo bk) { // Discounts the price of all // computer books by 20 percent. string ParamId = bk.ID(); string ParamAuthor = bk.Author(); string ParamTitle = bk.Title(); string ParamGenre = bk.Genre(); string ParamPrice = bk.Price(); if (ParamGenre.ToLower() == "computer") { double oldprice = Convert.ToDouble(ParamPrice); double newprice = oldprice - (oldprice * .20); ParamPrice = newprice.ToString(); if (ParamPrice.IndexOf(".") == ParamPrice.Length - 4) ParamPrice = ParamPrice.Substring(1, ParamPrice.Length - 1); Console.WriteLine("{0} - Old Price: {1}, New Price: {2}",ParamTitle,oldprice.ToString(),ParamPrice); } string ParamPublish_Date = bk.Publish_Date(); string ParamDescription = bk.Description(); BookInfo bookUpdated = new MyBookInfo(ParamId, ParamAuthor, ParamTitle, ParamGenre, ParamPrice, ParamPublish_Date, ParamDescription); return bookUpdated; } } // Creates a BookInfo object. class MyBookInfo : BookInfo { private string _id; private string _author; private string _title; private string _genre; private string _price; private string _publish_date; private string _description; public MyBookInfo(string id, string author, string title, string genre, string price, string publish_date, string description) { _id = id; _author = author; _title = title; _genre = genre; _price = price; _publish_date = publish_date; _description = description; } public override string ID() { return _id; } public override string Title() { return _title; } public override string Author() { return _author; } public override string Genre() { return _genre; } public override string Price() { return _price; } public override string Publish_Date() { return _publish_date; } public override string Description() { return _description; } } }
Развертывание конвейера
Теперь можно создать и развернуть сегменты надстройки в требуемой структуре каталогов конвейера.
Чтобы развернуть сегменты в конвейере
Для каждого проекта в решении используйте вкладку Построение в окне Свойства проекта (вкладка Компиляция в Visual Basic) для установки значения Путь вывода (Путь вывода построения в Visual Basic), как показано в следующей таблице.
Проект
Путь
BooksAddIn
Pipeline\AddIns\CalcV1
AddInSideAdapters
Pipeline\AddInSideAdapters
AddInViews
Pipeline\AddInViews
LibraryContracts
Pipeline\Contracts
BookStore
Pipeline (или собственный каталог приложения)
HostSideAdapters
Pipeline\HostSideAdapters
HostViews
Pipeline (или собственный каталог приложения)
Примечание Если следует разместить приложение в месте, отличном от папки Pipeline, необходимо изменить код основного приложения, который указывает расположение корневого каталога конвейера.
Создайте решение Visual Studio.
Сведения о развертывании в конвейере см. в разделе Требования к разработке конвейера.
Запуск основного приложения
Теперь все готово к запуску основного приложения и взаимодействию с надстройкой.
Чтобы выполнить основное приложение
В командной строке перейдите в корневой каталог конвейера и запустите основное приложение. В этом примере основным приложением является BookStore.exe.
Основное приложение находит все доступные надстройки этого типа и запрашивает пользователя о выборе надстройки. Введите 1 для единственной доступной надстройки.
Основное приложение активирует надстройку и использует ее для выполнения нескольких операций со списком книг.
Чтобы закрыть приложение, нажмите любую клавишу.
См. также
Задачи
Пошаговое руководство. Создание расширяемого приложения
Пошаговое руководство. Включение обратной совместимости при изменении основного приложения
Основные понятия
Требования к разработке конвейера