Skip to main content

Servicios de SharePoint 2010 - III: Programando un Servicio propio

Fabián Imaz
Juan Carlos González
Gustavo Vélez

La arquitectura de servicios de SharePoint 2010 nos permite construir aplicaciones de servicio con el fin de incorporar nuevas funcionalidades a la plataforma. Estas nuevas funcionalidades pueden ser muy diversas logrando así que la plataforma se transforme en una infraestructura base dentro de nuestra organización proporcionando a los usuarios un único entorno centralizado para trabajar. Muchas organizaciones cuentan con sistemas de gestión, facturación, de recursos humanos, de manejo de stock, monitorización y otros, que implican que los usuarios tengan que ingresar en las mismas de manera independiente para poder realizar sus tareas diarias. Muchas veces realizar la consolidación de datos requiere la implementación de otras herramientas o muchas horas de trabajo. La realidad de estas organizaciones sería diferente si en vez de implementar estas distintas plataformas, que incluso muchas veces están construidas con tecnologías diferentes, decidieran implementar servicios y aplicaciones de servicios en SharePoint 2010.

Elementos que componen una aplicación de servicio

Cada aplicación de servicio se compone de una serie de bloques necesarios para ejecutar los mismos dentro de la granja de SharePoint 2010. A continuación se detallan los elementos mínimos indispensables en una aplicación de servicio personalizada:

  • Servicio: Este elemento es el servicio en sí mismo, es el componente que estará ejecutándose una vez instalado, pudiendo ser un servicio de Windows o un servicio de WCF. Puede estar hospedado en uno o varios servidores de la granja. Si bien no es necesario la utilización de WCF como Framework de comunicación, es ideal para poder implementar una seguridad robusta dada todas las ventajas que esta tecnología provee.
  • Instancia del Servicio: Representa una instancia del servicio ejecutando en uno de los servidores de la granja. Se pueden tener múltiples instancias ejecutando en varios servidores de la granja de SharePoint 2010 y gracias a la arquitectura provista por las Aplicaciones de Servicio las instancias se ejecutan en entornos escalables y balanceados que permiten su correcto funcionamiento.
  • Aplicación de Servicio: Representa lógicamente el servicio dentro de la granja de SharePoint, encapsulando el servicio físico instalado en la granja sin importar en cuantos servidores se ha activado; todos los componentes de SharePoint utilizarán está abstracción cuando deban interactuar con el servicio.
  • Base de datos del Servicio: Algunos servicios necesitan tener una estructura de almacenamiento como una base de datos para su correcto funcionamiento. Esta base de datos será creada o al momento de activar el servicio en la granja de SharePoint o bien al crear la aplicación de servicio dentro de SharePoint.
  • Proxy del Servicio de Aplicación: Al crear una aplicación de servicio en SharePoint se crea también un Proxy, una estructura de comunicación que es expuesta por la granja a través de un servicio de WCF para que todos los componentes web (Elementos webs, Flujos, Eventos, aplicaciones personalizadas, etc.) puedan consumir las operaciones del servicios desde la capa de presentación o servidor web.

Cada vez que instalamos SharePoint 2010 este instala y configuran dos Aplicaciones de Servicio que serán utilizados para poder consumir correctamente todos los servicios que se configuren posteriormente. Estos servicios son:

  • Descubrimiento de Aplicaciones y Balanceador de carga: Esta aplicación de servicio es utilizada para hacer un correcto descubrimiento de todas las aplicaciones de servicio configurada en la granja de SharePoint y manejar todas las peticiones que se realizan entre las distintas instancias instaladas.
  • Identificadores de Seguridad (Security Token): Este servicio provee todo el mecanismo de autenticación entre las diferentes aplicaciones y capas de la granja de SharePoint.
  • Manejo de seguridad y acceso: Las aplicaciones de servicio cuentan con niveles de seguridad que permiten establecer quienes serán los administradores de los mismos. Por defecto los usuarios que son Administradores de la granja pueden acceder a todas las aplicaciones de servicio creadas, pero en un ambiente mixto se podría tener la administración de cada una de las aplicaciones de servicio descentralizada de manera que se pueda asignar a un usuario o grupo de usuarios por ejemplo la administración de una aplicación de servicio en particular.
  • Páginas de administración: Algunas Aplicaciones de Servicio cuentan con una serie de páginas necesarias para poderlas administrar, realizar cambios de configuración o editar sus propiedades. Dependiendo de cada aplicación de servicio estás páginas podrán variar según los parámetros y opciones de cada aplicación.

