Comparativa de rendimiento de WCF

Acabo de encontrar una compartiva de Rendimiento entre WCF y las tecnologias anteriores de .NET con respecto a sistemas distribuidos.


podeis echarle un vistazo aqui


Como resumen


 


«To summarize the results, WCF is 25%-50% faster than ASP.NET Web Services, and approximately 25% faster than .NET Remoting.
Comparison with .NET Enterprise Service is load dependant, as in one case WCF is nearly 100% faster but in another scenario it is nearly 25% slower.
For WSE 2.0/3.0 implementations, migrating them to WCF will obviously provide the most significant performance gains of almost 4x.«


Publish/Subscribe en WCF

Este post nace a partir de un problema que me comento Rodrigo en el TTT (hay que ver como se pone cuando tiene un problema da miedo!!). No voy a comentar el problema de Rodrigo, sino que voy a explicar como se realiza un sistema Publish/Subscribe en WCF.

Lo primero es explicar que en el sistema Publish/Subscribe los cliente se subscriben en el servidor a determinadas operaciones y el servidor les envia mensajes cuando una operación determinada ocuerre en el servidor. Con este sistema estamos evitando el sistema de que el cliente este preguntando periodicamente al servidor si ha ocurrido el evento que espera, sino que es el servidor quien informa a todos los clientes que se hayan suscrito cuando se produzca el evento.

Con remoting teniamos tambien esta posibilidad a traves de MarshalByRefObject, ahora en WCF lo implementamos a traves de CallbackContract, basicamente en la definición del servicio especificamos que interfaz va a ser usuado en el callback. Un interfaz callback podemos decir que es un interfaz en las que se definen las operaciones que el servidor va a poder enviar a los clientes suscritos.

Para explicar mejor esto como siempre mejor con un ejemplo, para el ejemplo vamos a mostrar un sistema de alarma en el cual los clientes se susbcribiran con una hora a la cual quiere que se les recuerde y un mensaje, y el servidor se encargara de resordarselo cada minuto. Un ejmeplo sencillo pero que nos da idea de lo que podemos alcanzar.

Lo primero que vamos a ver es el servidor en el definimos la interfaz 

 
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;

namespace ContractsLibrary
{
    [ServiceContract(
        Name="AlarmServer",
        CallbackContract=typeof(IAlarmCallback),
        SessionMode=SessionMode.Required)]
    public interface IAlarmServer
    {
        [OperationContract(IsOneWay = true)]
        void RegisterAlarm(DateTime alarmTime, string clientName, string reminderMessage);
    }
}

Como podemos ver en los atributos del servicio hemos definido cual es el contrato del callback (interfaz) que deben de cumplir los clientes. Por una parte nosotros solo tenemos un metodo que el el registro de la hora de alarma, mensaje y cliente

En el mismo sevidor debemos de definir el interfaz IAlarmCallback que en nuestro caso es

 

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;

namespace ContractsLibrary
{
    public interface IAlarmCallback
    {
        [OperationContract(IsOneWay = true)]
        void SignalAlarm(string reminderMessage);
    }
}

Este interfaz debe de ser implementado en la clase cliente, como ya veremos. para terminar con el servidor implementamos el interfaz IAlarmServer que utilzaran los clientes para subscribirse. El codigo en este caso seria

 

 

using System;
using System.Timers;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using ContractsLibrary;

namespace AlarmHost
{
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
    public class AlarmServer : IAlarmServer
    {
        private RegisteredAlarm alarm = null;
        private Timer _timer;

        public AlarmServer()
        {            
            _timer = new Timer();
            _timer.Elapsed += new ElapsedEventHandler(_timer_Elapsed);
            _timer.Interval = 60000; // 1 minuto
            _timer.Start();
        }

        private void _timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if ((DateTime.Now <= alarm.AlarmTime) && !alarm.Fired)
            {
                alarm.Callback.SignalAlarm(alarm.Message);
                alarm.Fired = true;
            }
        }
        #region IAlarmServer Members

        public void RegisterAlarm(DateTime alarmTime, string clientName, string reminderMessage)
        {
            alarm = new RegisteredAlarm();
            alarm.AlarmTime = alarmTime;
            alarm.ClientName = clientName;
            alarm.Message = reminderMessage;
            alarm.Callback = OperationContext.Current.GetCallbackChannel<IAlarmCallback>();
            
            System.Diagnostics.Debug.WriteLine("alarma registrada para el cliente" + clientName + ".");
        }

        #endregion
    }
}

En este caso si nos fijamos hemos puesto que el modo de instanciacion sea por sesion pero tambien podemos poner Singleton, eso dependera de como queremos utilizarlo. La parte importante de este codigo es la linea

