You are on page 1of 8

Por Jess Lpez Mndez (SqlRanger)

Gestin de concurrencia en ADO.NET


La concurrencia, en un entorno multiusuario, es siempre una cuestin problemtica, pero si adems se trata de un entorno desconectado como el que se usa en ADO.NET con sus DataSets y DataAdapters, la problemtica es an mayor debido a la propia naturaleza desconectada del entorno.

en primer lugar, cual es esa problemtica y << Veamos,posteriormente qu alternativas tenemos para
tratarla. El problema fundamental que se nos plantea son los conflictos de concurrencia. Un conflicto de concurrencia se produce cuando un usuario modifica un registro de una tabla de una base de datos y ese registro ha cambiado desde la ltima vez que lo ley. Por ejemplo, consideremos la siguiente secuencia de sucesos: Los usuarios A y B leen el registro R1 de la base de datos cargndolo en un DataSet. El usuario A modifica R1 El usuario A guarda R1 en la base de datos. El usuario B modifica R1 El usuario B guarda R1 en la base de datos.

Los conflictos de concurrencia no se producen solamente al actualizar un registro porque otro usuario lo haya modificado, tambin ocurren si el registro ha sido eliminado por otro usuario. Asimismo, tienen lugar cuando un usuario intenta eliminar un registro que ha sido modificado e incluso que ha sido eliminado. Con la insercin, sin embargo, es evidente que no se producen conflictos de concurrencia, ya que es imposible que otro usuario pueda modificar un registro que an no existe en la base de datos. En definitiva, los conflictos de concurrencia pueden producirse: Al modificar un registro Al eliminar un registro Y la causa del conflicto puede ser porque dicho registro: Ha sido modificado desde la ltima vez que se ley. Ha sido eliminado desde la ltima vez que se ley. Otro aspecto bsico acerca de los conflictos de concurrencia es la forma de detectarlos. La tcnica de deteccin se basa fundamentalmente en incluir en la clusula WHERE de la instruccin UPDATE o DELETE el valor original de los campos, es decir, el valor que tenan los campos del registro cuando se leyeron de la base de datos. Pongamos un ejemplo para aclarar ideas.

20

<<dotNetMana

El usuario B recibe una excepcin DBConcurrencyException, indicando un conflicto de concurrencia al haber sido modificado R1 desde la ltima vez que B lo ley.

<< dnm.plataforma.ado.net
Supongamos que estamos trabajando con la siguiente tabla en una base de datos de SQL Server: pos en la clusula SET, excepto el IdEmpleado que es autonumrico y por tanto de slo lectura. Esto viene a An as, podramos obtener un conflicto de concurrencia, pero slo en el caso de que haya sido eliminado el registro. Un inconveniente de esta opcin es que es posible perder modificaciones. Si por ejemplo, los usuarios A y B leen el empleado 10, el usuario A modifica su nombre y lo guarda, y luego B modifica el apellido y lo guarda, las dos actualizaciones tienen xito, pero la modificacin que hizo A se pierde, ya que el nombre es sobrescrito con el valor que ley B. En ciertos sistemas, esta posible prdida de modificaciones es inaceptable y por tanto habra que elegir otra opcin. Otra alternativa sera incluir en la clusula SET slo los campos que se han modificado. De esta manera, aunque slo incluyramos la clave primaria en la clusula WHERE, no se perderan las modificaciones. Tambin es una opcin interesante incluir en la clusula SET slo los campos modificados, y en la clusula WHERE la clave primaria ms el valor original de los campos que han cambiado, as el conflicto de concurrencia que detectaramos sera en el caso de que otro usuario hubiera modificado alguno de los campos que han sido modificados o en el caso de eliminacin. Por ejemplo, si los usuarios A y el B leen el empleado 10, el usuario A modifica su

CREATE TABLE Empleados ( IdEmpleado INT IDENTITY(1,1) PRIMARY KEY, DNI VARCHAR(12) NOT NULL UNIQUE, Nombre VARCHAR(50) NOT NULL, Apellidos VARCHAR(50) NOT NULL )

El comando UPDATE que detecta conflictos de concurrencia sera el siguiente:

suponer que se actualizarn todos los campos en la tabla, independientemente de si se han modificado o no,

UPDATE Empleados SET DNI=@DNI, Nombre=@Nombre, Apellidos=@Apellidos WHERE IdEmpleado=@Original_IdEmpleado AND DNI=@Original_DNI AND Nombre=@Original_Nombre AND Apellidos=@Original_Apellidos

Como veis, estn todos los valores originales en la clusula WHERE de esta instruccin parametrizada. De esta manera, si ha cambiado alguno de los campos, no se cumplir la condicin, y por tanto la instruccin no actualizar ningn registro, o lo que es lo mismo, el nmero de registros afectados ser cero. Solamente la instruccin tendr xito, o sea, actualizar el registro, si ste no ha cambiado. As es como ADO.NET detecta los conflictos de concurrencia, concretamente un DataAdapter lanzar una excepcin DBConcurrencyException cuando el comando de actualizacin afecte a cero registros. Observad que este comando incluye todos los cam-

lo que implica una falta de optimizacin. Sin embargo, es posible que no nos interese detectar conflictos de concurrencia y que queramos que la actualizacin se lleve a cabo independientemente de si el registro ha sido modificado o no desde la ltima vez que se ley. En ese caso slo incluiramos la clave primaria en la clusula WHERE. La instruccin UPDATE sera la siguiente:

UPDATE Empleados SET DNI=@DNI, Nombre=@Nombre, Apellidos=@Apellidos WHERE IdEmpleado=@Original_IdEmpleado

Los conflictos de concurrencia no se producen solamente al actualizar un registro porque otro usuario lo haya modificado, tambin ocurren si el registro ha sido eliminado por otro usuario

21

<<dotNetMana

nombre y lo guarda y el usuario B modifica su nombre y lo guarda, el usuario B recibe un conflicto de concurrencia. Pero si lo que ocurre es que el usuario A modifica el nombre y el B el apellido, no hay conflicto de concurrencia y las dos actualizaciones tienen xito. Una ltima alternativa para la clusula WHERE es incluir la clave primaria ms el valor original de un campo de tipo TimeStamp que por supuesto tendra que formar parte de la tabla. El funcionamiento es equivalente a incluir los valores originales de todos

<< dnm.plataforma.ado.net
los campos, pero resulta ms eficiente ya que la instruccin es ms corta, reducindose el trfico de red y reduciendo el trabajo del procesador de consultas del servidor de base de datos. Un campo de tipo TimeStamp en SQL Server es una especie de autonumrico de 64 bits nico en toda la base de datos. No puede haber dos registros en una base de Empecemos primero por los conflictos que se producen al actualizar. Si la causa es que otro usuario lo ha modificado podramos tener las siguientes alternativas: Descartar las modificaciones y refrescar el registro volvindolo a leer de la base de datos. Al usuario le avisaramos del conflicto de concurrencia y le daramos la oportunidad de volver a hacer las modificaciones. Refrescar slo los valores originales sin descartar las modificaciones. Al usuario le avisaramos del conflicto. Entonces l tendra la oportunidad de ver las modificaciones deshaciendo cambios o de volver a guardar con lo que forzara la actualizacin. Directamente forzar la actualizacin. Esto se conoce como la tcnica el ltimo que llega gana. En realidad esta accin no es una respuesta a un conflicto de concurrencia, ya que para llevarla a cabo incluiramos nicamente la clave primaria en la clusula WHERE, no detectndose conflictos de concurrencia por modificacin. Si la causa es que otro usuario lo ha eliminado, las alternativas seran las siguientes: Volver a insertar el registro en la base de datos. En el caso de que tengamos un autonumrico en la tabla no sera posible volver a insertar el registro exactamente igual a como era anteriormente. Eliminarlo del DataSet. Esta es la opcin que ms se suele utilizar. Detectar la causa del conflicto, al igual que refrescar un registro, puede realizarse volviendo a leer tal registro de la base de datos basndose en la clave primaria, pero si la clave primaria puede cambiar, esta tcnica no sirve para su propsito ya que si sta ha cambiado no es posible identificar el registro y no es posible determinar si el conflicto de concurrencia ha ocurrido por modificacin o por eliminacin. Por eso sera recomendable usar claves primarias artificiales como autonumricos o GUIDs. En cuanto a los conflictos de concurrencia que se producen al eliminar un registro, podramos tener las siguientes alternativas cuando la causa es por modificacin: Deshacer la eliminacin y refrescar el registro. Al usuario le informaramos del conflicto y tendra la posibilidad de volverlo a eliminar despus de haber visto los cambios realizados.