Modelo de objetos para servicios y Aplicaciones de Servicio

SharePoint 2010 cuenta con un Framework de clases para que los desarrolladores puedan construir sus propios servicios de aplicaciones e instalarlos. Todas las aplicaciones de servicios provistas por SharePoint fueron creadas basadas en este Framework el cual podemos ver en la Figura 1.

Figura 1.- Relación entre las clases del Framework del servicio de aplicación [1].

A continuación vamos a describir brevemente cada una de las clases involucradas en el Framework provisto para la construcción de servicios de aplicaciones:

  • SPFarm: Esta clase representa la granja de SharePoint 2010 dentro del modelo de objetos y es la clase más alta en la jerarquía de clases provista, brindando acceso a la topología de servidores dentro de la granja o los servicios configurados en la misma.
  • SPServer: A través de esta clase obtendremos acceso al servidor físico dentro de la granja pudiendo habilitar por ejemplo servicios que deberán correr en el mismo.
  • SPService: A través de esta clase se puede tener acceso al servicio instalado en la granja de SharePoint. Para acceder a todos los servicios disponibles debemos obtener la colección SPServiceCollection de servicios a través de la propiedad Services del objeto SPFarm.
  • SPServiceInstance: Esta clase representa la instancia del servicio ejecutando en un servidor de la granja. Puesto que podemos tener configurado varias instancias del mismo servicio en distintos servidores de la granja a través de la propiedad Instances de la clase SPservice podemos tener todas las instancias de dicho servicio que fueron configuradas.
  • SPServiceApplication: Esta clase representa una aplicación de servicio que se puede crear en SharePoint 2010 para exponer las funcionalidades del servicio que se haya configurado en la granja. Cada aplicación de servicio creada ejecuta de forma aislada e independiente con respecto a cualquier aplicación de servicio del mismo tipo, manteniendo sus propias bases de datos y su configuración de seguridad, permitiendo así tener por ejemplo varias aplicaciones de servicios de búsqueda donde los elementos que se están rastreando no serán compartidos entre sí.
  • SPServiceProxy: Esta es la clase base cliente que se utiliza para exponer un servicio de aplicación como hacia las aplicaciones web de SharePoint que tengan asociado dicho servicio.
  • SPServiceApplicationProxy: Esta clase expone todas las interfaces públicas que provee un servicio de aplicación para que cualquier Aplicación Web (Front End) de SharePoint pueda consumir. Cualquier elemento web, evento, workflow o artefacto que es hospedado en una Aplicación Web de SharePoint y necesita consumir un servicio de aplicación configurado en SharePoint deberá consumirlo a través de la clase cliente creada automáticamente cuando se creó la Aplicación de Servicio.

Programación de una Aplicación de Servicio personalizada

En este caso se va a implementar una Aplicación de Servicio de SharePoint 2010 que brinde la posibilidad de extender la plataforma de manera que los usuarios puedan manejar el sistema de gestión de la organización directamente desde sitios de SharePoint, incluso utilizando algunas de las ventajas a nivel de usabilidad que la plataforma aporta. Este servicio permitirá a los usuarios administrar “las órdenes de ventas” del sistema de gestión a través de SharePoint. Antes de comenzar, instale en su servidor de base de datos (BD) la BD “AdventureWorks2008R2” que puede descargarse desde el sitio correspondiente en CodePlex [2].

El primer paso para crear una nueva aplicación de servicio consiste en crear un proyecto en blanco de SharePoint 2010 en Visual Studio 2010.

Figura 2.- Proyecto en blanco de SharePoint 2010

Una decisión importante que se debe tomar es el lugar donde se implementará la lógica del servicio que se está construyendo. En el ejemplo, se va a construir un servicio que exponga operaciones de la lógica de negocios del sistema de gestión organizacional a través de WCF lo que implica definir los contratos y tipos de entidades de las operaciones. Se podría implementar todas las clases en un mismo ensamblado vinculado al servicio que se está creando, pero esto implica que cada vez que se realice un cambio en la lógica del negocio sea necesario recompilar completamente el servicio e implementarlo de nuevo, por lo que es recomendable implementar la lógica de negocios en ensamblados diferentes. Por lo tanto, se añadirán 5 proyectos más en Visual Studio 2010 de la siguiente forma:

  • Proyecto de Biblioteca de Clases para definir todas las entidades que vamos a estar usando.
  • Proyecto de Biblioteca de Clases para definir los contratos
  • Proyecto de Biblioteca de Clases para implementar las operaciones de negocio.
  • Proyecto de Biblioteca de Clases donde se implementarán los servicios que se suministrarán a la capa de presentación, para que encapsule las llamadas al servicio en SharePoint.
  • Proyecto de SharePoint donde se crean todos los componentes web que serán utilizados por los usuarios finales en un portal de SharePoint 2010.