alarm.Callback = OperationContext.Current.GetCallbackChannel<IAlarmCallback>();

En esta linea estamos guardando en la propiedad Callback una referencia al contrato de devolución de llamada llamando a OperationContext.Current.GetCallbackChannel para realizar la devolución de llamada.

La propiedad Callback de alarma tiene la definición

public IAlarmCallback Callback
        {
            get { return _callback; }
            set { _callback = value; }
        }

Es decir es del tipo del interfaz que hemos definidio como Callback.

Por ultimo nos queda en el servidor definir nuestro fichero de configuración

 

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>

    <services>
      <service behaviorConfiguration="MetaEnabledBehavior" name="AlarmHost.AlarmServer">
        <host>
          <baseAddresses>
            <add baseAddress="net.tcp://localhost:8080/AlarmServer"/>
          </baseAddresses>
        </host>
        <endpoint address="" binding="netTcpBinding" contract="ContractsLibrary.IAlarmServer"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MetaEnabledBehavior">
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Ahora en nuestro cliente creamos el proxy con svcutil como siempre, pero nos queda implementar el interfaz de callback que en nuestro caso era IAlarmCallback, pero debemos de implementar la que se genere cuando creamos el proxy en el cliente, en nuestro caso la implementación sera

 

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace AlarmClient
{
    public class AlarmCallback : AlarmServerCallback
    {
        #region AlarmServerCallback Members

        public void SignalAlarm(string reminderMessage)
        {
            MessageBox.Show("El servidor te recuerda que: " + reminderMessage);
        }

        #endregion
    }
}

Una vez implementado en el cliente solo nos que el codigo que nos permite subscribirnos al servicio que en este caso sera

 

namespace AlarmClient
{
    public partial class MainForm : Form
    {
        private AlarmServerClient client = null;
        public MainForm()
        {
            InitializeComponent();
            InstanceContext context = new InstanceContext(new AlarmCallback());
            client = new AlarmServerClient(context);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            client.RegisterAlarm(dateTimePicker1.Value, clientName.Text, reminderMessage.Text);
            MessageBox.Show("Alarma registrada en el cliente.");
        }
    }
}

De esta manera podemos tener diferentes clientes que se han suscrito al sistema de alarma y el servidor les respondera a todos ellos a traves de SignalAlarm.

 

Existen otras formas de implementar Publish/Subscribe como son usando MSQM o Straming, intentare hablar de ellas mas adelante si el trabajo me lo permite.

Problemilla con DSL Tools

Desde hace algún tiempo estoy en un proyecto de DSL Tools (ademas de otros 4), pñor eso me esta siendo imposible escribir. Este post es para autorecordarme y comentar un problemilla que pasa cuando se desarrolla con DSL Tools. El problema es que al Debuggear y pulsar con el botón derecho del ratón en el modelo para que se muestre el menu aparece el mensaje