La tcnica de deteccin se basa fundamentalmente en incluir en la clusula WHERE de la instruccin UPDATE o DELETE el valor original de los campos

22

<<dotNetMana

datos con el mismo valor de TimeStamp, incluso aunque pertenezcan a distintas tablas. Cada vez que se modifica un registro que tiene un campo TimeStamp, el valor del campo tambin cambia. Debido a esto y si usamos esta alternativa, despus de modificar un registro, sera necesario volver a leer el campo TimeStamp para poder realizar ms modificaciones en el mismo registro. Detectar conflictos de concurrencia en la eliminacin es similar a la actualizacin, con la salvedad de que en este caso slo podemos jugar con la clusula WHERE de la instruccin DELETE . Podramos incluir slo la clave primaria, en cuyo caso slo obtendremos conflictos de concurrencia cuando otro usuario haya eliminado el registro. En la mayora de los casos, este conflicto sencillamente lo podramos ignorar. Tambin podramos incluir todos los valores originales de los campos o la clave primaria ms el TimeStamp, en cuyo caso recibiremos un conflicto de concurrencia cuando otro usuario haya modificado o eliminado el registro. Una vez que tenemos decidido cmo vamos a detectar los conflictos de concurrencia y cmo vamos a hacer las actualizaciones y eliminaciones, hemos de decidir cmo los vamos a tratar, o sea, qu acciones vamos a tomar en el caso de un conflicto de concurrencia. Cada conflicto de concurrencia lo trataremos de manera diferente en funcin de si se ha producido al hacer una actualizacin o al realizar una eliminacin y en funcin de la causa del conflicto, esto es, si ha sido porque otro usuario lo ha modificado o porque lo ha eliminado.

<< dnm.plataforma.ado.net
Forzar la eliminacin. En realidad esta accin no es una respuesta a un conflicto de concurrencia, ya que para llevarla a cabo incluiramos nicamente la clave primaria en la clusula WHERE con lo que no se detectan conflictos de concurrencia por modificacin. Por ltimo, el conflicto de concurrencia que se produce al eliminar un registro que ha sido eliminado, generalmente puede tratarse sencillamente ignorando el conflicto y eliminando definitivamente el registro del DataSet. Como hemos visto, existen varias alternativas para detectar y tratar los conflictos de concurrencia. Veamos ahora qu nos ofrece ADO.NET en este sentido. En ADO.NET tenemos una serie de clases, los DataAdapters, que son los encargados de revertir las modificaciones realizadas en un DataSet sobre la base de datos mediante su mtodo Update. Los DataAdapters tienen tres propiedades: DeleteCommand, UpdateCommand e InsertCommand que son los comandos de actualizacin. Estos comandos son parametrizados, de manera que sirvan para todas las filas de un DataTable. Cuando invocamos al mtodo Update de un DataAdapter, ste recorre todas las filas del DataTable, y si la fila es una fila eliminada, ejecuta el DeleteCommand; si la fila es una fila modificada, invoca el UpdateCommand; y si es una fila nueva, invoca al InsertCommand. Si al invocar al UpdateCommand o al DeleteCommand , el nmero de registros afectados es cero, el DataAdapter lanza una excepcin DBConcurrencyException indicando que se ha producido un conflicto de concurrencia. Antes de invocar un comando de actualizacin, el DataAdapter establece el valor de los parmetros del comando con los valores originales o actuales de los campos de la fila basndose en la configuracin del propio comando. Cada parmetro de la coleccin Parameters de un comando tiene la propiedad SourceColumn que indica el nombre del campo cuyo valor deber copiarse al parmetro, y la propiedad SourceVersion que indica si se trata

Figura1. Opciones avanzadas de generacin de instrucciones SQL.