Una vez definidos todos los proyectos, la solución debería quedar como se muestra en la Figura 3.

Figura 3.- Proyectos para la implementación de la aplicación de servicio.

Antes de implementar el servicio y las operaciones que el mismo estará exponiendo para ser utilizado desde SharePoint, se deben crear algunas carpetas dentro del proyecto de SharePoint y mapearlas con carpetas del directorio de instalación de SharePoint 2010. A continuación se presenta la lista de carpetas que se necesitan:

Admin – Contiene todas las páginas necesarias para manejar la aplicación de servicio desde la administración central de SharePoint, creación, propiedades y manejo del servicio. La ruta física de esta carpeta se encuentra en {SharePointRoot}\Template\ADMIN.

PowerShellRegistro - Contiene los archivos de configuración para registrar los comandos PowerShell necesarios para poder crear la aplicación de servicio por línea de comandos. La ruta física de esta carpeta se encuentra en {SharePointRoot}\CONFIG\POWERSHELL\Registration.

SQL – En esta carpeta se ubican los archivos necesarios para crear la base de datos de la aplicación de servicio en el caso que así lo necesitara. Dependiendo del tipo de servicio que esté desarrollando, podrá disponer de una base de datos propia donde almacenar la información que este deba persistir. En el ejemplo propuesto no es necesario que el servicio disponga de una base de datos ya que se usara la base datos externa del sistema de gestión. La ruta física de esta carpeta se encuentra en {SharePointRoot}\Template\SQL.

WebServices – En esta carpeta se almacenan los archivos necesarios para exponer las operaciones de los servicios a través de WCF. Básicamente para el servicio de ejemplo que se va a construir, esta carpeta contiene el archivo .svc y el web.config del servicio con la definiciones de cómo serán expuestas las operaciones. La ruta física de esta carpeta se encuentra en {SharePointRoot}\WebServices.

WebClients – En esta carpeta se almacenan los archivos necesarios para que los clientes puedan consumir los servicios expuestos por WCF. Contiene un archivo de configuración que define como y donde se expusieron las operaciones que el servicio expone. La ruta física de esta carpeta se encuentra en {SharePointRoot}\WebClients.

En la Figura 4 se puede ver la estructura final de toda la solución en Visual Studio 2010 para poder empezar a codificar la aplicación de servicio.

Figura 4.- Estructura final del proyecto de Visual Studio 2010.

Una vez se tienen todos los componentes definidos, es posible comenzar a implementar todas las clases necesarias para poder crear la propia aplicación de servicio. La primera clase que se va a implementar es la clase del servicio, que en este caso particular tiene que heredar de la clase abstracta SPIisWebService que hereda a su vez de SPService (Nota: Recuerde que esta clase en el diagrama de clases de la Figura 1, es la clase base del Framework provisto por SharePoint). También se implementa la interfaz IServiceAdministration la cual obliga a implementar los métodos administrativos para la creación de los componentes necesarios para crear el servicio de aplicación.

namespace Msdn.ServiceApplication
{
    [Guid("A14F914A-7701-4fa8-83B6-975D37AEC410")]
    public class ServicioGestion : SPIisWebService, IServiceAdministration
    {

        public ServicioGestion()
            : base()
        { }

        public ServicioGestion(SPFarm pGranja)
            : base(pGranja)
        { this.Name = "MSDN Servicio de Gestion"; }
        public SPServiceApplication CreateApplication(string name, Type serviceApplicationType, SPServiceProvisioningContext provisioningContext)
        {
            if (provisioningContext == null)
                throw new ArgumentNullException("provisioningContext");
            if (serviceApplicationType != typeof(ServicioGestionAplicacion))
                throw new NotSupportedException("Tipo de servicio de aplicacion invalido, se esperaba AplicacionServicioGestion");
            ServicioGestionAplicacion lapplication = (ServicioGestionAplicacion)this.Farm.GetObject(name, this.Id, serviceApplicationType);

            if (lapplication == null)
            {
                lapplication = ServicioGestionAplicacion.Create(name, this, provisioningContext.IisWebServiceApplicationPool);
            }

            return lapplication;
        }