{«Error HRESULT E_FAIL has been returned from a call to a COM component.»

Despues de volver un poco loco, he descubierto que DSL cachea los menus y a veces se corrompe esta cache. Para limpiar esta cache debemos ejecutar el comando

devenv /setup /rootsuffix Exp

Y a funcionar…

 

Espero que dentro de poco os pueda escribir algún post sobre DSL Tools, la cual me parece interesentisimo

 

Saludos Oskar

Añadir en Vista al Menu de Inicio la Opción Herramientas Administrativas

Ayer de churro, para que lo vamos a negar (estaba enseñando el ordenador a mi hijo de 4 meses), pulse el botón derecho del ratón en el Botón me aparecio el menu

Pulsando en propiedades por curiosidad me aparece la siguiente ventana

Pulsando en Personalizar lo siguiente que aparece una serie de opciones para personalizar el menu de inicio y entre ellas la opción de mmostrar en el menu de inicio las herramientas administrativas

 

De manera que ahora ya me aparece las herramientas administrativas en el menu de incio algo que echaba de menos

 

Behaivor en WCF

El Behaivor refleja el comportamiento de los servicios en tiempo de ejecución, estos tipos de comportamiento pueden ser la instanciación, concurrencia, Throlling, manejo de errores,Metadata, Tiempo de vida del servicio, Seguridad y transacciones.

Todos estos comportamiento pueden ser especificados como parámetros del contrato, en archivos de configuración o mediante la codificación, es decir, como siempre en WCF

Si nos fijamos en la instanciación, este comportamiento determina como se instancian los contratos de un servicio WCF, esta instanciación puede ser de los tipos:

  • Singleton: una instancia atiende a todos los clientes.
  • Por llamada: se crea una instancia de clase por cada mensaje de petición y luego se destruye.
  • Sesión privada: se crea una instancia de clase por cada solicitud de un cliente.
  • Sesión compartida: idem anterior pero los clientes pueden compartir una instancia.

En concurrencia vamos a señalar como se relacionan las clases del contrato con los Threads, estas pueden ser

  • Simple: especifica que el código del objeto servicio no puede ser ejecuto por más de un thread al mismo tiempo.
  • Múltiple: especifica que el código del objeto servicio puede ser ejecutado por múltiples threads al mismo tiempo por lo que se deben establecer mecanismos de exclusión mutua.
  •  Reentrante: especifica que el código del objeto servicio no puede ser ejecutado por más de un thread al mismo tiempo. Es decir una operación recibe y procesa de a un mensaje por vez y se bloquea. Si durante ese proceso se produce una llamada fuera de la operación, el mensaje pierde el bloqueo y la operación se libera, durante esta fase los datos no locales a la operación podrían ser modificados. Cuando la llamada vuelva, el mensaje original recupera el bloqueo y se continúa procesando hasta que finalice o se realice otra llamada hacia fuera, al volver también hay que asegurarse que los datos no locales sigan siendo válidos.

En Manejo de errores determinamos si los errores pueden ser procesados por el programador o por el propio framework.

En Metadata, se establece la publicación de los metadatos que nos brinda el servicio

Un servicio puede controlar el tiempo de vida de las sesiones de un cliente, especificando las operaciones de inicio y fin de sesión siendo otro behaivor .

Con la Seguridad establecemos la confidencialidad e integridad de los mensajes, autenticación, autorización, auditoría y detección de reintentos.

A traves de las Transacciones especificamos si un servicio aceptará transacciones y el cliente controla el alcance y el tiempo de vida de una transacción

Como podemos ver tenemos una gran cantidad de comportamientos que podemos utilizar para nuestras aplicaciones de WCF y que debemos de saber cuando y donde utilizarlos.

 

Por ultimo decir que seguro que todos coneceis que WCF trae la herramienta SvcConfigEditor que brinda una interfaz gráfica para editar archivos de configuración que nos va a permitir configurar d euna manera sencilla todos los puntos anteriores que hemos dicho

serviceconfiguratoreditorsmall.JPG

Enterprise Library y Procedimientos Almacenados en Oracle

Como siempre ya he recibido la primera bronca de un compañero del curro que siempre me acusa de comentar que pasa entre Enterprise Library y los procedimientos almacenados en Oracle (el es un guru de Oracle), y tiene razón. Cuando utilizemos procedimientos alamacenados en Oracle y Enterprise Library debemos de tener en cuenta ala hora de hacer estos procedimientos de tener una variable OUT de tipo cursor con el nombre cur_OUT.

Nosotros en codigo, debemos de obviarlo como si no existiese pero en el procedimiento almacenado debe existir sino nos dara un error.

 

Procedimientos

PROCEDURE GET_USER_BYID( P_USER_ID_IN IN NUMBER, cur_OUT OUT REF CURSOR)

Funciones

FUNCTION GET_USER_BYID( P_USER_ID_IN NUMBER ) RETURN REF CURSOR

Enterprise Library 2.0 Bloque de Acceso a Datos

 

Ya ha habido varias personas que leen mi blog (que gente rara esa) que me han preguntado como utilizar Enterprise Library, no es que sea un experto pero voy a tratar de forma resumida, aunque creo que a Miguel Jimenez no le va a gustar este post de lo extenso que es.

Es una forma simple y eficiente para trabajar con bases de datos comunes (Sql Server, Oracle…), nos permite transparencia en el desarrollo de múltiples tipos de bases de datos, así como una abstracción entre la base de datos física y lógica. Por ultimo es una forma fácil de ajustar y validar la configuración de la base de datos.

Resumiendo nos permite de una manera facil y comoda poder desarrollar nuestra aplicación y que este abierta a cualquier base de datos en la cual queramos implementar, pero lo mas importante es que se va a estandarizar el manejo de la base de datos en todo la aplicación, evitando diferentes formas de desarrollo y evitando errores comunes.

Vamos a ver el siguiente trozo de código

En este trozo de código podemos ver que la base de datos a utilizar tiene que ser Sql Server, se inicia una transacción, se asigna el comando, se asigna los parámetros y se ejecuta el comando.

Si cambiase a Oracle, debería de cambiar el tipo de comando y las arrobas de los parámetros, significa realmente cambiar mucho código la migración de una base de datos a otra.

Como Utilizar Enterprise Library

Lo primero que debemos de hacer es configurarlo, para ello ejecutaremos la herramienta EntLibConfig.exe que se encuentra junto a las dll’s de Enterprise Library,

Veremos que al ejecutarla aparece la siguiente pantalla

Abrimos un fichero de configuración de la aplicación (web.config o app.config)

Exportar_diapositiva001

Exportar_diapositiva014

Nos muestra la siguiente pantalla

Exportar_diapositiva019

Como podemos observar nos muestra el Connection Strings que tenemos configurado en nuestro fichero de configuración. Tambien podiamos añadirlo desde aquí.

Ahora deberemos de utilizar Enterprise manager.

En el trozo de código anterior podemos ver que no indicamos el tipo de base de datos, porque enterprise manager cuando lea el fichero de configuración instanciar a las clases correspondientes a la base de datos adecuada.

Lo único que hacemos es crear una instancia de la base de datos a traves de la Interface DataBaseFactory, que no sabemos cual es. En el primer trozo cojera la por defecto del fichero de configuración, y si tenemos mas de una base de datos, en el constructor podemos indicarle cual de ellas.

Una vez instanciada la base de datos debemos de ejecutar las sentencias o procedimientos almacenados, no voy a entrar en que es mejor o no para eso hay multiples discusiones en todos los foros.

En la siguiente tabla tenemos los comandos mas utilizados que tienen multiples overloads

  • ExecuteReader():Ejecuta el commando y retorna un IDataReader. Es responsabilidad del llamador cerrar la connexion para que el reader sea destruido.
  • ExecuteDataSet():Ejecuta un comando y retorna un resultado en el Dataset.
  • LoadDataSet():Ejecuta un commando y añade una nueva DataTable al Dataset.
  • ExecuteNonQuery():Ejecuta el commando y retorna el número de filas afectadas.
  • ExecuteScalar():Ejecuta el commando y retorna la primera columna de la primera fila del resultado.
  • UpdateDataSet()_Llma a los respective INSERT, UPDATE, o DELETE sentencias pata cada fila insertada, modificada o borrada en el Dataset.

Como podeis ver realmente las sentencias son muy parecidas a las de .NET solo que en este caso nos abstrae del tipo base de datos a utilizar que configuramos en nuestro app.config o web.config.

RuleSet en Validation Application Block

Despues de un tiempo sin escribir, debido a unas merecidas vacaciones y al exceso de trabajo generado por las vacaciones, vuelvo a la carga, esta vez con una caracteristica que nos trae Validation Application Block de Enterprise Library 3.0.

Hay muchas veces que las validaciones de la entidad de negocio no son iguales en toda nuestra aplicación y que requieren reglas diferentes, para solucionar esto los chicos de Enterprise Library nos han creado RuleSet que me permite poner atributos de validación que se validarán asociados a una regla que yo defina. El ejemplo seria

 

public class Subscriber
{
    private string _name;

    [NotNullValidator(Ruleset="Persistence")]
    [StringLengthValidator(1,200)]
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    private string _emailAddress;

    [NotNullValidator(Ruleset="Persistence")]
    [EmailAddressValidator]
    public string EmailAddress
    {
        get { return _emailAddress; }
        set { _emailAddress = value; }
    }

    public Subscriber() {}
}

En este caso hemos definido la regla Persistence, para validar la entidad de negocio asociada a esta regla

, es tan sencillo como

 

Validation.Validate(subscriber, "Persistence");

o

 

IValidator<Subscriber> subscriberValidator =
  ValidationFactory.CreateValidator<Subscriber>
  ("Persistence");
ValidationResults results = subscriberValidator.
  Validate(subscriber);

Como podemos ver nos va a permitir tener centralizadas todas las validaciones de una entidad de negocio, de manera que el mantenimiento se facilita enormemente y la facilidad de programarlas tambien.

InternalsVisibleTo

Hoy he descubierto el atributo InternalsVisibleTo, este atributo esta definido en el namespace System.Runtime.CompilerServices. Nos permite utilizar metodos y tipos internos de un assembly.

si yo tengo un assembly que implementa la siguiente clase

internal class MiInternalClass
{
public void Metodo1() {...}
internal void InternalMetodo() {...}
}

Si yo en mi proyecto, al cual le he añadido la referencia del assembly donde se encuentra esa clase, le añado al fichero AssemblyInfo.cs la linea

[assembly: InternalsVisibleTo(«NombreAssembly»)]

En mi proyecto podre utilizar MiInternalClass y llamar a los emtodos publicos o internos.

Hay que reseñar que utilizando esta tecnica violamos la encapsulación de los internals que declaramos en nuestros assemblies, pero puede haber casos que nos venga bien esta tecnica como por ejemplo si tenemos un assemblie ya creado y lo queremos dividir en varios assemblies.