09.26.08
Posted in Team Foundation Server at 7:41 am by Miguel
Uno de los problemas más habituales a la hora de trabajar con repositorios de código fuente en modo bloqueo, es el de que un usuario deje bloqueado un fichero con su usuario (antes de irse de vacaciones, de viaje de trabajo o de la empresa). Esto supone un contratiempo más que importante para el equipo de desarrollo, sobre todo si se trata de un fichero que necesita un ajuste urgente o si se trata de un fichero de alta concurrencia.
Para los usuarios de nivel administrador de Team Foundation Server, existe solución al problema (por defecto tienen permisos para “volver atrás” los bloqueos realizados por otros usuarios). La única dificultad es que, aunque parezca mentira, no puede realizarse la acción de vuelta atrás desde el interfaz gráfico, si no que tendréis que abrir la consola de comandos incluida en Visual Studio y escribir una sentencia que siga la siguiente estructura:
tf undo /workspace:WorkSpaceDelOtroUsuario;OtroUsuario $/CarpetaDelProyecto/NombreFichero.cs /s:http://TuTeamFoundationServer:8080
Siendo los elementos en negrita los que deberéis substituir ajustándolos a vuestro entorno.
Saludos.
Miguel.
Permalink
09.25.08
Posted in .NET, Programación, Web at 7:46 am by Miguel
Aprovecho los tres minutos diarios que tengo últimamente para el blog para añadir una referencia
http://www.asp.net/ajax/
Dos meses ya trabajando con el Toolkit + .NET Framework 3.5 con resultados satisfactorios.
Saludos.
Miguel.
Permalink
09.11.08
Posted in Humor at 3:16 pm by Miguel
Por cortesía de mis compañeros del equipo de desarrollo donde estoy trabajando los últimos meses, os recomiendo ver la viñeta.