        public SPServiceApplicationProxy CreateProxy(string pNombre, SPServiceApplication pAplicacionServicio, SPServiceProvisioningContext pContexto)
        {
            if (pAplicacionServicio == null)
                throw new ArgumentNullException("serviceApplication");

            if (pAplicacionServicio.GetType() != typeof(ServicioGestionAplicacion))
                throw new NotSupportedException("Tipo de aplicación de servicio invalido");
            ServicioGestionProxy lServicioProxy = (ServicioGestionProxy)base.Farm.GetObject(string.Empty, base.Farm.Id, typeof(ServicioGestionProxy));
            if (lServicioProxy == null)
            {
                lServicioProxy = new ServicioGestionProxy(base.Farm);
                lServicioProxy.Update(true);
            }

            ServicioGestionAplicacionProxy lAplicacionProxy = lServicioProxy.ApplicationProxies.GetValue<ServicioGestionAplicacionProxy>(pNombre);
            if (lAplicacionProxy == null)
            {
                lAplicacionProxy = new ServicioGestionAplicacionProxy(pNombre, lServicioProxy, ((ServicioGestionAplicacion)pAplicacionServicio).Uri);
            }

            lAplicacionProxy.Update(true);
            return lAplicacionProxy;
        }

        public SPPersistedTypeDescription GetApplicationTypeDescription(Type serviceApplicationType)
        {
            if (serviceApplicationType != typeof(ServicioGestionAplicacion))
                throw new NotSupportedException("Tipo de aplicación de servicio invalido, se esperaba AplicacionServicioGestion");
            return new SPPersistedTypeDescription("Servicio de aplicacion de gestion", "Servicio para el manejo del sistema de gestion");
        }

        public Type[] GetApplicationTypes()
        {
            return (new Type[] { typeof(ServicioGestionAplicacion) });
        }

        public override SPAdministrationLink GetCreateApplicationLink(Type serviceApplicationType)
        {
            return new SPAdministrationLink("/_admin/ServicioGestion/ServicioGestionCreateApp.aspx");
        }

        SPCreateApplicationOptions GetCreateApplicationOptions(Type serviceApplicationType)
        {
            return SPCreateApplicationOptions.None;
        }
    }
}

Listado 1.- Implementación de la clase ServicioGestion.

El paso siguiente consiste en crear la clase que representa la instancia del servicio. Esta clase deberá heredar de la clase abstracta SPIisWebServiceInstance, como podemos ver en la Listado 2.

namespace Msdn.ServiceApplication
{
    [Guid("14BCA048-41C0-4d64-AE05-52D8656404F2")]
    public class ServicioGestionInstancia : SPIisWebServiceInstance
    {
        public ServicioGestionInstancia()
            : base()
        { }

        internal ServicioGestionInstancia(SPServer pServidor, ServicioGestion pServicio)
            : base(pServidor, pServicio)
        {
        }
        internal ServicioGestionInstancia(string pNombre, SPServer pServidor, ServicioGestion pServicio)
            : base(pServidor, pServicio)
        {
            this.Name = pNombre;
        }

        public override string DisplayName
        {
            get { return "MSDN Servicio de Gestion"; }
        }

        public override string TypeName
        {
            get { return "MSDN Servicio de Gestion"; }
        }
    }
}

Listado 2.- Clase InstanciaServicioGestion.

A continuación hay que crear la clase que representa la aplicación de servicio en SharePoint 2010 la cual debe heredar de la clase abstracta SPIisWebServiceApplication. Esta clase expone las operaciones que serán consumidas desde el cliente o incluso desde algún otro servicio desarrollado y hospedado en SharePoint 2010.

 

namespace Msdn.ServiceApplication
{
    [IisWebServiceApplicationBackupBehavior, Guid("E1B9DA13-DB64-4e4f-B296-D51FA01711AE")]
    public class ServicioGestionAplicacion : SPIisWebServiceApplication, ICliente, IOrdenesDeVenta, IProducto
    {
        [Persisted]
        private string mCadenaConexion = "Data Source=yoda;Initial Catalog=AdventureWorks2008R2;Integrated Security=True";


