Development

WCF – EndPoints

Introducción

l  Describen la ubicación de un servicio.

l  Asociados a una dirección y a un contrato del servicio.

l  Un servicio debe proveer al menos un endpoint para ser accesible.

l  También pueden proveer acceso a la metadata de un servicio.

Cumplen una función básica pero fundamental si hablamos de aplicaciones distribuidas, ya que éstos informan dónde se encuentra el servicio y, de esta forma, un cliente puede hacer uso del servicio.

Se encuentran ligados a una dirección y un contrato del servicio. Éstas son las partes que los componen.

Un servicio, para poder funcionar como tal, siempre deberá exponer al menos un endpoint y podrá contener más de uno.

También permitirá acceder a la información de la metadata del servicio mediante otro tipo de endpoint.

l  Compuestos por: Address + Binding + Contract.

l  Punto de acceso del servicio.

l  Debe existir al menos uno por servicio.

Un servicio debe proveer al menos un endpoint para que éste sea accesible, y puede tener múltiples endpoints. Un ejemplo de servicio simple se compone de un solo endpoint, un solo Service Contract y un solo binding, a diferencia de un servicio complejo, que podría tener numerosos endpoints, bindings y contracts.

Diseño de un endpoint

Cuando tenemos un contract y un binding en nuestro servicio, podemos designar un endpoint. Esto implica que se deberá seleccionar una dirección (por ejemplo, http://www.miaplicacion.com/servicio/) y asociarla al Service Contract y al binding que se seleccionaron previamente.

Esto puede ser realizado especificándolo en el archivo de configuración.

¿Qué son las address?

Las address representan la ubicación del endpoint de un servicio. La información de una address consta de los siguientes datos:

* El protocolo de transporte para utilizar:

                Qué protocolo de transporte se usará (TCP, HTTP).

* El nombre de la máquina donde está corriendo el servicio.

                Nombre del “target”, máquina donde se encuentra corriendo el servicio.          

* La ruta de acceso a la máquina que identifica el servicio específico al que se accederá.

                Ruta completa de acceso a la máquina donde se encuentra el servicio.

Tipos de address

Se encuentran distintos tipos de direcciones.

Endpoint Address: es la dirección del endpoint de un servicio al que un cliente puede acceder para utilizar el servicio. Por ejemplo, podría darse de la siguiente manera: http://localhost:8000/MiServicio/.

Un cliente podría dar con ese endpoint address para utilizar el servicio “Miservicio”.

MEX Address: es la dirección de intercambio de metadata. Una endpoint address del tipo HTTP a la cual puede accederse para obtener información del servicio; por ejemplo, http://localhost:8000/MiServicio/. Un cliente puede dar, en este caso, con la MEX address para obtener información del servicio “MiServicio”.

Base Address: es la dirección primaria asociada a un servicio, utilizando una dirección base.

Formatos de address

Existen varios tipos de formatos de direcciones.  Estos formatos están sujetos al tipo de transporte que utilicemos.

La primera parte de una dirección es el esquema que identifica el transporte. El entorno de hosting (donde se alojará el servicio) usualmente impone reglas en el formato de las direcciones.

La persona que desarrolla un servicio selecciona la dirección para los endpoints de éste. El cliente que quiera acceder deberá saber la dirección del endpoint para poder hacer uso del servicio.

Las direcciones deberán ser especificadas mediante código o un archivo de configuración.

l  HTTP Addresses: De uso más frecuente, utilizan protocolo HTTP.

l  HTTPS Addresses: Acceso seguro, utilizan sockets SSL.

l  TCP Addresses: Utilizan protocolo TCP (el esquema net.tcp)

l  Named Pipe Addresses: Utilizan el esquema net.pipe

Con Named Pipe se encuentran 2 diferencias para tener en cuenta:

– La comunicación no puede establecerse a través de distintas máquinas, no es “cross machine”.

– En segundo lugar, los números de puerto son relevantes.

Un ejemplo de dirección Named Pipe podría ser el siguiente:

Net.pipe://localhost/Servicio.

MSMQ Addresses

Las direcciones MSMQ utilizan el esquema net.msmq.

Net.msmq:// nombre-maquina/ tipo de cola / nombre de cola

La estructura difiere de las mencionadas anteriormente.

IIS Addresses

Los servicios que son alojados en IIS siguen un esquema de direccionamiento distinto.

La dirección debe incluir el nombre de un directorio virtual y el archivo .SVC.

Diferente esquema de direccionamiento

http:// nombre-maquina [:puerto] [/ruta directorio virtual] arvhivo.svc 

specificando direcciones endpoint

Mediante código, las direcciones de endoints se pueden especificar de varias maneras. Las direcciones pueden ser del tipo string, Uri o EndpointAddress.

Ejemplo: Mediante código:

Uri address = new Uri("http://localhost:8000/Servicio/Prueba");

            ServiceHost<wcfTesting> service = new ServiceHost<wcfTesting>();

            service.AddEndpoint(typeof(Iwcftest), binding, address);

Ejemplo: Mediante  un archivo de configuración

<endpoint configurationName= “EjemploEndpoint” address= “http://www.miaplicacion.com/servicio/service.svcbinding= “wsProfileDualHttpBinding” contract=“ISampleContact" />

Es importante aclarar que, en ambos casos, las direcciones pueden ser relativas o absolutas.

Publicando y exportando metadata

Importando y exportando metadata con Svcutil

l  Herramienta Svcutil

Ø  Generar código de la metadata de un servicio desde el endpoint de un servicio MEX activo.

Ø  Generar código de la metadata de un servicio leyendo un WSDL y archivos XSD.

Ø  Generar metadata del servicio WSDL y archivos XSD desde el assembly de un servicio.

WCF provee una herramienta disponible para ser utilizada por consola, que permite la importación y la exportación de la metadata de un servicio.

Como puede verse, esta herramienta tiene distintas utilidades y, a su vez, puede recibir algunos parámetros opcionales al utilizarla.

Demo: Generando WSDL y archivos XSD desde el assembly de un servicio:

Una vez que contamos con nuestro servicio compilado, svcutil puede generar los archivos de metadata desde el servicio.

Ejecutar la línea de comando: svcutil miServicio.dll

Controlando la publicación en un archivo de configuración

La publicación de la metadata se puede controlar dentro de un archivo de configuración utilizando el elemento <metadataPublishing>.

Este elemento, que se incluye dentro del elemento <behavior>, contiene tres propiedades:

         enableGetWsdl: si se encuentra en true, esta propiedad provoca que el servicio responda a un pedido HTTP GET para la dirección del servicio con el sufijo ?wsdl.

         enableHelpPage: si se encuentra en true, esta propiedad provoca que se muestre una página de ayuda una vez que el servicio es accedido a través del browser.

          enableMetadataExchange: si se encuentra en true, esta propiedad expone un MEX para el servicio.

Controlando las propiedades en el behavior

En este ejemplo puede observarse cómo, a través de un behavior, se configuran las tres propiedades disponibles, y de esta forma se logra controlar la publicación de la metadata.

<behavior>

      <behavior configurationName="miServicioBehavior">

            <metadataPublishing enableGetwsdl="false"

                  enableHelpPage="false"

                  enableMetadaExchange="false" />

      </behavior>

</behavior>

 

MEX Endpoints

Como se mencionó anteriormente, los endpoints del tipo MEX (Metadata Exchange Endpoint) cumplen la función de exponer información de la metadata de un servicio.

A éstos se puede acceder mediante HTTP, como lo muestra el ejemplo.

Un cliente puede acceder a nuestro MEX endpoint y, así, obtener información sobre el servicio que se encuentre publicado

 

Development

WCF – Message Contracts y Clase Binding

Message Contract

l  Describe la estructura del mensaje.

l  Define el contenido de la cabecera y el cuerpo del mensaje SOAP.

l  Interoperabilidad con otras plataformas.

l  Convierte la clase en un mensaje SOAP.

Trabajando con servicios, al enviar un mensaje, WCF se encarga de su creación, entrega, etc., lo cual no presenta problema alguno, a menos que estos mensajes deban tener alguna estructura, en especial para poder realizar la comunicación entre las partes.

Es por eso por lo que se encuentra el Contrato de Mensaje, que permite crear o definir la estructura del mensaje por enviar, y evitar problemas de compatibilidad entre las partes.

Al poderse controlar el mensaje, el contenido de éste, tanto en la cabecera como en el cuerpo, se puede representar en un formato específico, y lograr así la interoperabilidad con diferentes plataformas o sistemas.

Los mensajes tipados brindan facilidad de uso, ya que no se tiene que manipular el mensaje XML porque los datos del mensaje están enlazados dentro de su estructura.

Se declara por medio de una clase con las etiquetas que se verán a continuación, y éste luego es convertido en un mensaje SOAP con el formato definido en la clase.

Message Contracts – Atributos

El atributo [MessageContract] declara una clase como una estructura de mensaje.

[MessageHeader] permite definir el contenido del encabezado del mensaje.

[MessageBody] permite definir el contenido del cuerpo del mensaje.

Demo:

namespace RMS.Service.MessageContract

{

    [MessageContract]

    public class SolicitudItem

    {

        [MessageHeader]

        public string NroItem;

        [MessageBodyMember]

        public Item ItemSolicitado;

    }

}

—–

[ServiceContract]

    public interface IEjemploContrato

    {

        [OperationContract(IsOneWay = true)]

        void ProcesarMensaje(SolicitudItem message);

    }

[MessageContract] – Parámetros

Action(string): especifica la acción que realizará el mensaje.

WrapperElement: incluye todos los elementos del cuerpo del mensaje cuando el mensaje es formateado en un documento XML.

El cuerpo del mensaje puede ser formateado en varios estilos (por medio de la propiedad Style de [ServiceContract]) al ser convertido a formato XML. Los WrapperElements agrupan toda la información del cuerpo del mensaje dentro del elemento definido.        

Por medio del WrapperElementNameSpace se define un namespace para el WrapperElement, y con el WrapperElementName se establece el nombre del elemento.

[MessageHeader] – Parámetros

Actor(string): los mensajes SOAP tienen la propiedad Actor, que dentro del encabezado define el mensaje a un endpoint específico. La propiedad Actor recibe un URI (string).

MustUnderstand(boolean): por medio de esta propiedad, se le indica al receptor que debe reconocer el miembro del encabezado determinado.

Relay(boolean): si la propiedad tiene el valor true, cada endpoint indicado en la propiedad Actor, al recibir y terminar de procesar el mensaje, deberá reenviar el mensaje al próximo endpoint.

Clase Binding y miembros

Como mencionamos anteriormente, los bindings son los encargados de describir de qué manera se comunicarán los EndPoints de un servicio.

Los bindings determinan el método de transporte, la codificación del mensaje, los requerimientos de seguridad, las sesiones confiables y las transacciones.

Los bindings están compuestos por elementos binding. Podemos crear bindings personalizados, pero contamos con nueve bindings estándar para los escenarios más comunes.

Los bindings pueden ser programados por código o mediante archivo de configuración.

Un binding está compuesto por un nombre, un namespace y una colección de elementos binding.

El nombre y su namespace identifican al binding en la metadata del servicio.

Cada elemento binding restante describe cómo un EndPoint se comunicará con el exterior.

Especificaciones de bindings

l  Transporte de comunicación utilizado.

l  Requerimientos de seguridad.

l  Requerimientos de sesiones confiables.

l  Requerimientos de transacciones.

l  Forma de codificación.

Perfiles de bindings

profilebinding_01

En la imagen se observan los tres elementos que componen el BasicProfileBinding.

Éste es el único binding estándar que es compatible con la primera generación de Web Services. Esta gran interoperabilidad con la que cuenta este perfil tiene su costo. Seguridad SOAP, confiabilidad y novedades de transacción no están disponibles. Este perfil permite seguridad HTTPS. El transporte utilizado es HTTP o HTTPS, y los mensajes tienen “text encoding” o codificación de texto.

Windows Communication Foundation provee nueve tipos de perfiles estándar para los escenarios más comunes.

profilebinding_02

Si ninguno de los perfiles estándar se ajustara a las necesidades de nuestro entorno, podríamos crear perfiles propios.

Programando bindings

Los desarrolladores pueden hacer uso de los bindings estándar, pero también tienen la posibilidad de crear bindings personalizados. Éstos pueden ser definidos, configurados y especificados mediante código o archivo de configuración.

Cuando un binding es definido para un endpoint en código, éste debe crearse de una de las clases binding. Existen clases binding para cada uno de los bindings estándar, como también una clase CustomBinding para bindings estándar.

Especificando un binding para un endpoint en un archivo de configuración

Clientes y servicios tienen que definir endpoints. Los servicios deben crear endpoints y los clientes deben acceder a ellos. Esta información puede ser especificada en código o en un archivo de configuración.

Cuando definimos un binding para un endpoint en un archivo de configuración, debemos especificar el tipo de binding. Esto es posible haciendo uso del atributo Binding del elemento endpoint.

<endpoint address="http://localhost/MyService/service.svc"

          contract="ISampleContract" binding="wsProfileBinding"></endpoint>

Creando un binding por código

Los bindings en código son creados utilizando una de las clases estándar mostradas en la tabla anteriormente.

En el código de ejemplo, el binding es creado instanciando la clase WSProfileBinding y luego agregando un endpoint al servicio.

WSProfileBinding binding  = new WSProfileBinding();

  service.addEndpoint(typeof(IMyService), binding, address);

Creando un binding personalizado por código

Los custom bindings son creados con la clase CustomBinding. El constructor acepta un número variable de elementos binding. El código aquí presentado crea un custom binding con sesiones confiables, composite duplex y transporte HTTP.

Cuando se crea un custom binding, es importante que los elementos se agreguen a éste en el siguiente orden:

          Flujo de contexto.

          Sesiones confiables.

          Seguridad.

          Duplex compuesto.

          Transporte.

          Codificación del mensaje.

CustomBinding binding = new CustomBinding(new ReliableSessionBindingElement()

   new CompositeDuplexBindingElement(), new HttpTransportBindingElement()));

  service.addEndpoint(typeof(IMyService), binding, address);