del valor actual o del valor original. Antes de poder invocar al mtodo Update de un DataAdapter tenemos que configurarlo correctamente, esto es, tenemos que establecerle los comandos de actualizacin. Para configurar un DataAdapter tenemos tres alternativas: Usar el asistente para la configuracin del DataAdapter Usar un CommandBuilder Configurarlo manualmente escribiendo nosotros mismos el cdigo Para usar el asistente, slo tenemos que arrastrar un DataAdapter de la ficha datos del cuadro de herramientas a nuestro formulario o componente y seguir sus instrucciones. En el paso Generar las instrucciones SQL tenemos un botn Opciones avanzadas que nos pre-

senta el cuadro de dilogo de la figura 1. Como vemos, el asistente puede generar por nosotros los comandos de actualizacin INSERT, UPDATE y DELETE. Si elegimos Usar concurrencia optimista, el asistente incluir en la clusula WHERE de las instrucciones UPDATE y DELETE el valor original de todos los campos del registro. Mientras que si no activamos esa casilla de verificacin, la clusula WHERE slo incluir la clave primaria. Si elegimos Actualizar el conjunto de datos el asistente aade una instruccin SELECT a los comandos de actualizacin para refrescar el registro. En cualquier caso, la instruccin UPDATE incluye en la clusula SET todos los campos. Esta sera la instruccin UPDATE para nuestra tabla de ejemplo usando concurrencia optimista. Ver tabla 1.

Tabla 1

23

<<dotNetMana

UPDATE Empleados SET DNI=@DNI, Nombre=@Nombre, Apellidos=@Apellidos WHERE IdEmpleado=@Original_IdEmpleado AND DNI=@Original_DNI AND Nombre=@Original_Nombre AND Apellidos=@Original_Apellidos

<< dnm.plataforma.ado.net
Y esta sera la instruccin UPDATE sin usar la concurrencia optimista: ciones, que sea capaz de gestionar los conflictos de concurrencia y que disponga de todas las opciones

UPDATE Empleados SET DNI=@DNI, Nombre=@Nombre, Apellidos=@Apellidos WHERE IdEmpleado=@Original_IdEmpleado

Como hemos dicho anteriormente, tambin podemos usar un CommandBuilder. Este sera el cdigo a usar para nuestra tabla de ejemplo:

mencionadas en este artculo. Podis encontrar un DataAdapter para SQL Server (SqlRanger.SqlAdapter) escrito por m en la web de la revista o en mi propia pgina web: http://sqlranger.com/descargas.aspx.

SqlDataAdapter Adapter = new SqlDataAdapter(SELECT * FROM Empleados, Connection); SqlCommandBuilder CommandBuilder = new SqlCommandBuilder(Adapter); Adapter.UpdateCommand = CommandBuilder.GetUpdateCommand(); Adapter.InsertCommand = CommandBuilder.GetInsertCommand(); Adapter.DeleteCommand = CommandBuilder.GetDeleteCommand();

24

<<dotNetMana

Las instrucciones UPDATE y DELETE seran equivalentes a las generadas por el asistente usando concurrencia optimista y sin actualizar el conjunto de datos. La alternativa de configurar manualmente el DataAdapter no es muy recomendable, ya que requiere escribir bastante cdigo y la funcionalidad obtenida es exactamente igual a la conseguida usando el asistente. Adems es posible que cometamos algn error al escribir el cdigo, mientras que el asistente no los comete. Como vemos, el DataAdapter slo nos deja la posibilidad de incluir en la clusula WHERE de las instrucciones UPDATE y DELETE o bien la clave primaria, o bien todos los campos. No tenemos las otras alternativas que se mencionan en este artculo. Adems en la clusula SET de la instruccin UPDATE, slo podemos incluir todos los campos, no tenemos la opcin de incluir slo los modificados. Por otra parte, ADO.NET slo da soporte para la deteccin del conflicto de concurrencia, no hay nada que nos ayude a gestionarlo, por lo que tendremos que escribir nosotros mismos el cdigo necesario. El cdigo de ejemplo del fuente 1 muestra como gestionar conflictos de concurrencia, refrescando el registro si ha sido modificado y eliminndolo si ha sido eliminado. Como vemos, gestionar los conflictos de concurrencia no es trivial y repetir el mismo cdigo una y otra vez para cada caso es muy laborioso y pesado. Una buena alternativa a los DataAdapters que vienen incluidos en .NET Framework, es escribir nuestro propio DataAdapter que no tenga estas limita-