        internal string CadenaConexion
        {
            get { return mCadenaConexion; }
            set { mCadenaConexion = value; }
        }

        public ServicioGestionAplicacion()
        { }

        internal ServicioGestionAplicacion(string pNombre, ServicioGestion pServicio, SPIisWebServiceApplicationPool pApplicationPool)
            : base(pNombre, pServicio, pApplicationPool)
        {
        }

        public static ServicioGestionAplicacion Create(string pNombre, ServicioGestion pServicio, SPIisWebServiceApplicationPool pPoolDeAplicaciones)
        {
            if (pNombre == null)
                throw new ArgumentNullException("Nombbre");
            if (pServicio == null)
                throw new ArgumentNullException("Servicio");
            if (pPoolDeAplicaciones == null)
                throw new ArgumentNullException("Pool de aplicaciones");
            ServicioGestionAplicacion lAplicacionServicioGestion = new ServicioGestionAplicacion(pNombre, pServicio, pPoolDeAplicaciones);
            lAplicacionServicioGestion.Update();
            lAplicacionServicioGestion.AddServiceEndpoint("http", SPIisWebServiceBindingType.Http);
            lAplicacionServicioGestion.AddServiceEndpoint("https", SPIisWebServiceBindingType.Https, "secure");
            lAplicacionServicioGestion.Update();
            //creo la instancia para el sistema de gestion. 
            return lAplicacionServicioGestion;
        }

        protected override string DefaultEndpointName
        {
            get { return ("http"); }
        }

        public override string TypeName
        {
            get { return ("Servicio de Aplicacion de Gestion"); }
        }
        protected override string InstallPath
        {
            get { return SPUtility.GetGenericSetupPath(@"WebServices\ServicioGestion"); }
        }

        protected override string VirtualPath
        {
            get
            {
                return "SevicioGestion.svc";
            }
        }

        public override Guid ApplicationClassId
        {
            get { return (new Guid("F624D941-757A-4619-B635-0C9D8F8BD6A5")); }
        }

        public override Version ApplicationVersion
        {
            get { return (new Version("1.0.0.0")); }
        }
        public override void Provision()
        {
            base.Status = SPObjectStatus.Provisioning;
            this.Update();

            //this.Provision();

            this.Status = SPObjectStatus.Online;
            this.Update();
        }

        public override void Unprovision(bool deleteData)
        {
            base.Status = SPObjectStatus.Unprovisioning;
            this.Update();

            base.Unprovision(deleteData);

            base.Status = SPObjectStatus.Disabled;
            this.Update();
        }
        public override SPAdministrationLink ManageLink
        {
            get
            {
                return new SPAdministrationLink("/_admin/ServicioGestion/ServicioGestionManejarApp.aspx?id=" + this.Id.ToString());
            }
        }

        public override SPAdministrationLink PropertiesLink
        {
            get
            {
                return new SPAdministrationLink("/_admin/ServicioGestion/ServicioGestionPropiedadesApp.aspx?id=" + this.Id.ToString());
            }
        }

        protected override SPNamedCentralAdministrationRights[] AdministrationAccessRights
        {
            get
            {
                return base.AdministrationAccessRights;
            }
        }

        protected override SPNamedIisWebServiceApplicationRights[] AccessRights
        {
            get
            {
                return new SPNamedIisWebServiceApplicationRights[]
                {
                    SPNamedIisWebServiceApplicationRights.FullControl,
                    new SPNamedIisWebServiceApplicationRights("Request", ServicioGestionDerechoAcceso.Request),
                    SPNamedIisWebServiceApplicationRights.Read,
                };
            }
        }
        #region Operaciones Gestion
        
        public List<Cliente> ObtenerTodosLosClientes()
        {
            LogicaClientes lLogica = new LogicaClientes(mCadenaConexion);
            return lLogica.ObtenerTodosLosClientes();
        }