Estableciendo propiedades estándar

Tenemos la posibilidad de ajustar con precisión el behavior de un binding modificando sus propiedades. Cabe aclarar que no todos los bindings contienen cada una de estas propiedades, esto depende del tipo de binding.

Aquí presentamos algunas de las tantas propiedades que podemos ajustar:

          HttpAuthentication: esta propiedad es utilizada para configurar opciones de autenticación HTTP. Se aplica sólo cuando el modo de seguridad es HttpAuthenticationOverHttps.

          MaxConnections: esta propiedad controla el número máximo de conexiones. El valor por default es 10.

          SecurityMode: esta propiedad se utiliza para especificar qué tipo de seguridad se usará. Las opciones varían dependiendo del binding. Muchos bindings tienen, por default, seguridad Windows.

          SessionInactivityTimeout: aquí se especifica el tiempo máximo que puede pasar antes de que se termine una sesión por inactividad. El valor predeterminado es 5 minutos.

          TransferTimeout: esta propiedad especifica el tiempo máximo de transporte que puede demandar un mensaje. El valor predeterminado es 10 minutos.

Development

WCF – Service Contracts, Data Contracts y Message Contracts

Service Contract

l  Define lo que puede hacer un servicio.

l  Permite la interoperabilidad de un servicio.

Un contrato no es más ni menos que un acuerdo entre las partes involucradas, que define la manera en que se va a trabajar y con qué elementos. En este caso se especificará qué funciones se van a ofrecer, qué tipos de datos se van a recibir o enviar, y cómo van a ser enviados.

