domingo, 21 de febrero de 2010

Cómo introduje Scrum en un equipo novel. Cap.2.

Lo bueno y lo malo de las empresas pequeñas es que hay que compartir el tiempo entre varios proyectos y, además, a toda prisa. A mí me pagan para evitarlo. Para que la productividad de la empresa no decaiga a causa de las necesidades comerciales  - trampa irreversible que ha llevado a muchas empresas a la quiebra o a la mediocridad.

Esto viene a cuenta de que he tenido que dejar a mi equipo prácticamente solo durante estas dos semanas, tutelándolos solamente con las reuniones diarias y “sacándolos del nido” mucho antes de lo que hubiera querido. Pero las cosas han ido bastante bien.

Finalizamos el sprint cero con una retrospectiva en toda regla. Tres columnas – positivo, negativo y propuestas – empezando por la más difícil: lo positivo. Me llamó la atención que una de las primeras cosas que saliera es el buen rollito del equipo y mi presencia como mentor técnico y de metodología.

Cuando llegamos a lo negativo, la tecnología se conformó como el bien y el mal del proyecto ya que al – por decisión mía - tener test unitarios, Linq to entities, arquitectura por capas y Scrum, lo ven como un auténtico “mal necesario”. También son conscientes de la necesidad de la autoformación y es una de las decisiones que se unió al listado de cosas que podíamos hacer para mejorar.

Al igual que me pasó en el Open Agile, es muy curioso como pequeños detalles se muestran con una prioridad inesperadamente alta cuando lo expones ante el grupo: la calefacción está demasiado alta. Y a gerencia hemos ido con la petición, y gerencia ha actuado de forma resolutiva.

Entonces empezamos el sprint 1 y aprieto un poquito más las tuercas. Un autentico Sprint Planning compuesto por dos fases diferenciadas: 1. Hablo un par de horas explicando la funcionalidades que quisiera cubrir en las dos siguientes semanas. 2. Cuatro horas en donde vamos detallando las tareas, estimando y comprometiéndonos con el Sprint Backlog. Como son noveles, subo de las 80 horas a 140 horas a cumplimentar en las dos semanas. Pero tened en cuenta que no son horas reales, son puntos de trabajo pero nombrados como horas para que les sea más fácil visionar que el objetivo es acercarse lo más posible al máximo de horas reales en dos semanas. También hay que notar que faltan mis horas, previstas finalmente en unas paupérrimas 20 horas, que finalmente no han cumplido.

Además, les pido que empiecen a insertar los errores en los Work Item para empezar a construir las métricas sobre errores.

Por último, y para ir cerrando este post, he encontrado un error en la lectura del BurnDown Sprint de Cochango. La estaba leyendo al revés. Lo malo es si la línea se queda por encima de las horas previstas. Y yo leía que era malo si se quedaba por debajo. Por lo cual mi equipo es aún mejor de lo que pensaba. Y he añadido un gráfico buenísimo para que sepamos aun mejor qué está ocurriendo: el Sprint Flow Chart. Me indica claramente qué hemos acabado, cuanto estamos en tarea y cuanto queda por iniciar.

Esta semana empezamos la segunda parte del sprint 1 con un retraso importante ya que uno de los dos está demostrando ser más novel de lo esperado. Pero he de sacar tiempo para echarles una mano, ya no tanto para que la tarea se cumpla en tiempos, si no para mantener su entusiasmo en alto.

Ah, el cliente esta CONTENTO de ver que en dos semanas ya tiene algo en las manos.

P.D. Los Daily Meeting SIEMPRE de pié. Se nota una barbaridad.

Cómo introduje Scrum en un equipo novel. Cap. 1
Kanban, Scrum, CMMI Light

domingo, 14 de febrero de 2010

Error de response.redirect.

Interesante y molesto error en asp.net cuando realizas un response.redirect y devuelve un error.

Eliminando la causa de que estés haciendo una redirección cíclica, como por ejemplo poner el redirect en la masterpage y apuntando a una página hija suya. Lo más seguro es que te falte configurar las capacidades de redirección HTTP en el servidor IIS – si lo estás probando en un Win7.

image

Si lo activas, dejará de salirte el error.

Migrar esquema MySql a Base de datos SQL 2008

Buena, en un nuevo e interesante proyecto tengo que migrar un esquema de MySql a una base de datos MS SQL 2008 Server.

Esto lo he realizado de la forma larga que es instalando un MySQL, importando el script del esquema, y utilizando la herramienta SSMA 2008 for MySQL de Microsoft.

Este post está centrado en las dudas y problemas que he tenido en realizar los pasos antes descritos.

Paso nº 1.

  • Instalar el MySQL 5.1.34. Y darle al usuario root una contraseña de la que me acuerde.
  • Instalar las gui tools 5.0. r17.