        public Cliente ObtenerClientePorId(int pIdCliente)
        {
            LogicaClientes lLogica = new LogicaClientes(mCadenaConexion);
            return lLogica.ObtenerClientePorId(pIdCliente);
        }
        public List<OrdenDeVenta> ObtenerTodasLasOrdenesDeVenta()
        {
            Logica.LogicaOrdenesDeVenta lLogicaOrdenesDeVenta = new Logica.LogicaOrdenesDeVenta(mCadenaConexion);
            return lLogicaOrdenesDeVenta.ObtenerTodasLasOrdenesDeVenta();
        }
        public List<OrdenDeVenta> ObtenerOrdenesDeVentaPorIdCliente(int pIdCliente)
        {
            Logica.LogicaOrdenesDeVenta lLogicaOrdenesDeVenta = new Logica.LogicaOrdenesDeVenta(mCadenaConexion);
            return lLogicaOrdenesDeVenta.ObtenerOrdenesDeVentaPorIdCliente(pIdCliente);
        }
        public void IngresarOrdenDeVenta(OrdenDeVenta pOrdenDeVenta)
        {            
        }
        public void ElimindarOrdenDeVenta(OrdenDeVenta pOrdenDeVenta)
        {            
        }
        public List<Producto> ObtenerTodosLosProductos()
        {
            LogicaProductos lLogica = new LogicaProductos(mCadenaConexion);
            return lLogica.ObtenerTodosLosProductos();
        }
        #endregion
    }
}

Listado 3.- Clase ServicioGestionAplicacion.

Una vez creadas las clases que representan los componentes del lado del servidor, hay que pasar a crear todos los componentes que expondrán los servicios a través de un proxy. Como se está creando un servicio que expone operaciones de negocios implementadas en WCF, hay qué exponer en primer lugar el servicio a través del IIS, para lo cual es necesario implementar una clase de servicio .svc que se encargue de exponer todas las operaciones. Dado que estas operaciones estarán implementadas en la clase “ServicioGestionAplicacion”, hay que declarar en el archivo que define el servicio que clase implementa las operaciones y cuál es la clase de creación del mismo tal y como se muestra en el Listado 4. Además se necesita realizar las configuraciones de WCF para establecer como estará expuesto el servicio en el servidor y como será consumido por los clientes.

<%@ServiceHost language=c# Debug="true" 
Service="Msdn.ServiceApplication.ServicioGestionAplicacion, Msdn.ServiceApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=711eed342842acee" 
Factory="Msdn.ServiceApplication.ServicioGestionApplicationHostFactory, Msdn.ServiceApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=711eed342842acee" 
%>

Listado 4.- Definición de SevicioGestion.svc.

A continuación, se procederá a crear las clases del tipo proxy que se encargan de exponer el servicio a los clientes web y que estos podrán consumir a través de las correspondientes operaciones. En el Listado 5 se muestra el proxy de la aplicación de servicio. Esta última es la clase que se asociará en las aplicaciones web que tenemos creadas y que realizara la interconexión con los clientes web. Esta clase será utilizada desde una WebPart para poder consumir las operaciones.

namespace Msdn.ServiceApplication.AppServicioClientes
{
    [Guid("2C009794-837C-4401-B3C9-E002C100E9A7")]
    [IisWebServiceApplicationProxyBackupBehavior]
    public class ServicioGestionAplicacionProxy : SPIisWebServiceApplicationProxy, IOrdenesDeVenta
    {
        private ChannelFactory<IOrdenesDeVenta> mChannelFactoryOrdenes;
        private object mChannelFactoryLock = new object();
        private String mEndpointConfigurationName;

        [Persisted]
        private SPServiceLoadBalancer mBalancer;