Los contratos son conceptos fundamentales a la hora de trabajar con WCF. Éstos permiten que los clientes y los servicios tengan el mismo conocimiento de las operaciones ofrecidas, las estructuras de datos y la estructura de los mensajes.

Para cada tipo de información del servicio proporcionado, existe una clasificación de contratos:

Service Contract: describe las operaciones que ofrece un servicio.

Data Contract: describe las estructuras de datos manejadas por las operaciones del servicio.

Message Contract: define el contenido de un mensaje, ya sea en la cabecera o en el cuerpo.

Conversión de los contratos a un formato de plataforma independiente.

serviceContract

Los contratos son definidos por el CLR por medio de clases o interfaces. Éstas, al ser utilizadas en los servicios, son convertidas a un formato común para que el servicio pueda ser usado por distintas plataformas con total compatibilidad.

Definir Service Contract

Service Contract describe las operaciones que provee un servicio. Convierte los métodos de la interfaz de un servicio en una descripción de plataforma independiente (WSDL), y define el patrón de mensajes utilizado en el servicio.

Un Service Contract se puede definir de dos maneras:

          Por medio de una interfaz con el atributo [ServiceContract]. Se identifican sus operaciones con el atributo [OperationContract].

          Por medio de una clase en la cual, al declarar la clase, se deberá escribir el atributo [ServiceContract], y a cada uno de sus métodos, el atributo [OperationContract].

