martes, 7 de septiembre de 2010

Transacciones en Oracle desde ASP con C#, distribución de datos

Reciéntemente me encontré con un requerimiento en un cliente donde se necesitaba que al presionar un botón, el sistema le trajera al usuario el siguiente registro que debía ser procesado.

Para esta funcionalidad hay que considerar varios aspectos, como el hecho de que pueden haber 1 o N usuarios presionando el botón o que los registros entregados nunca deben coincidir.

Como estamos en IIS hay varias posibles soluciones, adicional, tenemos una consideración mucho más importante que hacer, y es que el cliente utiliza Oracle en su BD.  Este tipo de funcionalidad puede traer bloqueos (deadlocks), o dependiendo de la cantidad de usuarios, pueden degradar el performance de la aplicación Web a un punto frustrante.

Para no alargar la historia, vamos directo a la solución. 

Primero, yo he escogido marcar como bandera (flag) un campo de una tabla para descartar los que ya han sido actualizados, esta tabla la carga un supervisor y se la asigna a un grupo de usuarios, y en ese momento el campo viene en NULL.  Para sortear los problemas de bloqueo utilizaremos un SELECT FOR UPDATE OF NOWAIT SKIP LOCKED.

Ok, nos creamos una función en el Oracle:

create or replace FUNCTION BLOQUEADA(rid in rowid) 
return number
is
ret_id number := 0;
pragma autonomous_transaction;
begin
select 1 into ret_id from TABLA where rowid = rid FOR UPDATE OF CAMPOACTUALIZAR NOWAIT SKIP LOCKED;
rollback;
return 1;
exception
when others then
if sqlcode = -54 then
rollback;
return 0;
else
raise;
end if;
end;

Obviamente el FOR UPDATE nos hará un bloqueo (lock) de la tabla y al definir OF le estamos diciendo al query que sólo queremos que bloquee el campo específico.  El NOWAIT evita que el query se quede "pegado" esperando a que otra actualización termine y el SKIP LOCKED hace que se salte los registros que están bloqueados.  El resto de la función es mandatorio para que todo funcione bien.  Importante recordar que el bloqueo sólo dura hasta el primer COMMIT.

Luego, vamos al formulario donde queremos la funcionalidad, insertamos un botón (llamémosle Button1) y le asociamos el siguiente código al evento click (aquí van a encontrar también, cómo crear una transacción, ejecutar múltiples sentencias para hacer commit al final, y muy importante, cómo conectarse a Oracle sin usar el famoso OracleClient Net Namespace o como se llame):

string LeeSiguiente = "select ID_TABLA FROM TABLA where (nvl(CAMPOACTUALIZAR,'-1') = '-1' OR (CAMPOACTUALIZAR='1' AND (BLOQUEADA(rowid) = 1 and rownum = 1))) for update of CAMPOACTUALIZAR skip locked";

string siguiente = "SIGS";
string LockSiguiente = "UPDATE TABLA SET CAMPOACTUALIZAR='1' WHERE ID_TABLA ='$iguiente'";
// así nos conectamos a Oracle sin usar el OracleClient para NETSystem.Data.Common.DbProviderFactory dbf = System.Data.Common.DbProviderFactories.GetFactory("System.Data.OracleClient");
IDbConnection connection = dbf.CreateConnection();
connection.ConnectionString = "Data Source=SERVICIOORACLE;Persist Security Info=True;User ID=USUARIO;Password=CLAVE;Unicode=True";
// aquí se crea el comando y la transacción
IDbCommand command = connection.CreateCommand();
command.CommandType = CommandType.Text;
IDbTransaction transaction = null;
connection.Open();
// aquí comienza la transacción y asignamos el comando a la tranacción
transaction = connection.BeginTransaction();
command.Transaction = transaction;
// ejecutamos el primer comando y verificamos si trae valores
command.CommandText = LeeSiguiente;
siguiente=Convert.ToString(command.ExecuteScalar());
if (siguiente + "$" != "$" )
{
// ejecutamos el segundo comando
command.CommandText = LockSiguiente.Replace("$iguiente",siguiente);
command.ExecuteNonQuery();

} else {
siguiente = "NO_HAY_MAS";
}
// hacemos COMMIT lo cual termina la transacción
transaction.Commit();
connection.Close();

Ok, con ese código, bloqueamos el siguiente registro disponible en la tabla, pero sólo 1!, traerá los registros instantaneamente en el campo siguiente, y si no hay más disponibles, traerá el valor "NO_HAY_MAS" para que hagamos nuestras excepciones.

Espero este código les sirva de guía, porque cuando se requiere procesamiento de datos en paralelo, ya sea por usuarios, threads, etc, y en Oracle, es un verdadero problema encontrar una solución razonable.

Recuerden hacer clic en los anuncios de Google!

IIS, Sin privilegios a la Metabase

Si en alguna ocasión tienen que desarrollar algo montado sobre IIS y ASP, y tienen la mala suerte de encontrarse con este error, pueden perder horas y horas de research, cuando en realidad la solución es muy simple:
  1. Abren una ventana de comando.
  2. Se cambian al directorio C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\ (ojo si su Windows está en otro disco duro, por ejemplo D...).
  3. Ejecutan el comando aspnet_regiis -ga miusuario (recuerden cambiar a miusuario por el usuario que está configurado en la página que están programando, o por el usuario que está "impersonando" el IIS).
  4. Ejecutan el comando iisreset y esperan a que termine para intentar nuevamente con su página.
Sin mucho que analizar, está más que claro que el parámetro -ga significa "grant access".

Para que no se queden con las dudas, este error ocurre por algo muy sencillo:  instalaste tu IIS luego de haber instalado el NET Framework.  Anótenlo en algún lugar, primero instalar el IIS y luego el NET Framework.

Con esto deben solucionar el problema y seguir con su desarrollo.

lunes, 6 de septiembre de 2010

SalesLogix, dulce, complejo, indispensable...

Esta es la herramienta que ADR Technologies promociona más en el área de centroamérica.

Es un CRM que incorpora muchas ideas novedosas en este tipo de sistemas.

Totalmente configurable y personalizable.

Esta herramienta cuenta con sus propios IDE's, y es en su plataforma Web que quemo mis pestañas constantemente...

Por el momento creo que será el tema principal de mis publicaciones.