        [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
        public ServicioGestionAplicacionProxy()
            : base()
        {
        }

        public ServicioGestionAplicacionProxy(string pNombre, SPIisWebServiceProxy pServicioProxy, Uri pServicioEndPointUri)
            : base(pNombre, pServicioProxy, pServicioEndPointUri)
        {
            mBalancer = new SPRoundRobinServiceLoadBalancer(pServicioEndPointUri);
        }

        public override string TypeName
        {
            get { return ("MSDN - Servicio de Gestion Aplicacion Proxy"); }
        }

        public override void Provision()
        {
            mBalancer.Provision();

            base.Provision();

            this.Update();
        }
        internal Configuration Configuration
        {
            get
            {
                return OpenClientConfiguration(SPUtility.GetGenericSetupPath(@"WebClients\ServicioGestion"));
            }
        }
        internal SPServiceLoadBalancer LoadBalancer
        {
            get
            {
                return mBalancer;
            }
        }
        public override void Unprovision(bool deleteData)
        {
            mBalancer.Unprovision();

            base.Unprovision(deleteData);

            this.Update();
        }
        public List<Entidades.OrdenDeVenta> ObtenerTodasLasOrdenesDeVenta()
        {
            List<OrdenDeVenta> lRetorno = null;
            ExecuteOnChannel("ObtenerTodasLasOrdenesDeVenta", lChannel => lRetorno = lChannel.ObtenerTodasLasOrdenesDeVenta());
            return lRetorno;
        }
        public List<Entidades.OrdenDeVenta> ObtenerOrdenesDeVentaPorIdCliente(int pIdCliente)
        {
            throw new NotImplementedException();
        }

        public void IngresarOrdenDeVenta(Entidades.OrdenDeVenta pOrdenDeVenta)
        {
            throw new NotImplementedException();
        }

        public void ElimindarOrdenDeVenta(Entidades.OrdenDeVenta pOrdenDeVenta)
        {
            throw new NotImplementedException();
        }
    }
}

Listado 5.- Clase ServicioGestionAplicacionProxy.cs

Llegados a este punto, el servicio implementado está disponible como una solución de SharePoint por lo que el próximo paso será instalarla en el servidor de manera que se desplieguen en el mismo todos los componentes desarrollados y se pueda comenzar a usar el servicio de gestión. En la Figura 5 se muestra el servicio disponible en la lista de servicios de cada servidor de SharePoint y el estado del mismo, que inicialmente es “Detenido” por lo que es necesario iniciarlo.

Figura 5.- Servicio de gestión instalado.

Una vez iniciado el mismo, se puede proceder a crear la correspondiente aplicación de servicio en SharePoint para poder asociarla a la aplicación o aplicaciones web dónde se vaya a utilizar. La Figura 6 muestra la aplicación de servicio creada con su correspondiente proxy de aplicación. Si al crear la aplicación de servicio, se deja marcada la opción de asociación al grupo por defecto de aplicaciones de servicio, todas las aplicaciones web que tengan configurado la opción por defecto tendrán asociada dicha aplicación.

Figura 6.- Aplicación de Servicio creada e iniciada.

Una vez la Aplicación de Servicio está iniciada y configurada, se puede proceder a consumirla desde una WebPart en una Aplicación Web de SharePoint. En el Listado 6 se muestra como obtener el proxy de la aplicación de servicio usando el tipo.

ServicioGestionAplicacionProxy lServicioGestion = (ServicioGestionAplicacionProxy)SPContext.Current.Site.WebApplication.ServiceApplicationProxyGroup.Proxies.OfType<ServicioGestionAplicacionProxy>().FirstOrDefault();
lServicioGestion.ObtenerTodasLasOrdenesDeVenta();

Listado 6.- Obtención del servicio de aplicación por el tipo.

También se puede usar una expresión para obtener una referencia al proxy de aplicación por una de las propiedades que este objeto tiene. Recuerde que una Aplicación Web puede tener una o más aplicaciones de servicio asociadas, incluso del mismo tipo de servicio y se debe poder tener acceso al servicio que se desee utilizar. El Listado 7 muestra el código para obtener el servicio usando una expresión para la propiedad nombre.

IEnumerable<ServicioGestionAplicacionProxy> lServicioGestionEx = SPContext.Current.Site.WebApplication.ServiceApplicationProxyGroup.Proxies.OfType<ServicioGestionAplicacionProxy>().Where(x => x.Name.Contains("MSDN Aplicacion Sistema Gestion"));

Listado 7.- Obtención del servicio de aplicación mediante una expresión LINQ.

Hasta ahora se ha visto cómo se puede obtener el servicio a través de la Aplicación Web donde se está ejecutando el código, pero también se puede obtener el servicio a través de la clase SPServiceContext del modelo de objetos, como se puede ver en el Listado 8.

ServicioGestionAplicacionProxy lServicioGestion = (ServicioGestionAplicacionProxy)SPServiceContext.Current.GetDefaultProxy(typeof(ServicioGestionAplicacionProxy));
lServicioGestion.ObtenerTodasLasOrdenesDeVenta();

Listado 8.- Obtención del servicio de aplicación a través de la case SPServiceContext.

Una forma alternativa para invocar una operación es la de llamar al método Invoke() que expone la clase “ServicioGestionAplicacionProxy” creada, esto dependerá de la visibilidad con la que se creará el método. En este caso, se ha creado dicho método público para poder consumirlo desde afuera del proyecto. Lo que se hará a continuación es crear una clase cliente que encapsule la llamada a los servicios y que permita desde los componentes web que se desarrollen llamar de una forma sencilla a las operaciones expuestas por nuestra aplicación de servicio. En el Listado 9 se detalla la definición de la clase cliente creada para el encapsulamiento de las llamadas.

namespace Msdn.ServiceApplication.Servicios.UI
{
    public class ServicioGestionOrdenesDeVentaCliente: IOrdenesDeVenta
    {
        private SPServiceContext mServiceContext;