Definir Service Contract en una interfaz

Demo:

namespace RMS.Service.Contracts

{

    [ServiceContract(SessionMode=SessionMode.Required)]

    public interface Iwcftest

    {

        [OperationContract]

        VendorContract[] GetVendors();

    }

}

[ServiceContract] – Parámetros opcionales

CallbackContract (type): es utilizado cuando el patrón de mensajes es Duplex (es decir, de dos vías de comunicación).

El contrato del cliente de llama Callback Contract.

Por default, no se encuentra habilitado.

FormatMode(ContractFormatMode): selecciona un serializer. Un serializer es una clase que se encarga de la serialización y deserialización de los datos. El predeterminado es XmlFormatter.

Name(string): define el nombre del contrato. En caso de omitirlo, le asigna el nombre de la clase o interfaz.

NameSpace: define el namespace del contrato. En caso de omitirlo, le asigna el namespace de la clase o interfaz.

Session(boolean): indica si el contrato requiere una sesión.

Style(ServiceOperationStye): indica la manera en que el WSDL Metadata puede ser convertido:

          document/wrapped,

          document/bare,

          RPC message.

Si se desea cambiar el estilo del formato, puede hacerse tanto en el atributo [ServiceContract], que afecta a todas las operaciones del contrato, como en [OperationContract], que afecta a la operación definida.

Use: determina si el mensaje será codificado (encoded) o no (literal). En caso de ser codificado, también se deberá indicar el serializador utilizado. Este parámetro puede ser utilizado a nivel del contrato completo ([ServiceContract]) o a nivel de una operación ([OperationContract]).

[OperationContract] – Parámetros opcionales

Action(string): indica el nombre de la operación que será ejecutada por el servicio. En el caso de un servicio no tipado, si se especifica *, traerá todas las operaciones del servicio.