En la instalación por defecto, el MySQL queda arrancado como un servicio windows.

Aquí me encuentro el primer “problema” . Que es cuando abro el MySql Administrator y me pide el nombre del host al que me quiero conectar…

image

… la solución es tan sencilla como inesperada para los que estamos acostumbrados a la excelente gestión automática de servidores de SQL Server. El nombre de la instancia es localhost y el usuario root. Más tarde podrás añadir los usuarios que necesites.

Una vez conectado seleccionas en Tools –> My Sql Query Browser, que es en donde gestionas los scripts de los esquemas.

Me ha encantado la función Drag & Drop en donde arrastrando una tabla de un esquema al editor de script, automáticamente te escribe la Select.

image

A continuación, en una ventana limpia cargo el script del esquema que quiero recuperar. Para ello selecciono File –> Open Script y selecciono el fichero .sql que más me convenga.

Y aquí llegamos al segundo impedimento que me encontré. El script, al lanzarlo, me devolvía un error indicando que no sabía que  base de datos utilizar. A lo que, utilizando lo de Drag & Drop, le señalé el esquema en donde desplegarlo. Poniéndome el solito la sentencia Use que necesitaba al principio del script.

Ya tengo mi esquema completito y toca migrarlo a SQL Server. Para ello busco en San Google y me encuentro una herramienta de Microsoft llamada SSMA 2008 for MySql que hace lo que yo quiero. La bajo y la instalo. Llegando a la siguiente detención en el proceso.

El instalador no encuentra ningún conector MySql en el sistema, pero me da la oportunidad de bajármelo de la Web de MySql. Entro en el Site y selecciono el fichero mysql-connector-net-6.2.2.zip y lo descargo sin registrarme (aunque debiera).

image

Por si las moscas, instalo el conector completo. Pero, por la ley de Murphy y por la manía de no leer las instrucciones, me he instalado el driver para .Net y lo que me pide la aplicación es un driver ODBC por lo cual me vuelvo a ir a la web de MySql y me bajo Connector-ODBC 5.1.6.

Cierro la instalación de SSMA y vuelvo a iniciarla encontrándome, ahora sí, el conector odbc. Como anteriormente, lo instalo completo no vaya a necesitar esa función extraña que nadie usa .

Siguiente impedimento: extrañado veo que el programa me pide que registre una licencia. Abro el enlace, y me valido con mi liveId, abriéndose un formulario de registro.

image

Cuando pulso el botón aceptar, la página me remite un fichero .license que supongo debo situar en el directorio que me señala la aplicación SSMA

image

Pulso el botón Refresh License y voala!! ya tengo mi aplicación registrada… (que curioso, no lo había tenido que hacer anteriormente más que en las licencias de pago)

image

Por fin se abre la ventana de Migración y creamos un nuevo proyecto de migración indicándole que el destino es una base de datos SQL Server.

image

Acto seguido me conecto a las dos bases de datos. Primero a la MySQL y segundo a la SQL 2008. Y aquí me encuentro con impedimento más: no tengo la posibilidad de realizar una búsqueda de las instancias de sql 2008, por lo cual tengo que abrir el SQL Manager y copiar el nombre de la instancia.

image

Y entonces también me doy cuenta que me obliga a meter la base de datos de destino… lo cual me recuerda que no la tengo echa!!. Asiqué aprovecho que tengo abierto el Sql Manager y me creo la base de datos destino para poder introducirla, después, en  la ventana de conexión del SSMA.

