Tutorial: Pasar colecciones entre hosts y complementos
En este tutorial se describe cómo se crea una canalización que pasa una colección de objetos personalizados entre un complemento y un host. Dado que los tipos de la colección no son serializables, deben agregarse a los segmentos de adaptador clases adicionales que definan los adaptadores de vista a contrato y de contrato a vista para que el flujo de tipos pueda atravesar el límite de aislamiento.
En este escenario, el complemento actualiza una colección de objetos de libro para el host. Cada objeto de libro contiene métodos que obtienen y establecen el título del libro, la editorial, el precio y otros datos.
A modo de demostración, el host crea una colección de libros; el complemento reduce el precio de todos los libros de informática en un 20 por ciento y quita todos los libros de terror de la colección. A continuación, el complemento crea un nuevo objeto de libro para el libro con mayor número de ventas y lo pasa al host como un único objeto.
En este tutorial se muestran las tareas siguientes:
Crear una solución de Visual Studio.
Crear la estructura de directorios de canalización.
Crear los contratos y las vistas para los objetos que deben pasarse de un lado a otro del límite de aislamiento.
Crear los adaptadores del complemento y el host necesarios para pasar los objetos a través del límite de aislamiento.
Crear el host.
Crear el complemento.
Implementar la canalización.
Ejecutar la aplicación host.
Nota |
---|
Parte del código que se incluye en este tutorial contiene referencias a espacios de nombres poco comunes.En los pasos del tutorial se reflejan exactamente las referencias que son necesarias en Visual Studio. |
Puede encontrar más código de ejemplo y vistas previas de herramientas con la tecnología de clientes para compilar canalizaciones de complementos en el sitio Managed Extensibility and Add-In Framework de CodePlex.
Requisitos previos
Necesita los componentes siguientes para completar este tutorial:
Visual Studio.
El archivo de ejemplo de books.xml, que se puede copiar de Sample XML File (books.xml).
Crear una solución de Visual Studio
Utilice una solución de Visual Studio que contenga los proyectos de los segmentos de canalización.
Para crear la solución de canalización
En Visual Studio, cree un nuevo proyecto denominado LibraryContracts. Utilice como base la plantilla Biblioteca de clases.
Llame a la solución BooksPipeline.
Crear la estructura de directorios de canalización
El modelo de complementos requiere que los ensamblados de los segmentos de canalización se sitúen en una estructura de directorios específica.
Para crear la estructura de directorios de canalización
Cree la siguiente estructura de carpetas en su equipo. Puede situarla donde quiera, incluso en las carpetas de la solución de Visual Studio.
Pipeline AddIns BooksAddIn AddInSideAdapters AddInViews Contracts HostSideAdapters
Todos los nombres de carpeta deben especificarse tal y como se indica aquí, a excepción del nombre de la carpeta raíz y los nombres de las carpetas de cada complemento. En este ejemplo se utiliza Pipeline como nombre de la carpeta raíz y BooksAddIn como nombre de la carpeta del complemento.
Nota Por comodidad, en el tutorial la aplicación host se sitúa en la carpeta raíz de la canalización.En el paso correspondiente del tutorial, se explica cómo debe cambiarse el código si la aplicación se encuentra en una ubicación diferente.
Para obtener más información sobre la estructura de carpetas de la canalización, vea Requisitos del desarrollo de canalizaciones.
Crear el contrato y las vistas
El segmento del contrato de esta canalización define dos interfaces:
La interfaz IBookInfoContract.
Esta interfaz contiene métodos, como Author, que albergan información sobre un libro.
La interfaz ILibraryManagerContract.
Esta interfaz contiene el método ProcessBooks que el complemento utiliza para procesar una colección de libros. Cada libro representa un contrato IBookInfoContract. La interfaz también contiene el método GetBestSeller, que el complemento utiliza para proporcionar al host un objeto de libro que representa el libro con mayor número de ventas.
Para crear el contrato
En la solución de Visual Studio denominada BooksPipeline, abra el proyecto LibraryContracts.
En Visual Basic, abra Propiedades del proyecto LibraryContracts y use la ficha Aplicación para eliminar el valor predeterminado proporcionado para Espacio de nombres de la raíz. De forma predeterminada, Espacio de nombres de la raíz está establecido en el nombre del proyecto.
En el Explorador de soluciones, agregue referencias de los siguientes ensamblados al proyecto:
Sytem.AddIn.Contract.dll
System.AddIn.dll
En el archivo de clase, agregue referencias a los espacios de nombres System.AddIn.Contract y System.AddIn.Pipeline.
En el archivo de clase, reemplace la declaración de clase predeterminada por dos interfaces:
La interfaz ILibraryManagerContract se utiliza para activar el complemento, por lo que debe tener el atributo AddInContractAttribute.
La interfaz IBookInfoContract representa un objeto que se pasa entre el host y el complemento, así que no necesita el atributo.
Ambas interfaces deben heredar la interfaz IContract.
Utilice el código siguiente para agregar las interfaces IBookInfoContract e 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(); } }
Dado que la vista de complemento y la vista de host tienen el mismo código, resulta sencillo crearlas al mismo tiempo. Sólo se diferencian en un aspecto: la vista de complemento que se utiliza para activar este segmento de la canalización necesita el atributo AddInBaseAttribute, mientras que la vista de host no necesita ningún atributo.
La vista de complemento de esta canalización contiene dos clases abstractas. La clase BookInfo proporciona la vista para la interfaz IBookInfoContract y la clase LibraryManager proporciona la vista para la interfaz ILibraryManagerContract.
Para crear la vista de complemento
Agregue un nuevo proyecto denominado AddInViews a la solución BooksPipeline. Utilice como base la plantilla Biblioteca de clases.
En Visual Basic, abra la ventana Propiedades del proyecto y use la ficha Aplicación para eliminar el valor predeterminado proporcionado para Espacio de nombres de la raíz.
En el Explorador de soluciones, agregue una referencia de System.AddIn.dll al proyecto AddInViews.
Cambie el nombre de la clase predeterminada del proyecto a LibraryManager y conviértala en una clase abstract (MustInherit en Visual Basic).
En el archivo de clase, agregue una referencia al espacio de nombres System.AddIn.Pipeline.
La clase LibraryManager se utiliza para activar la canalización, por lo que debe aplicar el atributo AddInBaseAttribute.
Utilice el código siguiente para completar la clase abstracta 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); } }
Agregue una clase abstract (una clase MustInherit en Visual Basic) al proyecto y denomínela BookInfo. La clase BookInfo representa un objeto que se pasa entre el host y el complemento. Esta clase no se utiliza para activar la canalización, por lo que no necesita ningún atributo.
Utilice el código siguiente para completar la clase abstracta 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(); } }
Para crear la vista de host
Agregue un nuevo proyecto denominado HostViews a la solución BooksPipeline. Utilice como base la plantilla Biblioteca de clases.
En Visual Basic, abra la ventana Propiedades del proyecto y use la ficha Aplicación para eliminar el valor predeterminado proporcionado para Espacio de nombres de la raíz.
Cambie el nombre de la clase predeterminada del proyecto a LibraryManager y conviértala en una clase abstract (MustInherit en Visual Basic).
Utilice el código siguiente para completar la clase abstracta 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); } }
Agregue una clase abstract (una clase MustInherit en Visual Basic) al proyecto y denomínela BookInfo.
Utilice el código siguiente para completar la clase abstracta 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(); } }
Crear el adaptador de conversión
El ensamblado del adaptador de conversión de esta canalización contiene cuatro clases de adaptadores:
BookInfoContractToViewAddInAdapter
Se llama a este adaptador cuando el host pasa un objeto BookInfo al complemento, en solitario o como parte de una colección. Esta clase convierte el contrato del objeto BookInfo en una vista. La clase hereda de la vista de complemento e implementa los métodos abstractos de la vista llamando al contrato que se pasa al constructor de la clase.
El constructor de este adaptador toma un contrato, por lo que se puede aplicar un objeto ContractHandle al contrato para implementar la administración de la duración.
Importante El objeto ContractHandle resulta crítico para la administración de la duración.Si no puede mantener una referencia al objeto ContractHandle, la recolección de elementos no utilizados lo reclamará y la canalización se cerrará cuando el programa no se lo espere.Esto puede dar lugar a errores difíciles de diagnosticar, como AppDomainUnloadedException.El cierre es una etapa normal del proceso de canalización, por lo que no hay ninguna forma de que el código de administración de la duración detecte que esta condición es un error.
BookInfoViewToContractAddInAdapter
Se llama a este adaptador cuando el complemento pasa un objeto BookInfo al host. Esta clase convierte la vista de complemento del objeto BookInfo en un contrato. La clase hereda del contrato e implementa el contrato llamando a la vista de complemento que se pasa al constructor de la clase. Las referencias de este adaptador se calculan en el host como un contrato.
LibraryManagerViewToContractAddInAdapter
Este es el tipo que se devuelve al host desde la llamada de activación del complemento. Se llama a este tipo cuando el host llama al complemento, incluida la llamada que pasa una colección de objetos host (IList<BookInfo>) al complemento. Esta clase convierte el contrato ILibraryManagerContract en la vista de host de LibraryManager. Esta clase hereda de la vista de host e implementa el contrato llamando a la vista que se pasa a su constructor.
Dado que las referencias de una colección de tipos personalizados, los objetos BookInfo, deben calcularse a través del límite de asilamiento, este adaptador utiliza la clase CollectionAdapters. Esta clase proporciona métodos para convertir una colección IList<T> en una colección IListContract<T>, lo que permite que la colección atraviese el límite de aislamiento y pase al otro extremo de la canalización.
BookInfoAddInAdapter
La clase LibraryManagerViewToContractAddInAdapter llama a los métodos static (Shared en Visual Basic) de este adaptador adaptar un contrato o una vista o para devolver un contrato o una vista existente. De este modo, se impide que se cree un adaptador adicional cuando un objeto realiza un viaje de ida y vuelta (round trip) entre el host y el complemento.
Para crear el adaptador de conversión
Agregue un nuevo proyecto denominado AddInSideAdapters a la solución BooksPipeline. Utilice como base la plantilla Biblioteca de clases.
En Visual Basic, abra la ventana Propiedades del proyecto y use la ficha Aplicación para eliminar el valor predeterminado proporcionado para Espacio de nombres de la raíz.
En el Explorador de soluciones, agregue referencias de los siguientes ensamblados al proyecto AddInSideAdapters:
System.AddIn.dll
System.AddIn.Contract.dll
En el Explorador de soluciones, agregue referencias de los siguientes proyectos al proyecto AddInSideAdapters:
AddInViews
LibraryContracts
En las Propiedades de estas referencias, establezca Copia local en False para impedir que los ensamblados a los que se hace referencia se copien en la carpeta de compilación local. Los ensamblados estarán ubicados en la estructura de directorios de canalización, tal y como se describe posteriormente en el procedimiento "Implementar la canalización" de este tutorial.
Llame al archivo de clase BookInfoContractToViewAddInAdapter.
En el archivo de clase, agregue una referencia al espacio de nombres System.AddIn.Pipeline.
Utilice el código siguiente para agregar la clase BookInfoContractToViewAddInAdapter. La clase no necesita ningún atributo porque no se utiliza para activar la canalización. La clase BookInfoAddInAdapter utiliza el método internal (Friend en Visual Basic) GetSourceContract para impedir que se cree un adaptador adicional cuando un objeto realiza un viaje de ida y vuelta (round trip) entre el host y el complemento.
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; } } }
Utilice el código siguiente para agregar la clase BookInfoViewToContractAddInAdapter al proyecto AddInSideAdapters. La clase no necesita ningún atributo porque no se utiliza para activar la canalización. La clase BookInfoAddInAdapter utiliza el método internal (Friend en Visual Basic) GetSourceView para impedir que se cree un adaptador adicional cuando un objeto realiza un viaje de ida y vuelta (round trip) entre el host y el complemento.
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; } } }
Utilice el código siguiente para agregar la clase LibraryManagerViewToContractAddInAdapter al proyecto AddInSideAdapters. Esta clase necesita el atributo AddInAdapterAttribute, ya que se utiliza para activar la canalización.
El método ProcessBooks muestra cómo se pasa una lista de libros a través del límite de aislamiento. Utiliza el método CollectionAdapters.ToIList para convertir la lista. Pasa los objetos de la lista, pasa los delegados de los métodos del adaptador proporcionados por la clase BookInfoAddInAdapter.
El método GetBestSeller muestra cómo se pasa un único objeto BookInfo a través del límite de aislamiento.
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; } } }
Utilice el código siguiente para agregar la clase BookInfoAddInAdapter al proyecto AddInSideAdapters. La clase contiene dos métodos static (Shared en Visual Basic): ContractToViewAdapter y ViewToContractAdapter. Los métodos son internal (Friend en Visual Basic) porque sólo los utilizan las otras clases de adaptadores. El propósito de estos métodos es evitar que se cree un adaptador adicional cuando un objeto realice un viaje de ida y vuelta (round trip) en cualquier dirección entre el host y el complemento. Estos métodos deberían proporcionarse a los adaptadores que pasan objetos a través del límite de aislamiento.
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); } } } }
Crear el adaptador del host
Este ensamblado del adaptador del host de esta canalización contiene cuatro clases de adaptadores:
BookInfoContractToViewHostAdapter
Se llama a este adaptador cuando el complemento pasa un objeto BookInfo al host, en solitario o como parte de una colección. Esta clase convierte el contrato del objeto BookInfo en una vista. La clase hereda de la vista de host e implementa los métodos abstractos de la vista llamando al contrato que se pasa al constructor de la clase.
El constructor de este adaptador toma un contrato como su constructor, por lo que se puede aplicar un objeto ContractHandle al contrato para implementar la administración de la duración.
Importante El objeto ContractHandle resulta crítico para la administración de la duración.Si no puede mantener una referencia al objeto ContractHandle, la recolección de elementos no utilizados lo reclamará y la canalización se cerrará cuando el programa no se lo espere.Esto puede dar lugar a errores difíciles de diagnosticar, como AppDomainUnloadedException.El cierre es una etapa normal del proceso de canalización, por lo que no hay ninguna forma de que el código de administración de la duración detecte que esta condición es un error.
BookInfoViewToContractHostAdapter
Se llama a este adaptador cuando el host pasa un objeto BookInfo al complemento. Esta clase convierte la vista de host del objeto BookInfo en un contrato. La clase hereda del contrato e implementa el contrato llamando a la vista de complemento que se pasa al constructor de la clase. Las referencias de este adaptador se calculan en el complemento como un contrato.
LibraryManagerContractToViewHostAdapter
Se llama a este adaptador cuando el host pasa una colección de objetos BookInfo al complemento. El complemento realiza su implementación del método ProcessBooks en esta colección.
Esta clase convierte la vista de host del objeto LibraryManager en un contrato. Esta clase hereda del contrato e implementa el contrato llamando a la vista de host que se pasa al constructor de la clase.
Dado que las referencias de una colección de tipos personalizados, los objetos BookInfo, deben calcularse a través del límite de asilamiento, este adaptador utiliza la clase CollectionAdapters. Esta clase proporciona métodos para convertir una colección IList<T> en una colección IListContract<T>, lo que permite que la colección atraviese el límite de aislamiento y pase al otro extremo de la canalización.
BookInfoHostAdapter
La clase LibraryManagerViewToContractHostAdapter llama a este adaptador para que devuelva los contratos o las vistas existentes de la adaptación en lugar de crear nuevas instancias de la llamada. De este modo, se impide que se cree un adaptador adicional cuando un objeto realiza un viaje de ida y vuelta (round trip) en cualquier dirección entre el host y el complemento.
Para crear el adaptador del host
Agregue un nuevo proyecto denominado HostSideAdapters a la solución BooksPipeline. Utilice como base la plantilla Biblioteca de clases.
En Visual Basic, abra la ventana Propiedades del proyecto y use la ficha Aplicación para eliminar el valor predeterminado proporcionado para Espacio de nombres de la raíz.
En el Explorador de soluciones, agregue referencias de los siguientes ensamblados al proyecto HostSideAdapters:
System.AddIn.dll
System.AddIn.Contract.dll
En el Explorador de soluciones, agregue referencias de los siguientes proyectos al proyecto HostSideAdapters:
HostViews
LibraryContracts
En las Propiedades de estas referencias, establezca Copia local en False para impedir que los ensamblados a los que se hace referencia se copien en la carpeta de compilación local.
En el archivo de clase, agregue una referencia al espacio de nombres System.AddIn.Pipeline.
Utilice el código siguiente para agregar la clase BookInfoContractToViewHostAdapter. La clase no necesita ningún atributo porque no se utiliza para activar la canalización. La clase BookInfoAddInAdapter utiliza el método internal (Friend en Visual Basic) GetSourceContract para impedir que se cree un adaptador adicional cuando un objeto realiza un viaje de ida y vuelta (round trip) entre el host y el complemento.
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; } } }
Utilice el código siguiente para agregar la clase BookInfoViewToContractHostAdapter al proyecto HostSideAdapters. La clase no necesita ningún atributo porque no se utiliza para activar la canalización. La clase BookInfoAddInAdapter utiliza el método internal (Friend en Visual Basic) GetSourceView para impedir que se cree un adaptador adicional cuando un objeto realiza un viaje de ida y vuelta (round trip) entre el host y el complemento.
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; } } }
Utilice el código siguiente para agregar la clase LibraryManagerContractToViewHostAdapter al proyecto HostSideAdapters. Esta clase necesita el atributo HostAdapterAttribute, ya que se utiliza para activar la canalización.
El método ProcessBooks muestra cómo se pasa una lista de libros a través del límite de aislamiento. Utiliza el método CollectionAdapters.ToIListContract para convertir la lista. Pasa los objetos de la lista, pasa los delegados de los métodos del adaptador proporcionados por la clase BookInfoHostAdapter.
El método GetBestSeller muestra cómo se pasa un único objeto BookInfo a través del límite de aislamiento.
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; } } }
Utilice el código siguiente para agregar la clase BookInfoHostAdapter al proyecto HostSideAdapters. La clase contiene dos métodos static (Shared en Visual Basic): ContractToViewAdapter y ViewToContractAdapter. Los métodos son internal (Friend en Visual Basic) porque sólo los utilizan las otras clases de adaptadores. El propósito de estos métodos es evitar que se cree un adaptador adicional cuando un objeto realice un viaje de ida y vuelta (round trip) en cualquier dirección entre el host y el complemento. Estos métodos deberían proporcionarse a los adaptadores que pasan objetos a través del límite de aislamiento.
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); } } } }
Crear el host
Una aplicación host interactúa con el complemento a través de la vista de host. Utiliza los métodos de detección y activación de complementos proporcionados por las clases AddInStore y AddInToken para realizar las operaciones siguientes:
Compilar de nuevo la memoria caché de canalización y la información del complemento.
Buscar complementos de tipo LibraryManager bajo el directorio raíz de la canalización especificado.
Solicitar al usuario que seleccione el complemento que va a utilizar. En este ejemplo, sólo hay un complemento disponible.
Activar el complemento seleccionado en un nuevo dominio de aplicación con un nivel de confianza de seguridad especificado.
Llamar al método ProcessBooks que va a pasar una colección de objetos BookInfo al complemento. El complemento llama a su implementación del método ProcessBooks y realiza ciertas funciones, como descontar el 20 por cierto a los libros de informática.
Llamar al método GetBestSeller que el complemento utiliza para devolver un objeto BookInfo con información sobre el libro con el mayor número de ventas.
Llamar al método Data para obtener el tipo del impuesto sobre la venta actual del complemento. Este método toma y devuelve una cadena que es un tipo de referencia serializable sellado. Como resultado, el método se puede pasar a través del límite de aislamiento al otro extremo de la canalización sin utilizar adaptadores de vista a contrato o de contrato a vista.
El host tiene un método CreateBooks que crea una colección de objetos BookInfo. Este método crea la colección utilizando el archivo de ejemplo books.xml que está disponible en Sample XML File (books.xml).
Para crear el host
Agregue un nuevo proyecto denominado BookStore a la solución BooksPipeline. Utilice como base la plantilla Aplicación de consola.
En Visual Basic, abra la ventana Propiedades del proyecto y use la ficha Aplicación para eliminar el valor predeterminado proporcionado para Espacio de nombres de la raíz.
En el Explorador de soluciones, agregue una referencia del ensamblado System.AddIn.dll al proyecto BookStore.
Agregue una referencia de proyecto al proyecto HostViews. En las Propiedades de esta referencia, establezca Copia local en False para impedir que el ensamblado al que se hace referencia se copie en la carpeta de implementación local.
En Visual Basic, cambie el módulo por una clase:
Excluya el módulo predeterminado del proyecto y, a continuación, agregue una clase denominada Program.
Sustituya la palabra clave Public por la palabra clave Friend.
Agregue un procedimiento Shared Sub Main() a la clase.
Utilice la ficha Aplicación del cuadro de diálogo Propiedades del proyecto para establecer Objeto de inicio en Sub Main.
En el archivo de clase, agregue referencias a System.AddIn.Pipeline y los espacios de nombres del segmento de la vista de host.
En el Explorador de soluciones, seleccione la solución y en el menú Proyecto, elija Propiedades. En el cuadro de diálogo Páginas de propiedades de la solución, establezca Proyecto de inicio único para que sea este proyecto de aplicación host.
Utilice el código siguiente en la aplicación host:
Nota En el código, cambie la ubicación desde la que se carga el archivo books.xml por "books.xml", para que el archivo se cargue desde la carpeta de la aplicación.Si desea situar la aplicación en una ubicación que no sea la carpeta Pipeline, cambie la línea de código que establece la variable addInRoot para que la variable contenga la ruta de acceso de la estructura de directorios de la canalización.
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; } } }
Para crear el archivo de datos books.xml
Agregue un nuevo archivo XML al proyecto BookStore. En el cuadro de diálogo Agregar nuevo elemento, denomine al archivo books.xml.
Reemplace el contenido predeterminado de books.xml con el código XML de Sample XML File (books.xml).
En el Explorador de soluciones, seleccione books.xml y en Propiedades establezca Copiar en el directorio de resultados en Copiar siempre.
Crear el complemento
Un complemento implementa los métodos especificados por la vista de complemento. Este complemento implementa el método ProcessBooks. El método realiza las operaciones siguientes en una colección de objetos BookInfo que el host le pasa:
Descuenta un 20 por cierto del precio de todos los libros de informática.
Quita todos los libros de terror de la colección.
Este complemento también implementa el método GetBestSeller; para ello, pasa al host un objeto BookInfo que describe el libro con el mayor número de ventas.
Para crear el complemento
Agregue un nuevo proyecto denominado BooksAddin a la solución BooksPipeline. Utilice como base la plantilla Biblioteca de clases.
En Visual Basic, abra la ventana Propiedades del proyecto y use la ficha Aplicación para eliminar el valor predeterminado proporcionado para Espacio de nombres de la raíz.
En el Explorador de soluciones, agregue una referencia del ensamblado System.AddIn.dll al proyecto BooksAddin.
Agregue una referencia de proyecto al proyecto AddInViews. En las Propiedades de esta referencia, establezca Copia local en False para impedir que el ensamblado al que se hace referencia se copie en la carpeta de implementación local.
En el archivo de clase, agregue referencias a System.AddIn y los espacios de nombres del segmento de la vista de complemento.
Utilice el código siguiente en la aplicación del complemento:
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; } } }
Implementar la canalización
Ya está listo para compilar e implementar los segmentos de los complementos en la estructura de directorios de canalización requerida.
Para implementar los segmentos en la canalización
En cada proyecto de la solución, utilice la ficha Generar de Propiedades del proyecto (la ficha Compilar en Visual Basic) para establecer el valor de Ruta de acceso de los resultados (Ruta de acceso de los resultados de la compilación en Visual Basic), tal y como se muestra en la tabla siguiente.
Proyecto
Ruta de acceso
BooksAddIn
Pipeline\AddIns\CalcV1
AddInSideAdapters
Pipeline\AddInSideAdapters
AddInViews
Pipeline\AddInViews
LibraryContracts
Pipeline\Contracts
BookStore
Canalización (o el directorio de la aplicación propio)
HostSideAdapters
Pipeline\HostSideAdapters
HostViews
Canalización (o el directorio de la aplicación propio)
Nota Si decidió situar la aplicación en una ubicación distinta de la carpeta Pipeline, asegúrese de cambiar el código del host en que se especifica la ubicación del directorio raíz de la canalización.
Compile la solución de Visual Studio.
Para obtener información acerca de cómo se realiza la implementación en la canalización, vea Requisitos del desarrollo de canalizaciones.
Ejecutar la aplicación host
Ya está listo para ejecutar el host e interactuar con el complemento.
Para ejecutar la aplicación host
En el símbolo del sistema, vaya al directorio raíz de la canalización y ejecute la aplicación host. En este ejemplo, la aplicación host es BookStore.exe.
El host busca todos los complementos disponibles de su tipo y le solicita que seleccione un complemento. Escriba 1 para el único complemento disponible.
El host activa el complemento y lo utiliza para realizar varias operaciones sobre la lista de libros.
Presione cualquier tecla para cerrar la aplicación.
Vea también
Tareas
Tutorial: Crear una aplicación extensible
Tutorial: Habilitar la compatibilidad con versiones anteriores al cambiar el host
Conceptos
Requisitos del desarrollo de canalizaciones