AsyncPattern(boolean): determina si la operación se ejecuta de manera asincrónica o sincrónica. En el caso de que opere de manera sincrónica, al llamar a la operación, el cliente se bloquea esperando a que la operación termine. En este caso utiliza un patrón de mensajes Duplex.

Si, en cambio, opera de manera asincrónica, al llamar a la operación, el cliente no tiene que esperar a que ésta termine y, mientras tanto, puede ejecutar otras tareas.

IsOneWay(boolean): indica si la operación trabaja en una dirección (true) o en dos direcciones (false).

Al comunicarse en una dirección (con el patrón de mensajes Simplex), trabaja de manera asincrónica, ya que al llamar a la operación, el cliente puede realizar otras tareas, porque no recibe ninguna respuesta que indique la finalización de ésta.

Al trabajar en dos direcciones (con el patrón de mensajes Duplex), lo hace de manera sincrónica, ya que al llamar a la operación, el cliente debe esperar a que ésta se ejecute por completo para realizar otras tareas. Esto se debe a que antes debe recibir la confirmación o el resultado de su ejecución.

IsInitiating(boolean): determina cuándo se inicializará un nuevo canal. El predeterminado es true.

IsTerminating(boolean): determina cuándo la operación será cerrada y se eliminará el canal utilizado por el cliente.

Name(string): define el nombre de la operación.

ReplyAction(string): es un URI, que especifica la acción de respuesta a una solicitud de la operación. En caso de no utilizar esta propiedad, el URI tomará el siguiente valor: “http://tempuri.org/contract-name/operation-nameRespons”.

Style(ServiceOperationStyle): indica la manera en que el WSDL Metadata puede ser convertido:

          document/wrapped,

          document/bare,

          RPC message.

Si se desea cambiar el estilo del formato, puede hacerse tanto en el atributo [ServiceContract], afectando a todas las operaciones del contrato, o en [OperationContract], afectando a la operación definida.

Use(ServiceOperationBindingUse): determina si el mensaje será codificado (encoded) o no (literal). En el caso de ser codificado, también se deberá indicar el serializador utilizado. Este parámetro puede ser utilizado a nivel del contrato completo ([ServiceContract]) o a nivel de una operación ([OperationContract]).

Data Contract

El Data Contract describe la estructura de datos que es manejada por las funciones del servicio.

También, con el Data Contract es posible especificar la estructura del tipo de datos que intercambia el servicio.

Permite, a la vez, convertir la estructura definida anteriormente en un XML Schema, el cual es un esqueleto de tipo datos y es un formato independiente de la plataforma utilizada.

Por último, define cómo se tienen que serializar y deserializar los nuevos datos.

Con el atributo [DataContract] es posible definir un contrato de datos para una clase, estructura o enumeración, y así establecer el tipo de datos para recibir o enviar.

El atributo [DataMember] permite definir los tipos de datos.

Demo:

namespace RMS.Service.DataContracts

{

    [DataContract]

    public class VendorContract:IExtensibleDataObject

    {

        #region IExtensibleDataObject Members

 

        public ExtensionDataObject ExtensionData

        {

            get;           

            set;           

        }

 

        #endregion        

 

        #region IDataContract Members

 

        [DataMember]

        public string Code { get; set; }

 

        [DataMember]

        public string Name { get; set; }

 

        [DataMember]

        public string Image { get; set; }

 

        [DataMember]

        public string Description { get; set; }

 

        [DataMember]

        public bool Active { get; set; }

 

        #endregion

    }

}

[DataContract] – Parámetros

l  NameSpace(Uri)

Ø  Especifica el namespace del contrato.

l  Name(string)

Ø  Define el nombre del contrato.

[DataMember] – Parámetros

IsOptional(boolean): controla qué sucede si datos que son recibidos no se encuentran en el Data Contract. Si el campo es false, no habrá problemas y seguirá adelante; en caso contrario, lanzará un error.

OBSERVACIÓN

Es importante no confundir IsOptional con MustUnderstand. Cuando se habla de IsOptional, se hace referencia a algo que el receptor especifica. MustUnderstand, en cambio, se refiere a algo que especifica el lado que envía los datos.

MustUnderstand(boolean): al enviar los datos se espera que el receptor entienda el campo enviado. Indica qué ocurre cuando el receptor recibe un grupo de datos en el cual hay un miembro que no está en el Data Contract del receptor, y si es obligatorio que el receptor entienda ese campo o no.

Name(string): indica el nombre del Data Contract.

VersionAdded(int): para documentar los cambios en el Data Contract se pueden asignar números de versión a los DataMembers.

SerializeAs(SerializationReferenceMode): controla qué sucede cuando dos miembros del Data Contract referencian la misma instancia de un objeto.