Y aquí viene un GRAN impedimento… no funciona con SQL 2008 Express :(

image

Pero para eso están las máquinas virtuales y más de una y más de dos tengo con algún SQL Server instalado… pero otra vez la Ley de Murphy aparece en toda su extensión y dos horas después aún me estoy pegando para que tener acceso a alguna instancia de un SQL 2008 server.

… unas horas más tarde aún me sigo pegando con las instancias de sql2008 a las que quiero tener acceso…

Bueno, finalmente he conseguido tener mi instancia de Sql 2008 server en funcionamiento y la cosa es tan simple como que hay dos únicos pasos.

1. Convertir el esquema, que pasa la estructura de la base de datos MySql a Sql Server.

2. Migrar datos.

Y con esto y un bizcocho, tengo replicada la base de datos MySql en Sql 2008 server.

image

miércoles, 10 de febrero de 2010

Eventos y delegados en C#. Error de delegado = null.

En este post hice un ejemplo de cómo crear un evento en un botón de un formulario y suscribirlo desde otro. Es decir, pulso el botón en el formulario B y pasa algo en el formulario A.
Pero lo mezcle con el uso del patron singleton y se me había escapado un insidioso error que me ha llevado, una vez más, un par  horitas hasta encontrar el problema.
Recapitulemos:
“ Tenemos un formulario A en donde tenemos un método llamado hazAlgo()"
” Tenemos un control de usuario llamado donde tenemos un botón ACEPTAR”
” Cuando pulsamos el botón ACEPTAR se debe lanzar el método hazAlgo()”
1º Insertamos el control de usuario en el formulario A (no te voy a explicar como se hace, búscalo en San Google) y le vamos a llamar cuAlerta.
2º En el control de usuario declaramos el Delegado en el ámbito del namespace. Es decir por encima de las clase del control de usuario. Es decir, lo más sencillo es ponerlo justo debajo de la última declaración del using.
Esto es debido a que el delegado debe ser visible continuadamente, y no estar dentro de la construcción/destrucción de la clase.
public delegate void bAceptarClickHandler();
3º Declaramos el evento justo después del inicio de la declaración de la clase y antes del primer método.
public event bAceptarClickHandler bAceptar_Click;


4º Declaramos un método virtual que va a lanzar el evento. No tengo claro si este punto se puede enlazar con el siguiente, pero supongo que sí. Yo prefiero tenerlo separado para un mayor control.


protected virtual void OnAceptarClick()
{
if (bAceptar_Click != null)
{ bAceptar_Click(); }
}


5º Dentro del evento del botón ACEPTAR invocamos al método virtual que lanza el evento.


public void bAceptar_Click(object sender, EventArgs e)
{
OnAceptarClick();
}


6º Nos vamos al formulario A y suscribimos al método hazAlgo() con el evento bAceptar_Click().

Esta suscripción hay que hacerla fuera del !IsPostBack para que en cada ciclo de destrucción/construcción de la página (acuerdate que en ASP.NET no se mantiene el estado, por lo cual se tiene que recargar cada vez todo) se vuelva a suscribir al evento del control de usuario.


protected void Page_Load(object sender, EventArgs e)
{
cuAlerta.bAceptar_Click += hazAlgo;

if (!IsPostBack)
{
…
}
}

Y si sigues estás 6 reglas te funcionará como la seda.

lunes, 8 de febrero de 2010

Fallo del Update con Linq to Entities

Hoy cogería al arquitecto responsable de los mensajes de error de Linq to Entities por el cuello y lo ahogaría – pero sin matarlo – por la tarde perdida siguiendo un mensaje de error que desorientaba y que me hizo recorrer los profundos vericuetos de San Google para, al final, hallar la solución en mis narices.

Compañeros: Cuando tienes una bonita entidad y le creas un método de actualización como:
public bool ModificarAsignacion(string codDeposito, int idTurno)
{
Madrid_MovilidadEntities dbv = new Madrid_MovilidadEntities();
var asig = (from asignacion in dbv.Conductores_Gruas
where asignacion.codigo_deposito == codDeposito
&& asignacion.id_turno == idTurno
&& asignacion.asignado == true
select asignacion);

if (asig.LongCount() > 0)
{
foreach (Conductores_Gruas asignados in asig)
{
asignados.asignado = false;
}
dbv.SaveChanges();
           }
Y cuando, lo lanzas se te queda tostado el servidor - si le tienes un Try Catch puesto – o te sale un mensaje espantoso que dice algo así:

“La búsqueda de no se pudo actualizar entityset 'conductores_gruas' porque tiene <definingquery> y no existe ningún elemento <updatefunction> en el elemento <modificationfunctionmapping> que admita la operación actual. no obtuvo ningún resultado.”

No te agobies buscando y buscando. Es más no te compliques la vida cambiando de tecnología e intentándolo con Linq to SQL (tampoco te va a funcionar y tampoco te va a decir porqué). La solución es mucho más sencilla.<

En tu modelo de datos hay una tabla sin clave o claves primarias
 
Lo cual lleva al ORM a avisarte (si es cierto que avisa, pero no le hice ni caso) y te crea una vista o entidad de esta tabla SOLO PARA LECTURA.

Por lo cual la solución es modificar el modelo de datos para que esa tabla tenga al menos una clave primaria. Y actualizar (yo lo borré y lo hice de nuevo) el modelo de entidades… y voala!!

sábado, 6 de febrero de 2010

Como introduje Scrum en un equipo novel.

Bueno, segunda semana con el equipo de noveles. Segunda semana de satisfacción y segunda semana de tensión para poder trabajar en el proyecto.

Después de varias instalaciones y mucha configuración tanto el servidor de SQL server 2005 como el TFS 2008 ya están funcionando ambos y con los permisos adecuados. Además, y por fin, nos  ha llegado la pizarra en donde hemos dibujado de forma inmediata un panel Scrum, olvidando –por ahora- Kanban. Ya que Scrum me es más útil tanto para la disciplina del equipo como para la visibilidad del proyecto ante los jefes y el cliente.

Como no tengo Product Owner, tengo que ejercer ese Rol y medio fusionarlo con el Scrum Master. La realidad siempre es más complicada que la teoría. Por lo cual para empezar el sprint cero defino el Product Backlog que es muy, muy corto, para así darle un ancho margen de beneficio al equipo. Ya que son bastante más nóveles de lo que me esperaba.

Una vez configurado el TFS, les explico lo que es un Sprint Planning meeting y empezamos a dividir el módulo principal para elegir las tareas del Sprint Backlog. Aún no les pido estimaciones de tiempo para no liarles mucho, pero si les empujo a realizar una subdividision de las tareas elegidas para el sprint para que miren más allá de la tarea general y descubran las actuaciones que constituyen la construcción de la misma. Así, más adelante podrán estimar con mayor seguridad y estarán acostumbrados a localizar la simplicidades y complejidades inherentes al desarrollo de una pieza del puzle.

Empezamos el sprint cero y les explico que nuestro trabajo debe generar métricas para su seguimiento y que para ello cada vez que se vaya a realizar un check-in de código, van a tener que elegir el workitem al que se va a asignar el mismo. Así obtenemos la métrica del trabajo realizado y por realizar y les muestro nuestra estrella de la métrica: el BurnDown Sprint.

Lo siguiente es un acto de fe: los Daily Meeting. Les veo emerger la duda en los ojos cuando les digo que son reuniones diarias, a la misma hora, de pie y de no más de quince minutos. Además, para reforzar su sensación de inutilidad, como están trabajando en pareja las respuestas  a las tres preguntas, ¿Que hice ayer?¿Que haré hoy?¿Que impedimentos tengo?,  son iguales.

El mejor momento llega después de la segunda reunión diaria cuando me señalan que yo no he respondido a mis tres preguntas… ya está la magia en acción !!  Al igual que en casos anteriores, las reuniones diarias se convierten en una costumbre que le encanta a los equipos y multiplica la comunicación.

El cuarto día del sprint cero la pizarra tiene un aspecto buenísimo. De un vistazo vemos el estado del sprint y, efectos de iniciar el proyecto con Kanban, se auto limitan las tareas sin que me tenga de preocupar de tener demasiadas abiertas y sin cerrar. Además, los test unitarios ya son parte del propio desarrollo y poco a poco están enseñando su gran ventaja. No los ven como el doble de trabajo, si no como un nivel más de seguridad y un reto intelectual.

La comunicación es muy buena y el ritmo, teniendo en cuenta que en mi previsión solo habíamos cogido tareas para el 50% del sprint, bastante aceptable.

La última alegría es cuando pongo mi flamante BurnDown Sprint Chart en la puerta del armario y se lo enseño a mis dos directores… los cuales lo entienden de forma inmediata. El tablero Kanban - ahora Scrum – ya lo están utilizando para saber como vamos. O sea, que todos contentos (y eso que la documentación CMMI la tengo un poquillo abandonada).

El viernes, medio sprint atrás ya, les enseño a iniciar y cerrar WorItem en el tfs. A crear nuevos, desde el Web Acess, relacionados con los demás. Y les dejo irse con un pequeño lio en la lógica de negocio pero con HAMBRE de hacer las cosas bien.

La próxima semana tengo que darle acceso a los informes del TFS/Cochango a los directores y explicarles qué están viendo. Preparar un servidor de desarrollo con acceso desde internet para publicar los desarrollos al que el cliente tenga acceso y que me dé el feedback lo antes posible y de forma continuada. Y preparar el fin del sprint, la retrospectiva, decidir la longitud del siguiente sprint, preparar la mudanza a las oficinas del cliente y vuelta a empezar.

Muy mal debe ir la cosa para que este proyecto no sea otro éxito.

jueves, 4 de febrero de 2010

Leer la conectionstring del web.config desde un proyecto asociado.

Premisa: Tenemos una solución compuesta por un proyecto del tipo Sitio web y un proyecto del tipo Biblioteca de clases en donde estamos construyendo nuestra capa de datos y que hemos llamado DAL.

Queremos, desde la DAL, recuperar la conectionstring que está almacenada en el web.config del proyecto Sitio Web.

Solución:

1. Añadir las referencias del tipo .NET:
     System.Web
    System.Configuration

referencias

2. Añadir los using, que sí son duplicados pero nos simplifican la sintaxis del código.
using System.Web;
using System.Configuration;

3. Invocar a la cadena de conexión en el web.config con el siguiente método (o la variación que se te ocurra.

/// <summary>
///
Devuelve la cadena de conexion a la base de datos
/// </summary>
/// <returns></returns>
public string cadenaConexion()
{
string retValue = string.Empty; ;
retValue = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["pruebaConnectionStringSQL"].ConnectionString;

return retValue;
}
Listo.