Tutorial: Pasar colecciones entre hosts y complementos

Actualización: Julio de 2008

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 siguientes tareas:

  • 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 generar canalizaciones de complementos en el sitio Managed Extensibility and Add-In Framework de CodePlex.

Requisitos previos

Necesita los componentes siguientes para completar este tutorial:

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

  1. En Visual Studio, cree un nuevo proyecto denominado LibraryContracts. Utilice como base la plantilla Biblioteca de clases.

  2. 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

  1. En la solución de Visual Studio denominada BooksPipeline, abra el proyecto LibraryContracts.

  2. 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.

  3. En el Explorador de soluciones, agregue referencias de los siguientes ensamblados al proyecto:

    Sytem.AddIn.Contract.dll

    System.AddIn.dll

  4. En el archivo de clase, agregue referencias a los espacios de nombres System.AddIn.Contract y System.AddIn.Pipeline.

  5. 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.

  6. 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

  1. Agregue un nuevo proyecto denominado AddInViews a la solución BooksPipeline. Utilice como base la plantilla Biblioteca de clases.

  2. 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.

  3. En el Explorador de soluciones, agregue una referencia de System.AddIn.dll al proyecto AddInViews.

  4. Cambie el nombre de la clase predeterminada del proyecto a LibraryManager y conviértala en una clase abstract (MustInherit en Visual Basic).

  5. En el archivo de clase, agregue una referencia al espacio de nombres System.AddIn.Pipeline.

  6. La clase LibraryManager se utiliza para activar la canalización, por lo que debe aplicar el atributo AddInBaseAttribute.

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

  9. 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

  1. Agregue un nuevo proyecto denominado HostViews a la solución BooksPipeline. Utilice como base la plantilla Biblioteca de clases.

  2. 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.

  3. Cambie el nombre de la clase predeterminada del proyecto a LibraryManager y conviértala en una clase abstract (MustInherit en Visual Basic).

  4. 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);
    }
    }
    
  5. Agregue una clase abstract (una clase MustInherit en Visual Basic) al proyecto y denomínela BookInfo.

  6. 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 del complemento

El ensamblado del adaptador del complemento 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.

    Nota 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 del complemento

  1. Agregue un nuevo proyecto denominado AddInSideAdapters a la solución BooksPipeline. Utilice como base la plantilla Biblioteca de clases.

  2. 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.

  3. En el Explorador de soluciones, agregue referencias de los siguientes ensamblados al proyecto AddInSideAdapters:

    System.AddIn.dll

    System.AddIn.Contract.dll

  4. 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.

  5. Llame al archivo de clase BookInfoContractToViewAddInAdapter.

  6. En el archivo de clase, agregue una referencia al espacio de nombres System.AddIn.Pipeline.

  7. 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;
        }
    }
    }
    
  8. 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;
        }
    }
    }
    
  9. 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;
        }
    }
    }
    
  10. 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.

    Nota 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

  1. Agregue un nuevo proyecto denominado HostSideAdapters a la solución BooksPipeline. Utilice como base la plantilla Biblioteca de clases.

  2. 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.

  3. En el Explorador de soluciones, agregue referencias de los siguientes ensamblados al proyecto HostSideAdapters:

    System.AddIn.dll

    System.AddIn.Contract.dll

  4. 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.

  5. En el archivo de clase, agregue una referencia al espacio de nombres System.AddIn.Pipeline.

  6. 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;
            }
        }
    }
    
  7. 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;
        }
    }
    }
    
  8. 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;
        }
    }
    }
    
  9. 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:

  • Generar 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

  1. Agregue un nuevo proyecto denominado BookStore a la solución BooksPipeline. Utilice como base la plantilla Aplicación de consola.

  2. 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.

  3. En el Explorador de soluciones, agregue una referencia del ensamblado System.AddIn.dll al proyecto BookStore.

  4. 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.

  5. 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 inicial en Sub Main.

  6. En el archivo de clase, agregue referencias a System.AddIn.Pipeline y los espacios de nombres del segmento de la vista de host.

  7. 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.

  8. 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

  1. Agregue un nuevo archivo XML al proyecto BookStore. En el cuadro de diálogo Agregar nuevo elemento, denomine al archivo books.xml.

  2. Reemplace el contenido predeterminado de books.xml con el código XML de Sample XML File (books.xml).

  3. 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

  1. Agregue un nuevo proyecto denominado BooksAddin a la solución BooksPipeline. Utilice como base la plantilla Biblioteca de clases.

  2. 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.

  3. En el Explorador de soluciones, agregue una referencia del ensamblado System.AddIn.dll al proyecto BooksAddin.

  4. 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.

  5. En el archivo de clase, agregue referencias a System.AddIn y los espacios de nombres del segmento de la vista de complemento.

  6. 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 generar e implementar los segmentos de los complementos en la estructura de directorios de canalización requerida.

Para implementar los segmentos en la canalización

  1. 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.

  2. Genere 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

  1. 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.

  2. 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.

  3. 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

Contratos, vistas y adaptadores

Desarrollo de canalizaciones

Historial de cambios

Fecha

Historial

Motivo

Julio de 2008

Errores corregidos en el texto. Se agregó una nota acerca de la forma de mantener una referencia al contrato.

Comentarios de los clientes.