Cuando la clase del Data Contract es serializada, más de un miembro puede tener la misma referencia a un objeto. En ese caso, la serialización puede realizarse de dos maneras:

          ReferenceType: al serializar y deserializar el Data Contract, los miembros siguen con las referencias compartidas.

          ValueType: al serializar y deserializar el Data Contract, cada miembro tendrá su propia referencia al objeto, sin importar si dos miembros referencian objetos iguales.

Por default, se especifica ReferenceType.

Development

Triggers en Microsoft SQL Server 2005

Introducción:

Microsoft SQL Server 2005 proporciona dos mecanismos principales para exigir las reglas de negocios y la integridad de los datos: restricciones y triggers.

SQL Server incluye dos tipos generales de triggers: DML y DDL.

Los triggers DDL son nuevos en SQL Server 2005. Estos triggers se invocan cuando un evento de lenguaje de definición de datos (DDL) tiene lugar en el servidor o la base de datos.

Los triggers DML se invocan cuando un evento de lenguaje de manipulación de datos (DML) tiene lugar en la base de datos

Un trigger DML puede consultar otras tablas e incluir instrucciones Transact-SQL complejas. El trigger y la instrucción que lo activa se tratan como una sola transacción, que puede revertirse desde el trigger. Si se detecta un error grave (por ejemplo, no hay suficiente espacio en disco), se revierte automáticamente toda la transacción.

Los triggers DML tienen varias utilidades:

Ø  Pueden realizar cambios en cascada mediante tablas relacionadas de la base de datos; sin embargo, estos cambios pueden ejecutarse de manera más eficaz mediante restricciones de integridad referencial en cascada.

Ø  Pueden proteger contra operaciones INSERT, UPDATE y DELETE incorrectas o dañinas, y exigir otras restricciones que sean más complejas que las definidas con restricciones CHECK.
A diferencia de éstas, los triggers DML pueden hacer referencia a columnas de otras tablas. Por ejemplo, un trigger puede utilizar una instrucción SELECT de otra tabla para comparar con los datos insertados o actualizados y para realizar acciones adicionales, como modificar los datos o mostrar un mensaje de error definido por el usuario.

Ø  Pueden evaluar el estado de una tabla antes y después de realizar una modificación de datos y actuar en función de esa diferencia.

Ø  Varios triggers DML del mismo tipo (INSERT, UPDATE o DELETE) en una tabla permiten realizar distintas acciones en respuesta a una misma instrucción de modificación.

Los triggers DML se limitan al esquema de la tabla o vista en la que se crearon.
No es posible definir triggers DML en tablas temporales locales o globales.
El uso de WITH ENCRYPTION impide que el trigger se publique como parte de la réplica de SQL Server. WITH ENCRYPTION no se puede especificar para triggers CLR.
INSTEAD OF no se puede especificar para triggers DDL Los triggers INSTEAD OF no se pueden utilizar en vistas actualizables que usan WITH CHECK OPTION

Como máximo, se puede definir un trigger INSTEAD OF por cada instrucción INSERT, UPDATE o DELETE en cada tabla o vista. No obstante, en las vistas es posible definir otras vistas que tengan su propio trigger INSTEAD OF.

Para los triggers INSTEAD OF, no se permite la opción DELETE en tablas que tengan una relación de integridad referencial que especifica una acción ON DELETE en cascada. Igualmente, no se permite la opción UPDATE en tablas que tengan una relación de integridad referencial que especifica una acción ON UPDATE en cascada.

Una vez que CREATE TRIGGER ha terminado de ejecutarse, event_group también actúa como una macro al agregar los tipos de eventos que comprende a la vista de catálogo sys.trigger_events.

Las instrucciones de control de flujo no pueden dividirse en varios lotes, funciones definidas por el usuario o procedimientos almacenados.
Los triggers DML usan las tablas lógicas (conceptuales) deleted e inserted. Son de estructura similar a la tabla en que se define el trigger, es decir, la tabla en que se guarda la acción del usuario. Las tablas deleted e inserted
guardan los valores antiguos o nuevos de las filas que la acción del usuario puede cambiar

Los triggers DDL capturan información sobre el evento trigger mediante el uso de la función EVENTDATA . Devuelve información acerca de los eventos de base de datos o servidor. EVENTDATA se llama cuando se activa una notificación de eventos y el resultado se devuelve al Service Broker especificado. EVENTDATA también se puede utilizar dentro del cuerpo de un desencadenador DDL.

En el caso de un trigger CLR, especifica el método de enlace de un ensamblado con el trigger. El método no puede utilizar argumentos y debe devolver void.

Triggers DML

Los triggers DML se utilizan frecuentemente para imponer las reglas empresariales y la integridad de los datos. SQL Server proporciona integridad referencial declarativa (DRI) mediante las instrucciones ALTER TABLE y CREATE TABLE.

Sin embargo, DRI no proporciona integridad referencial entre bases de datos.