        public ServicioGestionOrdenesDeVentaCliente(SPServiceContext pServiceContext)
        {
            if (pServiceContext == null)
                throw new ArgumentNullException("El contexto del servicio es nulo.");

            this.mServiceContext = pServiceContext;
        }

        public List<OrdenDeVenta> ObtenerTodasLasOrdenesDeVenta()
        {
            List<OrdenDeVenta> lResultado = null;
            ServicioGestionAplicacionProxy.Invoke(mServiceContext, proxy => lResultado = proxy.ObtenerTodasLasOrdenesDeVenta());
            return lResultado;
        }
        public List<Entidades.OrdenDeVenta> ObtenerOrdenesDeVentaPorIdCliente(int pIdCliente)
        {
            List<OrdenDeVenta> lResultado = null;
            ServicioGestionAplicacionProxy.Invoke(mServiceContext, proxy => lResultado = proxy.ObtenerOrdenesDeVentaPorIdCliente(pIdCliente));
            return lResultado;
        }
        public void IngresarOrdenDeVenta(Entidades.OrdenDeVenta pOrdenDeVenta)
        {
            ServicioGestionAplicacionProxy.Invoke(mServiceContext, proxy => proxy.IngresarOrdenDeVenta(pOrdenDeVenta));
        }

        public void ElimindarOrdenDeVenta(Entidades.OrdenDeVenta pOrdenDeVenta)
        {
            ServicioGestionAplicacionProxy.Invoke(mServiceContext, proxy => proxy.ElimindarOrdenDeVenta(pOrdenDeVenta));
        }
    }
}

Listado 9.- Clase cliente para encapsular las llamadas a las operaciones.

Por último, el Listado 10 muestra cómo debemos consumir la clase cliente creada pasando en el constructor una referencia de la instancia de la clase SPServiceContext. La clase cliente deberá tener las operaciones expuestas para que puedan ser invocadas, lo que permite poder tener un mayor nivel de control y validación de los datos antes de enviarlos para que se ejecuten dentro de los procesos de negocio. De esta forma se logra crear una capa de servicios para la UI.

ServicioGestionOrdenesDeVentaCliente lClinte = new ServicioGestionOrdenesDeVentaCliente(SPServiceContext.Current);
lResultado = lClinte.ObtenerTodasLasOrdenesDeVenta();

Listado 10.- Consumiendo servicio expuesto por una clase cliente.

Una vez construido el servicio y expuestas las operaciones de negocio, se implementarán los componentes web que consumirán los servicios correspondientes. En el ejemplo se han construido 3 WebParts para listar los datos desde la base de datos.

Figura 7.- Componente web que lista las órdenes de venta.

Figura 8.- Componente web que lista los productos.

Figura 9.- Componente web que lista los clientes.

Conclusiones

La arquitectura de aplicaciones de servicio en SharePoint 2010, como el resto de la plataforma, se caracteriza por su alto grado de flexibilidad y por ser completamente extensible de manera que es posible construir nuevas aplicaciones de servicio que proporcionen funcionalidades especialidades en un despliegue de SharePoint que puedan estar disponibles en toda la granja. Estas nuevas aplicaciones se beneficiarán de todas las ventajas y características de serie disponibles en el framework de aplicaciones de servicio: escalabilidad, consumo a la carta, soporte cross-granja, etc.

Referencias

[1]         Services Hierarchy of Microsoft SharePoint Foundation. Disponible online en inglés en: http://msdn.microsoft.com/es-es/library/cc768564.aspx.

[2]         SQL Server Databases Sample Products. Disponible online (inglés) en: http://msftdbprodsamples.codeplex.com/

Microsoft está realizando una encuesta en línea para comprender su opinión del sitio web de. Si decide participar, se le presentará la encuesta en línea cuando abandone el sitio web de.

¿Desea participar?