Este DataAdapter es completamente gratis y se incluye el cdigo fuente as como un ejemplo de su uso. El SqlRanger.SqlAdapter tiene propiedades especficas para tratar la concurrencia. Entre las que se incluyen: UpdateCriteria: Determina los campos a incluir en la clusula WHERE de la instruccin UPDATE. Puede tomar los siguientes valores: All: Se incluirn los valores originales de todos los campos. Key: Se incluir slo la clave primaria. Modified: Se incluir la clave primaria ms los valores originales de los campos modificados. TimeStamp: Se incluir la clave primaria ms el valor original del campo TimeStamp si es que existe. UpdateColumns: Determina qu campos aparecern en la clusula SET de la instruccin UPDATE. Puede tomar los siguientes valores: All: Se incluyen todos los campos. Modified: Se incluyen slo los campos modificados. DeleteCriteria: Determina los campos a incluir en la clusula WHERE de la instruccin DELETE. Puede tomar los siguientes valores: All: Se incluirn los valores originales de todos los campos. Key: Se incluir slo la clave primaria. TimeStamp: Se incluir la clave primaria ms el valor original del campo TimeStamp si es que existe.

<< dnm.plataforma.ado.net
public void Guardar(DataTable Empleados) { // creamos un adapter para realizar la actualizacin SqlDataAdapter Adapter = new SqlDataAdapter(SELECT * FROM Empleados, this.cn); // usamos un command builder para configurar los comandos de actualizacin SqlCommandBuilder CommandBuilder = new SqlCommandBuilder(Adapter); Adapter.UpdateCommand = CommandBuilder.GetUpdateCommand(); Adapter.InsertCommand = CommandBuilder.GetInsertCommand(); Adapter.DeleteCommand = CommandBuilder.GetDeleteCommand(); // este comando nos sirve para refrescar un registro SqlCommand Resync = new SqlCommand(SELECT * From Empleados WHERE IdEmpleado=@IdEmpleado, this.cn); Resync.Parameters.Add(@IdEmpleado, SqlDbType.Int); try { Adapter.Update(Empleados); } catch ( DBConcurrencyException ex ) { // Nuestra respuesta a un conflicto va a ser refrescar el registro Adapter.SelectCommand = Resync; Resync.Parameters[@IdEmpleado].Value = ex.Row[IdEmpleado, DataRowVersion.Original]; // // if // { el mtodo Fill buscar el registro en el DataTable por clave primaria (IdEmpleado) y lo refrescar ( Adapter.Fill(Empleados) == 0 ) la causa del conflicto es que ha sido eliminado (Fill devuelve cero registros) if ( ex.Row.RowState == DataRowState.Deleted ) { // en este punto tenemos un conflicto de concurrencia // al eliminar un registro porque ha sido eliminado. // Eliminamos definitivamente el registro ex.Row.AcceptChanges(); // ignoramos el conflicto y seguimos con la actualizacin Guardar(Empleados); } else { // en este punto tenemos un conflicto de concurrencia // al modificar un registro porque ha sido eliminado // Eliminamos el registro // y volvemos a lanzar la excepcin ex.Row.Delete(); ex.Row.AcceptChanges(); throw ex; } } else { // la causa del conflicto es que ha sido modificado // Si el conflicto ha sido al eliminar el registro // Fill ya lo habr recuperado y refrescado. // Aparecer el registro con el error // Si el conflicto ha sido al modificar el registro // Fill lo habr refrescado. Y aparecer el registro // con el error // slo hay que volver a lanzar la excepcin throw ex; } }

Fuente 1. Ejemplo de gestin de conflictos de concurrencia

25

<<dotNetMana

<< dnm.plataforma.ado.net
ConflictUpdatingChangedAction: Determina la accin a realizar en caso de un conflicto de concurrencia al actualizar un registro porque haya sido modificado desde la ltima vez que se ley. Puede tomar los siguientes valores: NoAction: No hace nada. ResyncAllValues: Refresca todos los valores del registro, volvindolo a leer de la base de datos. ResyncOriginalValues: Refresca los valores originales del registro, leyndolo de la base de datos. ConflictUpdatingDeletedAction : Determina la accin a realizar en caso de un conflicto de concurrencia producido al actualizar un registro porque haya sido eliminado desde la ltima vez que se ley. Puede tomar los siguientes valores: Delete: Elimina definitivamente el registro del DataSet. Insert: Vuelve a insertar el registro en la base de datos. NoAction: No hace nada. ConflictDeletingChangedAction : Determina la accin a realizar en caso de un conflicto de concurrencia producido al eliminar un registro porque haya sido modificado desde la ltima vez que se ley. Puede tomar los siguientes valores: NoAction: No hace nada. ResyncAllValues : Refresca el registro, volvindolo a leer de la base de datos. ResyncCommand : Comando parametrizado basado en clave primaria utilizado para refrescar un registro. El SqlRanger.SqlAdapter genera automticamente los comandos de actualizacin y el ResyncCommand, no siendo necesario proporcionrselos. Para ello hace uso del SqlRanger.CommandBuilder.

Conclusin
La concurrencia es un tema problemtico en ADO.NET dada su naturaleza desconectada. Existen varias opciones para detectar y tratar los conflictos de concurrencia. Cada una de estas opciones tiene sus ventajas e inconvenientes y es necesario elegir cuidadosamente la ms adecuada para el sistema en cuestin. ADO.NET da soporte limitado para la gestin de la concurrencia, siendo una buena alternativa escribir nuestro propio DataAdapter para superar las limitaciones.

Qu es qu? Qu
Qu es Whitehorse?

es qu?
Indy est inmerso en la versin 2.0 de la suite Microsoft System Center para la que an no hay fecha prevista de salida, ni tan siquiera una aproximacin. La versin actual, la 1.0 llamada System Center 2005 es la primera suite de gestin integrada para el Windows Server System e incluye el System Management Server 2003, Microsoft Operations Manager 2005 y el nuevo sistema comn de reporting. Se habl de l en el Summit celebrado en Las Vegas el pasado mes de marzo. La web del Summit 2004: http://www2.mms2004.com. Ms informacin en Microsoft Watch: http://www.microsoft-watch.com y en el sito Betanews http://www.betanews.com/article.php3?sid=1079576470

Whitehorse es el nombre en clave del software que se incluir en Visual Studio 2005 y que aporta herramientas de diseo model-driven dirigida a los arquitectos de software, enlazando el modelo conceptual al cdigo. T endremos ms informacin en el devdays que se celebrar en San Diego, California entre el 23 y el 28 de Mayo (http://www.microsoft.com/seminar/teched2004). En Europa se celebrar en msterdam, Holanda, entre el 29 de Junio y el 2 de Julio (http://www.microsoft.com/europe/teched). Entretanto puede descargarse un video demostrativo de la web de MSDNTV en http://msdn.microsoft.com/msdntv

Qu es Lonestar?
Aparte de un mtico grupo de rock cataln de los aos 70, Lonestar es el nombre en clave de la prxima versin del sistema operativo de Microsoft para Tablet PC. Si bien se iba a vender como un add-on para los usuarios de Tablet PC, finalmente ser incluido dentro de Windows XP SP2. Tendr un nuevo SDK para desarrolladores e integracin con Office 2003.

Qu es Laguna?
En el Microsoft Mobile DevCon Conference 2004 celebrado en San Francisco entre el 23 y el 27 de marzo se habl de Laguna, nombre en clave del SQL Server CE 3.0. Esta versin se ver retrasada igual que la versin completa, el SQL Server 2005. Segn nuestras noticias, ambas versiones saldrn juntas, si bien la versin beta 1 de Laguna estar disponible cuando est la beta 2 de Yukon. La web del Mobile DevCon Conference 2004 est en: http://www.microsoftmdc.com. Puede ver informacin de la versin actual de SQL Server CE 2.0 en: http://www.microsoft.com/sql/ce.

Qu es Windows XP Reloaded?
Windows XP Reloaded es el nombre en clave para la versin de Windows XP que har de puente entre la actual y Longhorn.

Qu es Symphony y Harmony?
Symphony es el nombre en clave de la prxima versin de Windows XP Media Center Edition el cual est basado en XP SP2. Una versin previa a Windows XP Media Center Edition 2004. Harmony es el nombre en clave del prximo Windows XP Media Center Edition 2004. Incluir soporte para High Definition Televisin, soporte para mltiples sintonizadores, soporte para diferentes formatos de grabacin de vdeo y radio.

Qu es Indy?

<<dotNetMana

Indy es el nombre en clave de una nueva herramienta de gestin desarrollada por Microsoft Research y que se comercializar por la divisin Enterprise Management de Microsoft. Simula un centro de datos empresarial derivado del modelo de hardware, software y los sistemas de servidores del cliente.

26

noticias.noticias.noticias.noticias

Suscripcin a dotNetMana
Deseo suscribirme a dotNetMana por un ao (11 ejemplares) y beneficiarme de la oferta del 10% de descuento por un importe total de 60 para Espaa; o por 75 para el resto de Europa; o por 90 para el resto del mundo (IVA incluido). Deseo suscribirme a dotNetMana por un ao (11 ejemplares) por un importe de 45 por ser estudiante (IVA incluido). Aporto fotocopia del carn de estudiante o sello del centro acadmico (IMPRESCINDIBLE). OFERTA VLIDA SLO PARA ESTUDIANTES RESIDENTES EN ESPAA. IMPORTES VLIDOS HASTA NUEVA OFERTA DATOS DE FACTURACIN CIF/NIF . . . . . . . . . . . . . . . . . . . . .Empresa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Nombre y apellidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Direccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Poblacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Cdigo Postal . . . . . . . . . . . . . . . . . . . Provincia . . . . . . . . . . . . . . . . . . . . . . . . . Telfono . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fax . . . . . . . . . . . . . . . . . . . . . . . . . . . email . . . . . . . . . . . . . . . . . . . . . . . . . . . . DATOS DE ENVO (slo si son distintos de los datos de facturacin) CIF/NIF . . . . . . . . . . . . . . . . . . . . .Empresa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Nombre y apellidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Direccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Poblacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Cdigo Postal . . . . . . . . . . . . . . . . . . . Provincia . . . . . . . . . . . . . . . . . . . . . . . . . Telfono . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fax . . . . . . . . . . . . . . . . . . . . . . . . . . . email . . . . . . . . . . . . . . . . . . . . . . . . . . . .

FORMA DE PAGO Taln nominativo a nombre NETALIA, S.L. Transferencia bancaria a nombre de NETALIA, S.L. a: La Caixa - Nmero de cuenta 2100 4315 48 2200014696 (Indique su nombre en la transferencia) Domiciliacin Bancaria (con renovacin automtica, previo aviso) Indique su nmero de cuenta: Tarjeta de crdito VISA MASTERCARD Nmero de su tarjeta: Fecha de caducidad:

(imprescindible)

Firma y/o sello (imprescindible) a de de 20

Usted autoriza a la mecanizacin de estos datos. El responsable y destinatario de stos es Netalia, S.L. Usted tiene derecho a acceder a sus datos, modificarlos y cancelarlos cuando lo desee. Sus datos no sern cedidos en ninguna de las formas posibles a terceras partes y no se utilizarn ms que para el buen funcionamiento de su suscripcin a la revista dotNetMana y para informarle de las actividades comerciales que realice la editorial Netalia, S.L. Si no desea recibir informacin comercial de dotNetMana marque la casilla siguiente

N3

N4

N5

N6

N7

N8

N9

Si desea algn otro nmero indquelo

Puede enviar los datos al email suscripciones@dotnetmania.com, al FAX (34) 91 499 13 64 o al telfono (34) 91 666 74 77. Tambin puede enviarlo por correo postal a la siguiente direccin:

C/ Robledal, 135 28529- Rivas Vaciamadrid Madrid (Espaa)

You might also like