El primer y último trigger AFTER que se ejecuta en una tabla se puede especificar mediante sp_settriggerorder. Sólo se puede especificar el primer y último trigger AFTER para cada una de las operaciones INSERT, UPDATE y DELETE de una tabla. Si hay otros triggers AFTER en la misma tabla, se ejecutan aleatoriamente.

Si una instrucción ALTER TRIGGER modifica el primer o último trigger, se elimina el primer o último atributo establecido en el trigger modificado, y el valor del orden se debe restablecer mediante sp_settriggerorder.

 Limitaciones de los triggers

Un trigger se crea solamente en la base de datos actual; sin embargo, un trigger puede hacer referencia a objetos que están fuera de la base de datos actual.

Si es preciso que existan asignaciones de variable en un trigger, utilice una instrucción SET NOCOUNT al principio del mismo para impedir la devolución de cualquier conjunto de resultados.

 Un trigger DELETE no captura una instrucción TRUNCATE TABLE. Aunque una instrucción TRUNCATE TABLE es, de hecho, un trigger DELETE sin una cláusula WHERE, no se registra y, por tanto, no puede ejecutar un trigger.  Dado que el permiso de la instrucción TRUNCATE TABLE es del propietario de la tabla y no se puede transferir, sólo el propietario de la tabla debe preocuparse de invocar sin darse cuenta una instrucción TRUNCATE TABLE que no producirá la ejecución del trigger DELETE.

Triggers DDL

Los triggers DDL, al igual que los estándars, ejecutan procedimientos almacenados como respuesta a un evento. Pero a diferencia de los triggers estándar, no se ejecutan como respuesta a instrucciones UPDATE, INSERT o DELETE en una tabla o vista. En cambio, se ejecutan principalmente como respuesta a instrucciones de lenguaje de definición de datos (o DDL). Entre ellas se incluyen instrucciones CREATE, ALTER, DROP, GRANT, DENY, REVOKE y UPDATE STATISTICS. Algunos procedimientos almacenados del sistema que ejecutan operaciones de tipo DDL también pueden activar triggers DDL.

 Por ejemplo, la instrucción CREATE TYPE y el procedimiento almacenado sp_addtype activarán un trigger DDL que se crea en un evento CREATE_TYPE

Los triggers DDL no se activan como respuesta a eventos que afectan a procedimientos almacenados y tablas temporales, ya sean locales o globales.

A diferencia de los triggers DML, los triggers DDL no tienen como ámbito los esquemas. Por tanto, OBJECT_ID, OBJECT_NAME, OBJECTPROPERTY y OBJECTPROPERTYEX no se pueden utilizar para efectuar consultas en metadatos sobre triggers DDL. Utilice en su lugar las vistas de catálogo.

Los triggers DDL con ámbito en el servidor aparecen en el Object Explorer  de SQL Server Management Studio, en la carpeta Triggers. Dicha carpeta se encuentra en la carpeta Server Objects. Los triggers DDL con ámbito en la base de datos aparecen en la carpeta Database Triggers. Esta carpeta se encuentra en la carpeta Programmability de la base de datos correspondiente.

Notas generales sobre los triggers

Evite la devolución de conjuntos de resultados desde triggers en los nuevos trabajos de desarrollo y piense en modificar las aplicaciones que la usan actualmente. Para evitar que los triggers devuelvan conjuntos de resultados en SQL Server 2005, establezca la opción disallow results from triggers en 1.

 SQL Server permite que se creen varios triggers por cada evento DML o DDL. Por ejemplo, si se ejecuta CREATE TRIGGER FOR UPDATE para una tabla que ya tiene un trigger UPDATE, se creará un trigger de actualización adicional. En las versiones anteriores de SQL Server, sólo se permitía un trigger por cada evento de modificación (INSERT, UPDATE, DELETE) en cada tabla.

SQL Server permite también la invocación recursiva de triggers cuando el valor RECURSIVE_TRIGGERS está habilitado mediante ALTER DATABASE.

Deshabilitar RECURSIVE_TRIGGERS sólo evita las repeticiones directas. Para deshabilitar la repetición indirecta, establezca la opción nested triggers del servidor en 0 con sp_configure.

Si alguno de los triggers ejecuta una instrucción ROLLBACK TRANSACTION, no se ejecuta ningún trigger posterior, independientemente del nivel de anidamiento.

Los triggers pueden anidarse hasta un máximo de 32 niveles. Si un trigger cambia una tabla en la que hay otro trigger, el segundo se activa y puede, entonces, llamar a un tercero, y así sucesivamente. La configuración predeterminada permite triggers anidados.

 Los métodos invocados desde el código administrado no cuentan para este límite.

Resolución diferida de nombres

SQL Server permite triggers hagan referencia a tablas que no existen en el momento de la compilación. Esta capacidad se denomina resolución diferida de nombres.  Sin embargo se emitiría una advertencia en el momento de la creación sólo si el valor de nivel de compatibilidad se establece en 65.