Estimaciones, Basado en un Hecho Real
¿A cuántos nos resulta familiar esta situación?
Saludos.
Miguel.
Permalink
09.09.08
Posted in Arquitectura, Buenas Prácticas, Web at 11:03 pm by Miguel
En posts anteriores hablamos ya alguna vez del Principio de KISS. Hoy toca un ejemplo práctico. Últimamente no paro de cruzarme con situaciones en las que el principio resulta una premisa básica. Para qué complicarse cuando hay soluciones sencillas y facilmente mantenibles que cumplen el requerimiento funcional suficientemente y hacen que el cliente obtenga su objetivo.
Como ejemplo, un botón, no sé si disponéis de una cuenta de Gmail, si es así, podréis comprobar que en las opciones de un e-mail tenéis la de imprimirlo. Hay miles de formas diferentes de provocar una impresión desde web, y la que ha elegido Google es la siguiente:
1) Al pulsar sobre el botón de imprimir se abre una ventana emergente y se carga el texto relacionado al e-mail más información básica de las direcciones implicadas, además aparece también en la parte superior el logo de Gmail
2) A continuación, tras la carga, se lanza una sentencia javascript que provoca que en el cliente se lance el cuadro de diálogo de selección de impresora.
3) El cliente selecciona la impresora y se imprime el contenido actual.
Ala fin. Así de simple. Si quieres imprimir un e-mail, lo imprimes sin problemas, que ese era tu objetivo.
¿Para qué complicarse más?
Saludos.
Miguel.
Permalink
09.04.08
Posted in .NET, Bases de Datos, Programación at 3:17 pm by Miguel
La verdad es que el mundo de los TableAdapters y los Datasets Tipados abre muchísimas puertas de cara a la productividad en el desarrollo y a la disminución de tasa de errores a la hora de trabajar contra una fuente de datos.
El problema que nos hemos encontrado muy amenudo es querer utilizarlos transaccionalmente pero sin emplear la herramienta TransactionScope de ADO.NET, y seguir utilizando una conexión y transacción controlada por nosotros mismos. No sé por qué pero en su día intenté hacerlo así y no hubo manera y tuve que morir al palo del TransactionScope. Recientemente ha vuelto a surgir la necesidad, y, esta vez con Visual Studio 2008 y el Framework 3.5, parece que la cosa ha funcionado.
No sé si es cosa del Framework 3.5 (en su día lo intenté con la versión 2.0), o que hoy me ha dado por estar lúcido, o las otras veces demasiado poco lúcido, pero hoy con una tontería de código he conseguido trabajar de forma transaccional entre dos TableAdapters que están en dos Datasets distintos, compartiendo la misma conexión y transacción.
Sin más rodeos, el código:
public void InsertMarca(string marca, int veces)
{
SqlTransaction tran = null;
SqlConnection con = null;
try
{
// abrimos una conexión
con = new SqlConnection(“Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\BBDD.mdf;Integrated Security=True;User Instance=True”);
con.Open();
//establecemos que la conexión de la marca es la que hemos creado
MarcaTableAdapter adapterMarca = new MarcaTableAdapter();
adapterMarca.Connection = con;
// establecemos que la conexión del coche es la que hemos creado
CocheTableAdapter adapterCoche = new CocheTableAdapter();
adapterCoche.Connection = con;
// iniciamos una transacción sobre dicha conexión
tran = con.BeginTransaction();
// le pasamos a los adaptadores la transacción que acabamos de crear
adapterMarca.Transaction = tran;
adapterCoche.Transaction = tran;
for (int i = 0; i < veces; i++)
{
adapterMarca.InsertMarca(marca);
adapterCoche.InsertCoche(“4444-cxv”, 4, 8);
if (i > 10)
{
// forzamos un error para ver qué pasa
throw new Exception(“prueba de transacción”);
}
}
tran.Commit();
}
catch (Exception ex)
{
tran.Rollback();
}
finally
{
con.Close();
}
}
Aquí la pieza que falta es la siguiente, en el diseñador de Datasets, hay que irse a las propiedades de cada uno de los TableAdapter implicados, y establecer la propiedad “ConnectionModifier” a “public”.
Existen otras soluciones a este mismo problema que podéis encontrar descritas en el siguiente artículo que explica bastante bien el problema:
http://www.codeproject.com/KB/dotnet/transactionta.aspx
En concreto el autor del artículo defiende una solución utilizando Reflection, ya que el otro tipo de solución con la que se suele trabajar para este mismo problema es con el uso de clases parciales.
El problema del uso de las clases parciales es que necesitas generar una clase parcial por cada uno de los TableAdapters, y esto a la larga puede hacer generar mucho código. La solución con Reflection tampoco es la panacea, ya que aunque te evites generar una clase parcial por TableAdapter, aparece el problema de siempre al utilizar Reflection, que nadie te garantiza que tras una actualización del framework la propiedad que has explotado cambie su nombre…
La solución que aporto tiene alguna ventaja, por ejemplo no usa Reflection así que nos evitamos problemas de compatibilidades futuras, tampoco es necesario generar una clase parcial por TableAdapter, lo único que tendremos que hacer es en los casos de actuar de forma transaccional (por experiencia el volumen de accesos transaccionales a base de datos no suele llegar ni a la mitad de los accesos de modificación en base de datos) añadir algunas líneas más en el código.
Eso sí, hay una cosa que no me gusta un pelo, que es que tengamos ahí en el código la apertura y el cierre de la conexión. Que nos dejemos la apertura lo veo complicado ya que si lo probamos aunque sea una vez nos dará error. El terrible problema lo veo en el cierre de la conexión, ya que es la historia de antaño, como te dejes el finally acabas de reventar tu sistema. Dándole más vueltas, este problemas con una clase que maneje la conexión, que herede de la interfaz IDisposable y que utilicemos mediante un using podríamos solventar el problema de olvidarnos cerrar la conexión, pero vamos a seguir teniendo que tragarnos el asignar la conexión y la transacción a todos los adapters que participen.
Saludos.
Miguel.
Permalink
09.01.08
Posted in AgilePoint, BPM at 7:47 am by Miguel
Como hemos hablado en anteriores artículos, “WorkToPerform” es una de las propiedades que definen algunos tipos de actividad de AgilePoint (como por ejemplo las actividades “Manual” y “Manual with TimeOut”).
En esta propiedad definimos cuál es el trabajo a realizar. A niveles prácticos el trabajo a realizar se resume en un interfaz gráfica que va a ser la encargada de transmitirnos cuál va a ser el siguiente paso en el flujo y de almacenar cierta información de negocio que maneje nuestra propia aplicación. Resumiendo, “WorKToPerform” es nuestro link entre la actividad del flujo y la capa de presentación.
El típico ejemplo de uso de “WorkToPerform” sería por ejemplo definir que en la actividad “Aprobar Compra” el “WorkToPerform” a realizar sería “Aprobar”. Esto se traduciría en que vamos a necesitar en nuestra aplicación (pongamos que es una aplicación web) un fichero “compra.jsp” o “compra.php” o “compra.aspx” que es el que visualizará el usuario y en el que nos aparecería un hipotético radiobutton que marca o “Aprobar” o “No Aprobar”.

WorkToPerform de un nivel
Hasta aquí va la cosa más o menos bien, tenemos una página web para cada acción a realizar y listo.
Vamos a complicar ahora un poco más la cosa y vamos a suponer que debido a requerimientos funcionales, nuestra actividad del flujo no viene representada por un único aspx, si no que puede ser representada por varios ascx (controles de usuario ASP.NET), y vamos a complicarlo un poco más aún, que dichos ascx deberían poder cargarse en diferentes paneles ubicados en un aspx concreto.

Página con varios paneles contenedores
¿Cómo podríamos resolver este problema mediante el uso de “WorkToPerform”? Pues tal vez pudiéramos hacer algo similar a esto:
PanelConfirmaciones:Confirmacion1,Confirmacion2//PanelNegocio:Negocio3,Negocio4

WorkToPerform Multi-Nivel
Analizando un poco la estructura podremos ver que lo que acabamos de hacer es definir en qué panel vamos a cargar una serie de controles ascx. Por lo que necesitamos tener en nuestra aplicación una carpeta con los siguientes ficheros: Confirmacion1.ascx, Confirmacion2.ascx, Negocio3.ascx y Negocio4.ascx.
Ahora lo que queda es que en nuestra capa cliente, cuando recibamos la información relacionada a la actividad, capturemos su “WorkToPerform” y lo interpretemos a la hora de cargar la página para cargar los ascx definidos de forma dinámica.
Fantástico, de esta forma tan sencilla hemos conseguido definir un sistema que permite trabajar a múltiples niveles (imaginaros por ejemplo que pueden existir diferentes paneles cargados en diferentes pestañas de un control de tabuladores)
Saludos.
Miguel.
Permalink