Permisos:

Para crear un trigger DML, es necesario contar con permiso ALTER sobre la tabla o vista en la que se crea el trigger.

Para crear un trigger DDL con ámbito en el servidor (ON ALL SERVER) es necesario un permiso CONTROL SERVER sobre el servidor. Para crear un trigger DDL con ámbito en la base de datos (ON DATABASE) es necesario un permiso ALTER ANY DATABASE DDL TRIGGER en la base de datos actual.

Demos:

Utilizar un trigger DML con un mensaje de aviso

–El siguiente trigger DML imprime un mensaje en el cliente cuando alguien intenta agregar o cambiar datos en la tabla Customer.

CREATE TRIGGER reminder1

ON Sales.Customer

AFTER INSERT, UPDATE

AS RAISERROR (‘Notify Customer Relations’, 16, 10)

GO

Utilizar un trigger DML con un mensaje de correo electrónico de aviso

–Este ejemplo envía un mensaje de correo electrónico a una persona especificada (MaryM) cuando cambia la tabla Customer.

CREATE TRIGGER reminder2

ON Sales.Customer

AFTER INSERT, UPDATE, DELETE

AS

   EXEC msdb.dbo.sp_send_dbmail

        @profile_name = ‘AdventureWorks Administrator’,

        @recipients = ‘danw@Adventure-Works.com’,

        @body = ‘Don”t forget to print a report for the sales force.’,

        @subject = ‘Reminder’;

GO

Utilizar un trigger DML AFTER para exigir una regla de negocio entre las tablas PurchaseOrderHeader y Vendor

/*

Debido a que las restricciones CHECK sólo pueden hacer referencia a las columnas en que se han definido las restricciones de columna o de tabla, cualquier restricción de referencias cruzadas, en este caso, reglas de negocio, debe definirse como triggers.

 

En este ejemplo se crea un trigger DML. El trigger comprueba que la solvencia del proveedor es satisfactoria cuando se intenta insertar un nuevo pedido de compra en la tabla PurchaseOrderHeader. Para obtener la solvencia del proveedor, debe hacerse referencia a la tabla Vendor. Si la solvencia no es satisfactoria, se muestra un mensaje y no se ejecuta la inserción.

*/

CREATE TRIGGER LowCredit ON Purchasing.PurchaseOrderHeader

AFTER INSERT

AS

DECLARE @creditrating tinyint,

   @vendorid int

SELECT @creditrating = v.CreditRating, @vendorid = p.VendorID

FROM Purchasing.PurchaseOrderHeader p INNER JOIN inserted i

     ON p.PurchaseOrderID = i.PurchaseOrderID

           JOIN Purchasing.Vendor v on v.VendorID = i.VendorID

IF @creditrating = 5

BEGIN

   RAISERROR (‘This vendor”s credit rating is too low to accept new

      purchase orders.’, 16, 1)

ROLLBACK TRANSACTION

END

Utilizar un trigger DDL con ámbito en la base de datos

— En el ejemplo siguiente se utiliza un trigger DDL para impedir que se quiten sinónimos en una base de datos

IF EXISTS (SELECT * FROM sys.triggers WHERE parent_class = 0 AND name = ‘safety’)

     DROP TRIGGER safety

ON DATABASE

GO

CREATE TRIGGER safety

ON DATABASE

FOR DROP_SYNONYM

AS

   RAISERROR (‘You must disable Trigger "safety" to drop synonyms!’,10, 1)

   ROLLBACK

GO

Utilizar un trigger DDL con ámbito en el servidor

/*

En el ejemplo siguiente se utiliza un trigger DDL para imprimir un mensaje si se produce un evento CREATE DATABASE en la instancia actual del servidor, y se utiliza la función EVENTDATA para recuperar el texto de la instrucción Transact-SQL correspondiente.

*/

IF EXISTS (SELECT * FROM sys.server_triggers

    WHERE name = ‘ddl_trig_database’)

DROP TRIGGER ddl_trig_database

ON ALL SERVER

GO

CREATE TRIGGER ddl_trig_database

ON ALL SERVER

FOR CREATE_DATABASE

AS

    PRINT ‘Database Created.’

    SELECT EVENTDATA().value(‘(/EVENT_INSTANCE/TSQLCommand/CommandText)[1]’,‘nvarchar(max)’)

GO

En todas las instalaciones de SQL Server 2005, el nivel de compatibilidad predeterminado es 90. Las bases de datos creadas en SQL Server 2005 se configuran en este nivel, excepto la base de datos model que tiene un nivel de compatibilidad inferior. Cuando se actualiza una base de datos de una versión anterior de SQL Server a SQL Server 2005, la base de datos mantiene su nivel de compatibilidad. Esto se aplica a las bases de datos del usuario y del sistema.