Kata CodeBreaker

Reblogueado desde El programador perdido:

Haz clic para visitar la entrada original
  • ORIGEN

Esta Kata la hemos obtenido de:

http://www.solveet.com/exercises/Kata-CodeBreaker/14

  • ENUNCIADO

El doctor Maligno se encuentra a salvo en su super laboratorio secreto a 500 metros bajo tierra. Para atraparlo, sólo tienes que acertar el código de seguridad de la puerta acorazada de la entrada. Este código cambia cada día, así que deberás darte prisa en acertarlo.

El código de seguridad consiste en una secuencia de cuatro colores.

Leer más… 1.194 palabras más

SQLBulkCopy Batch con gestión de tabla destino

He creado una librería para el volcado masivo de datos de un fichero a una base de datos Sql Server. Para este trabajo ya existe el objeto SQLBulkCopy en la librería System.Data.Client pero una de las cosas que hecho en falta de este objeto es que tienes que crear la tabla y después insertar los datos.

Para la inserción masiva de datos en base de datos siempre he utilizado el programa bcp.exe que nos ofrece SQl Server. El inconveniente es que para usarlo desde código tienes que crear un proceso System.Diagnostics.Process, llamar al programa bcp.exe y lanzadlo y dependiendo si lo configuras síncrono o asíncrono esperar a que termine. Además debes de capturar el hilo para en caso de error durante el volcado poder capturar el error que devuelve el sistema operativo.

Como al final por unas y por otras he tenido que cambiarlo he creado esta librería en la que puedes pasarle un objeto DataTable o DataSet y volcar tus tablas en memoria en base de datos, con las siguientes opciones.

-         Crear la tabla si no existe.

-         Borrar los datos de la tabla a volcar si existe.

-         Borrar la tabla si existe y crearla.

-         Crear tabla y volcar datos.

-         Configurar el tamaño de volcado de datos.

La librería esta formada por dos clases:

-         SqlBulCopyBatch.cs contiene los métodos del volcado de datos y son los mismos métodos que el objeto SQLBulkCopy

-         SqlCreateTable.cs contiene todos los métodos de relativos a la tabla de destino.

SqlBulCopyBatch

Constructores de la clase, para los diferentes formatos de cadenas de conexión,


private SqlBulkCopy _bulkCopy;
private SqlTransaction _transaction;
private SqlConnection _connection;

<span style="line-height: 1.5;">public SQLBulkCopyBatch(SqlConnection cnn)</span>
{
_connection = cnn;
_bulkCopy = SQLBulkCopyBatchTransacional(cnn);
}

public SQLBulkCopyBatch(string connectionString)
{
_connection = new SqlConnection(connectionString);
_bulkCopy = SQLBulkCopyBatchTransacional(_connection);
}

private SqlBulkCopy SQLBulkCopyBatchTransacional(SqlConnection cnn)
{
cnn.Open();
//_transaction = cnn.BeginTransaction();
//
return new SqlBulkCopy(cnn, SqlBulkCopyOptions.Default, _transaction);
}

Método que inserta los datos en la tabla de base de datos tiene la opción de borrar la tabla si existe.

Si no especificamos el tamaño de inserción hará un bulkcopy de la tabla entera.


/// <summary>
/// Inserta datos en bbdd
/// </summary>
/// <param name="table">Tabla que se va a insertar. Es el mismo nombre que la tabla en bbdd</param>
/// <param name="DeleteTableIfExist">Si es true borra la tabla y la crea sino no la crea y no la borra</param>
public void InsertarDatos(DataTable table, bool DeleteTableIfExist)
{
ISqlCreateTable tableSQL = new SqlCreateTable();
tableSQL.CrearTablaEnBBDD(table, _connection, DeleteTableIfExist);

_bulkCopy.BulkCopyTimeout = 0;
_bulkCopy.DestinationTableName = table.TableName;

sizeBatchInsert = (sizeBatchInsert == 0) ? table.Rows.Count : sizeBatchInsert;

while (table.Rows.Count > sizeBatchInsert)
{
_bulkCopy.WriteToServer(table.Select().Take<DataRow>(sizeBatchInsert).ToArray<DataRow>());

for (int i = 0; i < sizeBatchInsert; i++)
{
table.Rows[0].Delete();
}
}
_bulkCopy.WriteToServer(table.Select().Take<DataRow>(sizeBatchInsert).ToArray<DataRow>());
}

Propiedad para especificar el tamaño del bloque de inserción.

public int sizeBatchInsert { get; set; }

SqlCreateTable

Constructor de la clase que crea la tabla condicionada si existe en base de datos o no.

Capturo la excepción de SQLServer  2714 que me indica que la tabla existe y se puede  ejecutar un Truncate table para vaciar la tabla.

</span>
public void CrearTablaEnBBDD(DataTable table, SqlConnection _connection, bool DeleteTableIfExist)
{
try
{
if (DeleteTableIfExist)
{
BorrarTablaIfExite(table.TableName, _connection);
}

string sql = GetCreateFromDataTableSQL(table, DeleteTableIfExist);
SqlCommand cmd = new SqlCommand(sql, _connection);
if (_connection.State == ConnectionState.Closed) _connection.Open();
cmd.ExecuteNonQuery();
}
catch (SqlException ex)
{
if (ex.Number != 2714) throw ex;
}
}

Métodos privados que crean los comandos SQL para crear y borrar la tabla.

private bool BorrarTablaIfExite(string nombreTabla, SqlConnection _connection)
{
if (_connection.State == ConnectionState.Closed) _connection.Open();

SqlCommand command = new SqlCommand("", _connection);

command.CommandText = "IF object_id('" + nombreTabla + "') IS not null " +
"BEGIN  DROP TABLE " + nombreTabla + " END\n";

command.ExecuteNonQuery();

return true;
}

private string GetCreateFromDataTableSQL(DataTable table, bool DeleteTableIfExist)
{
string sql = "";

sql += "CREATE TABLE " + table.TableName + " (\n";

foreach (DataColumn column in table.Columns)
{
sql += "[" + column.ColumnName + "] " + SQLGetType(column) + ",\n";
}
sql = sql.TrimEnd(new char[] { ',', '\n' }) + "\n";

if (table.PrimaryKey.Length > 0)
{
sql += "CONSTRAINT [PK_" + table.TableName + "] PRIMARY KEY CLUSTERED (";
foreach (DataColumn column in table.PrimaryKey)
{
sql += "[" + column.ColumnName + "],";
}
sql = sql.TrimEnd(new char[] { ',' }) + "))\n";
}

if ((table.PrimaryKey.Length == 0) && (!sql.EndsWith(")")))
{
sql += ")";
}

return sql;
}

private string SQLGetType(DataColumn column)
{
return SQLGetType(column.DataType, column.MaxLength, 10, 2);
}

private string SQLGetType(object type, int columnSize, int numericPrecision, int numericScale)
{
switch (type.ToString())
{
case "System.String":
return "VARCHAR(" + ((columnSize == -1) ? "255" : (columnSize > 8000) ? "MAX" : columnSize.ToString()) + ")";

case "System.Decimal":
if (numericScale > 0)
return "REAL";
else if (numericPrecision > 10)
return "BIGINT";
else
return "INT";

case "System.Double":
case "System.Single":
return "REAL";

case "System.Int64":
return "BIGINT";

case "System.Int16":
case "System.Int32":
return "INT";

case "System.DateTime":
return "DATETIME";

case "System.Boolean":
return "BIT";

case "System.Byte":
return "TINYINT";

case "System.Guid":
return "UNIQUEIDENTIFIER";

default:
throw new Exception(type.ToString() + " no contemplado.");
}
}

Estoy modificando la librería para que sea transsacional, cuando la tenga terminada la subiré.

Esto es todo espero que os sea útil.

Open Space, “Sprint 0, Antes de empezar”

Open Space “Sprint 0, Antes de empezar”

Las metodologías ágiles nos ofrecen una serie de artefactos para desarrollar proyectos basándose en los principios ágiles. La parte mas oscura de esta metodología es la parte en la que hay que saber que tenemos que hacer antes de empezar el proyecto, donde nos surgen diferentes preguntas.

¿Cómo convencer a nuestros a nuestros clientes de que esta es la mejor manera de trabajar, para llevar el proyecto a buen puerto?

¿Si un cliente tiene un presupuesto determinado (Coste o Alcance) como podemos saber con metodologías ágiles si nos saldrá rentable?

¿Con proyectos de tiempo finito impuesto por el cliente la mejor opción es únicamente realizar ciclos de crecimiento hasta donde le llegue el presupuesto?

¿Cómo hago un contrato ágil con mi cliente?

¿Un cliente me sale rentable, si no me aporta nada?

Por otra parte si aplicamos metodología Lean, para basar nuestro crecimiento en aprendizaje validado.

¿Cómo hago al cliente entender que el crecimiento es el aprendizaje validado?

¿Cómo consigo clientes que quieran crear – medir – validar conmigo?

Si mis clientes no quieren aceptar mis términos

¿Cómo negocio con ellos para que los acepten?

¿Cómo busco clientes ágiles?

Para todas estas preguntas no hay una metodología, debemos basarnos en nuestro aprendizaje obtenido en experiencias anteriores. Por eso he pensado en montar este Open Space en el que todos podemos proponer temas en los que podemos aportar y aprender.

He creado un meetup en el que podéis apuntaros

http://www.meetup.com/madriagil/events/109385582/

El evento será en MadridOnRails el Sábado 6 de Abril de 10:00h a 18:00h

Gracias a todos

@francalles

Los números de 2012 en el blog

Los duendes de las estadísticas de WordPress.com prepararon un informe sobre el año 2012 de este blog.

Aquí hay un extracto:

600 personas llegaron a la cima del monte Everest in 2012. Este blog tiene 2.000 visitas en 2012. Si cada persona que ha llegado a la cima del monte Everest visitara este blog, se habría tardado 3 años en obtener esas visitas.

Haz click para ver el reporte completo.

Corporate Agile

Ayer estuve en el evento Corporate-Agile organizado por el Banco Santander y Agile-Spain.

Este evento intento llevar las metodologías ágiles a las grandes compañías (más de 200 empleados) y demostrar que es posible aplicar esta metodología en este tipo de organizaciones y que la cultura agile no es solo para unos “frikis” y para startups.

El lugar elegido para el evento fue en la ciudad del Banco Santander, nunca mejor dicho lo de ciudad, las instalaciones son verdaderamente asombrosas.

Los presentadores y organizadores fueron Jose Manuel Beas (@jmbeas) y Jesús Ruiz, nos hablaron de cómo en el Banco Santander han apostado en el departamento de Innovación tecnológica por empezar a usar este tipo de metodologías. Se han dado cuenta de que el modelo de negocio esta en constante cambio y hay que adaptarse lo mas rápido posible a estos cambios y para ello la mejor solución son las metodologías ágiles.

Hubo cinco charlas

I – Xavi Gost “Introducción al agilísimo”

Hizo una breve explicación de que son las metodologías ágiles, cuales son sus ventajas, que es scrum y XP y como funcionan ellos en becodemyfriend

Habló de cómo se debe valorar el software ya que es algo que lo cubre todo, es algo artesano que no se puede industrializar ya que cada software es diferente. Además si se intenta industrializar no funciona. Sería como intentar industrializar Picassos.

Conclusiones:

  • Las metodologías ágiles son necesarias.
  • Hay una forma diferente de hacer las cosas.
  • El software es algo artesano.
  • La cultura ágil se basa en aportar valor, no somos una ONG.

II – Ángel Díaz-Maroto “El agilísimo en el mundo real”

Nos explico su experiencia en ING Direct como lleva desde hace 2 años y medio implantando metodologías ágiles en esta compañía y poco a poco esta viendo cambian las cosas.

También nos enseño los valores en los que se basa la cultura ágil, valores como la confianza y respeto son necesarios para llevar a cabo cualquier proyecto.

El ser ágil no es una certificación, es un camino, es largo y hay que recorrerlo sabiendo que nunca acaba, por lo que hay que ponerse a estudiar ya.

Conclusiones:

  • Agilidad no es una metodología es una cultura.
  • Debes contar con el apoyo de la dirección para cambiar cosas en las compañías.

III – Xavier Quesada “Proyectos exitoso, Proyectos fracasados”

Xavier contó su experiencia en empresas de correos belgas y de cómo por casualidad acabo en esto del agilísimo.

En estas empresas hubo proyectos que fueron mal con el clásico waterfall y hubo que rescatarlas gracias a las metodologías ágiles.

Es necesario tener un coach o un consultor ágil para implementar esta forma de trabajo, ya que, otro ejemplo de compañía en la que estuvo, cuando el se fue se volvieron a hacer las cosas como se hacían antes. Se debe implantar una cultura agil en la empresa y en los miembros de los equipos para que esto no pase.

Nos mostró fotos durante la charla de tableros Kanban de oficinas donde ha trabajado y de lo importante que es tener visible para cualquiera que lo desee la información de quien esta haciendo que en cualquier momento.

Algo esencial para que un proyecto termine con éxito es que el equipo este motivado. Si el equipo esta motivado se trabaja mejor, con más calidad y en consecuencia se aporta valor a la empresa y al software.

Conclusiones:

  •  Es necesario tener información visual de lo que se esta haciendo en cada momento para mejorar la productividad y aportar valor.
  • Debes contar con el apoyo de tu equipo.
  • Debes saber cual es tu objetivo y no desviarte.
  • El equipo debe estar motivado.

IV – Carmen Lasa “Creando cultura ágil en Telefónica PDI”

Carmen lleva muchos años trabajando en Telefónica aunque empezó como auditora de proyectos waterfall pronto cambió a metodologías ágiles.

Empezaron a utilizar metodologías ágiles por casualidad en su sede de Barcelona y desde entonces han conseguido que toda la parte de Telefónica I+D utilice estas metodologías.

Ha escrito el libro “Metodologías ágiles y SCRUM”.

En telefónica Son un ejemplo claro de cómo estas metodologías se pueden instaurar en una gran compañía.

Conclusiones:

  • Las frases como “aquí no se puede”, “es que este proyecto es especial”… quedan como excusas y se usan como escudo para no cambiar y seguir como estamos.
  • Abracemos el cambio.

V – Roberto Canales “Encajando Agilísimo con Gobierno y Gestión de sistemas de información”

Roberto es Ceo de Autentia intentan industrializar el software creando framewoks para desarrollo en otras compañías. También son los creadores de la serie Terrakas,  terrakas.com y del portal www.adictosaltrabajo.com , en el que se pueden encontrar multitud de manuales sobre casi cualquier tema.

Roberto nos explico de forma muy visual como a medida que una empresa crece se separa y se alejan tanto en concepto, como de forma física los departamentos de negocio y departamentos técnicos. Por lo que al final los técnicos que realizan un proyecto tiene un sentimiento desmotivador que complica que el proyecto finalice con éxito.

Algo muy representativo es que para hacer un proyecto técnico, la mayor parte de los stakeholders no son técnicos. Nos enseño una tira cómica de la película 300 bastante clarificadora.

Os dejo aquí el link de su presentación. No tiene desperdicio http://www.slideshare.net/rcanalesmora/corporate-agile

Conclusión:

  • Para hacer un buen trabajo contrata buenos profesionales.
  • Técnicos profesionales pueden abarca la mayor parte del proyecto.
  • Haz las cosas sencillas.

VI – Rodrigo Corral “Llevando Scrum a grandes organizaciones: errores, lecciones y resultados”

Rodrigo trabaja en Plain Concepts una empresa especializada en tecnologías .net y creadora del portal de ayuda en .net http://www.geeks.ms

Rodrigo estuvo implantando metodologías ágiles en Panda y en Plain concepts. Primero fue en Panda aunque el riesgo de implantar estas metodologías le costo el trabajo, pero también le costo que luego le volvieran a llamar para instaurar las metodologías ágiles y que luego le volvieran a despedir.

Explico las lecciones que había aprendido como que no se pueden llevar a cabo estas metodologías sin el apoyo de la dirección, que hace falta un coach para implantarla, que las personas deben cambiar su forma de ver las cosas…

Conclusión:

  • Aprender de los errores.
  • Ser fiel a tus principios.
  • Tarde o temprano todos seremos ágiles.

A grandes rasgos estas fueron las charlas que se dieron y esas mi conclusiones.

Doy la enhorabuena a los organizadores y ponentes siempre es bueno aprender de los que saben de verdad de esto. Aunque intente que viniera el director de mi empresa y al final no pudo ser, intentare transmitirle todo lo que se dijo y que se interese por estas metodologías, mientras tanto seguiré haciendo “agile personal” y “agile desde las trincheras”.

También quiero dar las gracias a Sergio Sánchez, jefe de departamento de desarrollo, que sí conseguí que viniera y creo que aunque sea poco alguna fibra le hemos tocado y podremos empezar a cambiar cosas en nuestra empresa.

Esto es todo

Kata TDD FizzBuzz

Hola a todos

Utilizando Test Driven Devlopment (TDD) voy a realizar esta Kata. Es de las fáciles que he encontrado ya que no se usan mocks, ni stubs y pata empezar en esto del TDD es muy buena para cojer conceptos.

El enunciado del ejercicio es el siguiente:

Imagínate el siguiente escenario. Tienes 11 años y en los últimos 5 minutos del final de la lección, tu profesor de matemáticas decide hacer la clase más divertida, introduciendo un juego. El explica que irá señalando a cada compañero de clase en orden y preguntándole el siguiente número de la secuencia, comenzando por el número uno. La parte divertida es que, si el número es divisible por 3, tienes que decir “Fizz” en vez del número. Y si es divisible por 5, debes decir “Buzz”. Las matemáticas no es tu fuerte y tienes miedo de que te llegue el turno y no sepas qué decir. Así que, con el fin de evitar la vergüenza delante de toda la clase, tienes que obtener la lista completa impresa para saber qué decir. Tu clase tiene unos 33 alumnos y se pueden llegar a hacer unas tres rondas antes de que suene el timbre para el recreo. La siguiente clase de matemáticas es el jueves. ¡Codifícalo! :)

Enunciado:

Escribe un programa que imprima los números del 1 al 100, pero aplicando las siguientes normas:

  • Devuelve Fizz si el número es divisible por 3.
  • Devuelve Buzz si el número es divisible por 5.
  • Devuelve FizzBuzz si el número es divisible por 3 y por 5.

Salida de ejemplo: 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz … etc hasta el 100

Fase 2 – Nuevos requerimientos:

  • Un número es Fizz si es divisible por 3 o si incluye un 3 en el número
  • Un número es Buzz si es divisible por 5 o si incluye un 5 en el número

El proceso para aplicar TDD es el siguiente:

  1. Definimos la prueba unitaria.
  2. Agregar código mínimo necesario para pasar la prueba.
  3. Refactorizar el código.
  • Dentro de la definición de la prueba aplicamos la regla AAA (Arrange, Act, Assert) (Prepara, Actuación, Afirmar)
  • Para agregar el código mínimo usaremos Luz roja y Luz verde, Cuando nos de Luz verde la prueba pasamos a refactorizar.
  • Para refactorizar los haremos aplicando los principios SOLID.

La Kata la he realizado con Visual studio 2010. Tengo una solución con dos proyectos uno de consola y otro de test

Ciclo 1 “El test devuelve el valor que se le pase por parámetro”

Primero se escribe la prueba


        /// <summary>
        ///Devuelve el valor que le pasemos
        ///</summary>
	[TestMethod()]
        [DeploymentItem("FizzBuzz.exe")]
        public void DevuleveElValorPasado()
        {
            //Prepara
            int numero = 1;
            //Actua
            string result = FizzBuzz.FizzBuzz.ValorAPintar(numero);
            //Afirma
            Assert.AreEqual(numero.ToString(), result);
        }

Se compila y da error (Luz Roja) la clase FizzBuzz y el método ValorAPintar no están declarados.


    class FizzBuzz
    {
        internal static string ValorAPintar(int numero)
        {
            return numero.ToString();
        }
    }

La prueba más sencilla devuelve el número que le pasamos

Ciclo 2 “Devuelve Fizz si el número es divisible por 3″

Primero la prueba


        /// <summary>
        ///Devuelve Fizz si es divisible entre 3
        ///</summary>
        [TestMethod()]
        [DeploymentItem("FizzBuzz.exe")]
        public void DevuleveFizzDivisible3()
        {
            //Prepara
            string Fizz = "Fizz";
            int numero = 3;
            //Actua
            string result = FizzBuzz.FizzBuzz.ValorAPintar(numero);
            //Afirma
            Assert.AreEqual(result, Fizz);
        }

Ejecutamos la prueba y tenemos luz roja ya que 3 no es Fizz

Modificamos el código para que pase la prueba. (Ha de pasar esta prueba y las pruebas anteriores).


class FizzBuzz
    {
        internal static string ValorAPintar(int numero)
        {
            if ((numero % 3) == 0)
            {
                return "Fizz";
            }
            return numero.ToString();
        }
    }

Ejecutamos la prueba y luz verde

Refactorizamos el código

Sacamos el texto “Fizz” a una constante de la clase FizzBuzz
Sacamos a un método privado Si el número es divisible entre 3


    class FizzBuzz
    {
        private const string FIZZ = "Fizz";

        internal static string ValorAPintar(int numero)
        {
            if (EsDivisible(numero))
            {
                return FIZZ;
            }
            return numero.ToString();
        }

        private static bool EsDivisible(int numero)
        {
            return (numero % 3) == 0;
        }
    }

Volvemos a probar que tenemos luz verde por si nos hemos equivocado al refactorizar

Podemos seguir

Ciclo 3 “Devuelve Buzz si el número es divisible por 5″

Primero escribimos la prueba


        /// <summary>
        ///Devuelve Buzz si es divisible entre 5
        ///</summary>
        [TestMethod()]
        [DeploymentItem("FizzBuzz.exe")]
        public void DevuleveBuzzDivisible5()
        {
            //Prepara
            string Buzz = "Buzz";
            int numero = 5;
            //Actua
            string result = FizzBuzz.FizzBuzz.ValorAPintar(numero);
            //Afirma
            Assert.AreEqual(Buzz, result);
        }

Es igual que para el 3 pero con 5

Ejecutamos la prueba y tenemos luz roja ya Buzz no es 5

Modificamos el código


        internal static string ValorAPintar(int numero)
        {
            if ((numero % 5) == 0)
            {
                return "Buzz";
            }

            if (EsDivisible(numero))
            {
                return FIZZ;
            }
            return numero.ToString();
        }

Luz verde prueba pasada

Vamos a refactorizar

Pasamos la cadena Buzz a una constante al igual que con Bizz
Modificamos el método privado EsDibisible para que nos diga si es divisible por un valor que le pasamos


    class FizzBuzz
    {
        private const string FIZZ = "Fizz";
        private const string BUZZ = "Buzz";

        internal static string ValorAPintar(int numero)
        {
            if (EsDivisible(numero, 5))
            {
                return BUZZ;
            }

            if (EsDivisible(numero, 3))
            {
                return FIZZ;
            }

            return numero.ToString();
        }

        private static bool EsDivisible(int numero, int valor)
        {
            return (numero % valor) == 0;
        }

Comprobamos que está bien la refactorización y seguimos con otro test

Ciclo 4 “Devuelve FizzBuzz si el número es divisible por 3 y por 5″

Primero el test


        /// <summary>
        ///Devuelve FizzBuzz si es divisible entre 3 y 5
        ///</summary>
        [TestMethod()]
        [DeploymentItem("FizzBuzz.exe")]
        public void DevuleveFizzBuzzDivisible35()
        {
            //Prepara
            string ValorFizzBuzz = "FizzBuzz";
            int numero = 15;

            //Actua
            string result = FizzBuzz.FizzBuzz.ValorAPintar(numero);

            //Afirma
            Assert.AreEqual(ValorFizzBuzz, result);
        }

Lanzamos la prueba y tenemos luz roja

Modificamos el código para que se cumpla la prueba


	internal static string ValorAPintar(int numero)
        {
            if (EsDivisible(numero, 5) && EsDivisible(numero, 3))
            {
                return "FizzBuzz";
            }

            if (EsDivisible(numero, 5))
            {
                return BUZZ;
            }

            if (EsDivisible(numero, 3))
            {
                return FIZZ;
            }

            return numero.ToString();
        }

Pasamos a las pruebas

Refactorizamos
Pasamos la cadena FizzBuzz a la suma de las dos constantes
Los enteros 3 y 5 los pasamos a constantes


    class FizzBuzz
    {
        private const string FIZZ = "Fizz";
        private const string BUZZ = "Buzz";
        private const int TRES = 3;
        private const int CINCO = 5;

        internal static string ValorAPintar(int numero)
        {

            if (EsDivisible(numero, TRES) && EsDivisible(numero, CINCO))
            {
                return FIZZ + BUZZ;
            }

            if (EsDivisible(numero, CINCO))
            {
                return BUZZ;
            }

            if (EsDivisible(numero, TRES))
            {
                return FIZZ;
            }

            return numero.ToString();
        }

        private static bool EsDivisible(int numero, int valor)
        {
            return (numero % valor) == 0;
        }

    }

Fase 2

Ciclo 5 “Un número es Fizz si es divisible por 3 o si incluye un 3 en el número”

Primero la prueba


        /// <summary>
        ///Devuelve Fizz si contiene 3
        ///</summary>
        [TestMethod()]
        [DeploymentItem("FizzBuzz.exe")]
        public void DevuleveFizzContiene3()
        {
            //Prepara
            string Fizz = "Fizz";
            int numero = 13;
            //Actua
            string result = FizzBuzz.FizzBuzz.ValorAPintar(numero);
            //Afirma
            Assert.AreEqual(Fizz, result);
        }

Tenemos luz roja

Modificamos el código


        internal static string ValorAPintar(int numero)
        {
            if (numero.ToString().Contains('3'))
            {
                return FIZZ;
            }

            if (EsDivisible(numero, TRES) && EsDivisible(numero, CINCO))
            {
                return FIZZ + BUZZ;
            }

            if (EsDivisible(numero, CINCO))
            {
                return BUZZ;
            }

            if (EsDivisible(numero, TRES))
            {
                return FIZZ;
            }

            return numero.ToString();
        }

Tenemos luz verde

Refactorizamos
Como tenemos la constante TRES quitamos el valor de la función contains

Ciclo 6 “Un número es Buzz si es divisible por 5 o si incluye un 5 en el número”

Primero la prueba


         /// <summary>
        ///Devuelve Buzz si contiene 5
        ///</summary>
        [TestMethod()]
        [DeploymentItem("FizzBuzz.exe")]
        public void DevuleveFizzContiene3()
        {
            //Prepara
            string Buzz = "Buzz";
            int numero = 52;
            //Actua
            string result = FizzBuzz.FizzBuzz.ValorAPintar(numero);
            //Afirma
            Assert.AreEqual(Buzz, result);
        }

Tenemos luz roja

modificamos el código


        internal static string ValorAPintar(int numero)
        {
            if (numero.ToString().Contains(Char.Parse(CINCO.ToString())))
            {
                return BUZZ;
            }

            if (numero.ToString().Contains(Char.Parse(TRES.ToString())))
            {
                return FIZZ;
            }

            if (EsDivisible(numero, TRES) && EsDivisible(numero, CINCO))
            {
                return FIZZ + BUZZ;
            }

            if (EsDivisible(numero, CINCO))
            {
                return BUZZ;
            }

            if (EsDivisible(numero, TRES))
            {
                return FIZZ;
            }

            return numero.ToString();
        }

Tenemos luz verde

Refactorizamos el código

Quitamos los returns duplicados
Extraemos el método contiene


        internal static string ValorAPintar(int numero)
        {

            if (EsDivisible(numero, TRES) && EsDivisible(numero, CINCO))
            {
                return FIZZ + BUZZ;
            }

            if (EsDivisible(numero, CINCO) || Contiene(numero, CINCO))
            {
                return BUZZ;
            }

            if (EsDivisible(numero, TRES) || Contiene(numero, TRES))
            {
                return FIZZ;
            }

            return numero.ToString();
        }

        private static bool Contiene(int numero, int valor)
        {
            return numero.ToString().Contains(Char.Parse(valor.ToString()));
        }

        private static bool EsDivisible(int numero, int valor)
        {
            return (numero % valor) == 0;
        }

Ciclo 7 “Valores especiales”

Tenemos dos valores especiales en los que no sabemos qué especificar por no indicarse en el enunciado estos valores son 53 y 35 si realizamos un test para estos valores en ambos casos nos devuelve “Buzz” porque en las sentencias if se evalúa antes el


if (EsDivisible(numero, CINCO) || Contiene(numero, CINCO))

Como no se especifica en el enunciado yo he decidido que cuando contenga un 3 y un 5 devuelva “FizzBuzz”

Por lo que primero la prueba.


        /// <summary>
        ///Valores especiales 53 35
        ///</summary>
        [TestMethod()]
        [DeploymentItem("FizzBuzz.exe")]
        public void ValoresEspeciales()
        {
            //Prepara
            string ValorFizzBuzz = "FizzBuzz";

            int numero1 = 53;
            int numero2 = 35;

            //Actua
            string result1 = FizzBuzz.FizzBuzz.ValorAPintar(numero1);
            string result2 = FizzBuzz.FizzBuzz.ValorAPintar(numero2);

            //Afirma
            Assert.AreEqual(ValorFizzBuzz, result1);
            Assert.AreEqual(ValorFizzBuzz, result2);
        }

Lanzamos la prueba y tenemos luz roja

Modificamos el código para obtener luz verde


     internal static string ValorAPintar(int numero)
        {

            if ((EsDivisible(numero, TRES) && EsDivisible(numero, CINCO)) ||
                (Contiene(numero, TRES) && Contiene(numero, CINCO)))
            {
                return FIZZ + BUZZ;
            }

            if (EsDivisible(numero, CINCO) || Contiene(numero, CINCO))
            {
                return BUZZ;
            }

            if (EsDivisible(numero, TRES) || Contiene(numero, TRES))
            {
                return FIZZ;
            }

            return numero.ToString();
        }

Lanzamos la prueba de nuevo

Refactorizamos
No tenemos nada que refactorizar

Ciclo 8 “Modificamos el programa principal”

Agregamos el bucle for a la clase main del program.cs


 class Program
 {
 static void Main(string[] args)
 {
 for (int i = 1; i <= 100; i++)
 {
 Console.WriteLine(FizzBuzz.ValorAPintar(i));
 }
 }

Cuando lanzamos el programa este es el resultado

1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
Fizz
14
FizzBuzz
16
17
Fizz
19
Buzz
Fizz
22
Fizz
Fizz
Buzz
26
Fizz
28
29
FizzBuzz
Fizz
Fizz
Fizz
Fizz
FizzBuzz
Fizz
Fizz
Fizz
Fizz
Buzz
41
Fizz
Fizz
44
FizzBuzz
46
47
Fizz
49
Buzz
Buzz
Buzz
FizzBuzz
Buzz
Buzz
Buzz
Buzz
Buzz
Buzz
FizzBuzz
61
62
Fizz
64
Buzz
Fizz
67
68
Fizz
Buzz
71
Fizz
Fizz
74
FizzBuzz
76
77
Fizz
79
Buzz
Fizz
82
Fizz
Fizz
Buzz
86
Fizz
88
89
FizzBuzz
91
92
Fizz
94
Buzz
Fizz
97
98
Fizz
Buzz

Esta solución es la que yo he encontrado, seguro que hay otras.

Para quien quiera saber más sobre TDD le recomiendo que lea el libro de Carlos Ble que se encuentra en

La fuente de esta Kata me la paso un compañero de hay podréis encontrar más katas y soluciones.

Cualquier critica o consulta será bien recibida

Esto es todo.

Membership en .net con WCF

Algo que utilizo bastante a  menudo en mis aplicaciones es la gestión de usuarios de .net

Normalmente trabajo con una arquitectura de capas donde la parte web (web Forms o MVC) conecta con un web service o SVC (Capa de negocio) para gestionar datos de la bbdd.

Os dejo este post con los conceptos básicos para montar la gestión de usuarios de .net en nuestras aplicaciones.

1.- Configurar SQL Server
Se ejecuta aspnet_regsql.exe

Accedemos al configurador de SqlServer don de le indicaremos en que servidor y bbdd se va a crear la bbdd de nuestro sistema de usuarios

Terminada la configuración nos en la bbdd que hayamos elegido las tablas que utiliza .net para la gestión de usuarios.

La tabla aspnet_ProfileExtended la veremos mas adelante ya que no se crea de manera automática.

2.- Configurar nuestra aplicación web.

Voy a configurar un servicio en WCF para la gestión de los usuarios. Hay que crear un nuevo proyecto WCF como Aplicación de servicios WCF.

 Renombramos la Interface a IUsuarios y el svc a Usuarios.svc

La parte dura de la configuración esta en el web config que se divide en tres partes, Membership, profile, Role

 - Membership: Configura la conexión con el proveedor de datos.

 Se indica el proveedor de datos que vamos a usar, por defecto el que instalado en el apartado anterior con el asistente de SQL Server.

 El resto de parámetros son para configurar los requisitos de usuarios, longitud del password, codificación del pasword,  número de intentos de login para que se bloquee el usuario, pregunta de seguridad…

 Buscar en google cada parámetro para que sirva aunque son muy intuitivos.

 


<membership defaultProvider="AspNetSqlMembershipProvider">
    <providers>
        <clear />
    	<add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" 
	connectionStringName="UsuariosPortal" 
	enablePasswordRetrieval="true" 
	enablePasswordReset="true" 
	requiresQuestionAndAnswer="false" 
	requiresUniqueEmail="true" 
	maxInvalidPasswordAttempts="5" 
	minRequiredPasswordLength="6" 
	minRequiredNonalphanumericCharacters="0" 
	passwordAttemptWindow="30" 
	applicationName="/" 
	passwordFormat="Clear" />
    </providers>
</membership>

 - Profile: Perfil añadido al proveedor de datos.

 Si tenemos datos añadidos a cada usuario, como en este caso es el nombre, apellidos y teléfono y los queremos guardar en una tabla en bbdd.

 Si es una tabla de bbdd indicárselo en el parámetro table.

   


<profile defaultProvider=”AspNetSqlMembershipProvider” enabled=”true”>

	 <providers>
 		<clear />
		<add name=”SqlTableProfileProvider” type=”System.Web.Profile.SqlTableProfileProvider” connectionStringName=”UsuariosPortal” applicationName=”/” table=”aspnet_ProfileExtended” />
	</providers>

	<properties>
		<add name=”UserID” provider=”SqlTableProfileProvider” type=”string” defaultValue=”fasle” customProviderData=”UserID;nvarchar” />
		<add name=”Nombre” provider=”SqlTableProfileProvider” type=”string” defaultValue=”false” customProviderData=”Nombre;nvarchar” />
		<add name=”Apellidos” provider=”SqlTableProfileProvider” type=”string” defaultValue=”false” customProviderData=”Apellidos;nvarchar” />
		<add name=”Telefono” provider=”SqlTableProfileProvider” type=”string” defaultValue=”false” customProviderData=”Telefono;nvarchar” />
	</properties>

 </profile>

La tabla aspnet_ProfileExtended hay que crearla en la bbdd manualmente.

 - Role: Roles de los usuarios.

 Le indicamos donde tenemos el proveedor de roles que es el mismo que de usuarios.


 <roleManager enabled="true">
 
     <providers>
        <clear />
        <add connectionStringName="UsuariosPortal" 
		applicationName="/" 
		name="AspNetSqlRoleProvider" 
		type="System.Web.Security.SqlRoleProvider" />
      </providers>
    </roleManager>

 En la sección de connectionString del web.config añadimos al cadena de conexión a la bbdd donde tenemos el proveedor.

 Con esto quedaría configurada nuestra aplicación para conectar con nuestro proveedor de usuario.

3.- Gestionar usuario desde la aplicación.

 Hay que enlazar nuestro servicio con la bbdd, para realizar esto lo más sencillo es utilizar “Entity Data Framework”. Añadimos un fichero .edmx con el modelo de datos que vamos a utilizar, que además será, exportable a la capa web y nos ahorrará mucho tiempo y código.

Con el modelo de nuestro perfil de usuario encapsulado en una clase, ya lo tenemos todo para empezar a programar.

4.- Operaciones para gestionar usuarios.

Definimos las operaciones en la Interface.



[ServiceContract]
    public interface IUsuarios
    {

        [OperationContract]
        Boolean Login(string UserName, string Password);

        [OperationContract]
        void OlvidoPassword(string email);

        [OperationContract]
        bool CrearUsuario(Usuario NuevoUsuario);

        [OperationContract]
        bool ModificarUsuario(Usuario UsuarioModificador);

        [OperationContract]
        List<Usuario> ObtenerUsuarios(Usuario UsuarioAdministrador);

        [OperationContract]
        Usuario ObtenerUsuarioPorUserName(string UserName);

        [OperationContract]
        Boolean DesbloquearUsuario(string UserName);

        [OperationContract]
        string[] ObtenerRoles();

        [OperationContract]
        void ActivacionCaducada(string UserName);

        [OperationContract]
        bool CambiarPassword(string UserName, string OldPassword, string NewPassword);


    }

Y los datos de usuario que queremos que exporte nuestro servicio


    [DataContract]
    public partial class Usuario
    {
        [DataMember]
        public string email { get; set; }
        [DataMember]
        public string UserName { get; set; }
        [DataMember]
        public string Password { get; set; }
        [DataMember]
        public string Nombre { get; set; }
        [DataMember]
        public string Apellidos { get; set; }
        [DataMember]
        public string Telefono { get; set; }
        [DataMember]
        public string Rol { get; set; }
        [DataMember]
        public bool Activo { get; set; }
        [DataMember]
        public bool Bloqueado{ get; set; }       
    }

Para implementar estas operaciones necesitaremos cuatro clases mas una

Validaciones.cs: Realiza las validaciones de campos que recibimos.

- Encriptación.cs: La utilizaremos para encriptar datos.

- Mail.cs: Se utilizará para enviar los mails necesarios.

- Usuario.cs: Clase con los métodos de usuario

 5.- Clase Usuario

 La clase usuario es una clase partitial que se compone del modelo de la bbdd y los métodos. Estos métodos solo son accesibles desde el mismo namespace.


    public partial class Usuario
    {

        internal bool CrearUsuario(Usuario NuevoUsuario, object UserId)

        internal bool ModificarUsuario(Usuario UsuarioModificado)

        internal List<Usuario> ObtenerUsuarios(Usuario UsuarioAdministrador)

        internal Usuario ObtenerUsuario(string UserName)

        internal void EnviarActivacion(string email, string UserName)

        private string GenerarUrlActivacion(string UserName)

    }

- Crear usuario


   internal bool CrearUsuario(Usuario NuevoUsuario, object UserId)
        {
            this.Apellidos = NuevoUsuario.Apellidos;
            this.Nombre = NuevoUsuario.Nombre;
            this.Entidad = NuevoUsuario.Entidad;

            Roles.AddUserToRole(NuevoUsuario.UserName, NuevoUsuario.Rol);

            PortalExperianEntities context = new PortalExperianEntities();
            aspnet_ProfileExtended profile = new aspnet_ProfileExtended();

            profile.Apellidos = Apellidos;
            profile.Nombre = Nombre;
            profile.UserId = (Guid)UserId;
            profile.Telefono = Telefono;

            context.AddToaspnet_ProfileExtended(profile);
            context.SaveChanges();

            EnviarActivacion(NuevoUsuario.email, NuevoUsuario.UserName);

            return true;
        }

 - Modificar Usuario


 internal bool ModificarUsuario(Usuario UsuarioModificado)
       {

            PortalExperianEntities context = new PortalExperianEntities();
            MembershipUser user = Membership.GetUser(UsuarioModificado.UserName);
            aspnet_ProfileExtended profile = context.aspnet_ProfileExtended.Single(u =&gt; u.UserId == (Guid)user.ProviderUserKey);

            profile.Apellidos = UsuarioModificado.Apellidos;
            profile.Nombre = UsuarioModificado.Nombre;
            profile.Telefono = UsuarioModificado.Telefono;
            context.SaveChanges();

            foreach(string role in Roles.GetRolesForUser(UsuarioModificado.UserName)){
                Roles.RemoveUserFromRole(UsuarioModificado.UserName, role);
            }

            Roles.AddUserToRole(UsuarioModificado.UserName, UsuarioModificado.Rol);

            user.IsApproved = UsuarioModificado.Activo;

            user.Email = UsuarioModificado.email;

            Membership.UpdateUser(user);

            return true;
        }

 - Obtener usuarios


  internal List<Usuario> ObtenerUsuarios(Usuario UsuarioAdministrador)
       {
            List<Usuario> usuarios = new List<Usuario>();

            PortalExperianEntities context = new PortalExperianEntities();

            var query = from u in context.aspnet_ProfileExtended
                        select u;

            List<aspnet_ProfileExtended> usuarioExtended = query.ToList<aspnet_ProfileExtended>();

            foreach (aspnet_ProfileExtended user in usuarioExtended)
            {
                Usuario usuario = new Usuario();

                usuario.Apellidos = user.Apellidos;
                usuario.Nombre = user.Nombre;
                usuario.Telefono = user.Telefono;

                MembershipUser userMembership = Membership.GetUser(user.UserId);
                usuario.Rol = Roles.GetRolesForUser(userMembership.UserName)[0].ToString();

                usuario.UserName = userMembership.UserName;

                if (userMembership.IsLockedOut)
                {
                    usuario.Password = "";
                }
                else
                {
                    usuario.Password = userMembership.GetPassword();
                }
                usuario.email = userMembership.Email;
                usuario.Bloqueado = userMembership.IsLockedOut;
                usuario.Activo = userMembership.IsApproved;

                usuarios.Add(usuario);

            }

            return usuarios;
        }

 - Obtener Usuario


  internal Usuario ObtenerUsuario(string UserName)
       {
            PortalExperianEntities context = new PortalExperianEntities();
            MembershipUser userMembership = Membership.GetUser(UserName);
            aspnet_ProfileExtended profile = context.aspnet_ProfileExtended.Single(u =&gt; u.UserId == (Guid)userMembership.ProviderUserKey);

            Usuario usuario = new Usuario();

            usuario.Apellidos = profile.Apellidos;
            usuario.Nombre = profile.Nombre;
            usuario.Telefono = profile.Telefono;
            usuario.Bloqueado = userMembership.IsLockedOut;

            usuario.Rol = Roles.GetRolesForUser(userMembership.UserName)[0].ToString();

            usuario.UserName = userMembership.UserName;
            usuario.Activo = userMembership.IsApproved;

            if (userMembership.IsLockedOut)
            {
                usuario.Password = "";
            }
            else
            {
                usuario.Password = userMembership.GetPassword();
            }

            usuario.email = userMembership.Email;

            return usuario;
        }

 - Enviar Activación


 internal void EnviarActivacion(string email, string UserName)
        {

            mail.MandarMail(email, mail.MailActivacion(UserName, GenerarUrlActivacion(UserName)), "Activacion del servicio");
        }

- Generar Activacion

 Este es el único método privado de la clase, u lo que hace es encriptar los datos necesarios para activar al usuario.

 En el webconfig tenemos que guardar la dirección de la página de activación.


 private string GenerarUrlActivacion(string UserName)
       {
            string url = ConfigurationManager.AppSettings["WebActivacion"].ToString();
            url += "?UserName=" + Encriptacion.Encriptar(UserName, Encriptacion.Semilla) + "&amp;fecha=" + Encriptacion.Encriptar(DateTime.Now.ToString("yyyyMMdd"), Encriptacion.Semilla);
            return url;
        }

 Las clases de encriptación, validación y mail son muy comunes y cada uno que las desarrolle como le venga bien.

6.- Código de las Operaciones

 Según la lógica de negocio que utilicemos nos harán falta mas operaciones, o el código de cada operación será diferente.

-         Login: Permite el acceso a la aplicación.

-         OlvidoPassword: Manda un mail para recordar el password al usuario.

-         CrearUsuario: Crea un usuario nuevo

-         Modificar usuario: modifica los datos de un usuario.

-         ObtenerUsuarios: Obtiene todos los usuarios de la bbdd

-         ObtenerUsuario: Obtiene el detalle de un usuario.

-         ObtenerRoles: Devuelve todos los roles.

-         ActivacionCaducada: manda un mail si el link de activación a caducado.

-         CambiarPassword: Modifica password.

 - Login

  public Usuario Login(string UserName, string Password)
        {
            try
            {
                //Validas datos
                Validaciones.ValidarDatos(UserName, Password);

                if (Membership.ValidateUser(UserName, Password))
                {
                    Usuario usuario = new Usuario();
                    usuario = usuario.ObtenerUsuario(UserName);
                    if (!usuario.Activo)
                    {
                        throw new Exception("Usuario desactivado");
                    }
                    return usuario;
                }

                MembershipUser user = Membership.GetUser(UserName);

                if ((user != null) &amp;&amp; (user.IsLockedOut))
                {
                    mail.MandarMail(user.Email, mail.MailUsuarioBloqueado(user.UserName), "Usuario Bloqueado");
                    throw new Exception("Usuario bloqueado");
                }

                throw new Exception("Usuario o password no es correcto");
            }
            catch (Exception ex)
            {
                throw ex;
            }
         }

- Crear Usuario

 El usuario se crea desactivado y se envía un mail con una url para activar.


public bool CrearUsuario(Usuario NuevoUsuario)
        {
            try
            {
                Validaciones.ValidarUsuario(NuevoUsuario);
                MembershipCreateStatus status;
                MembershipUser user = Membership.CreateUser(NuevoUsuario.UserName, NuevoUsuario.Password, NuevoUsuario.email, null, null, false, out status);
                if (status == MembershipCreateStatus.Success)
                {
                    try
                    {
                        Usuario usuario = new Usuario();

                        usuario.UserName = NuevoUsuario.UserName;
                        usuario.Password = NuevoUsuario.Password;
                        usuario.email = NuevoUsuario.email;
                        usuario.Telefono = NuevoUsuario.Telefono;

                        return usuario.CrearUsuario(NuevoUsuario, user.ProviderUserKey);
                    }
                    catch (Exception ex)
                    {
                        Membership.DeleteUser(NuevoUsuario.UserName);
                        throw new Exception("No es posible crear usuario " + ex.Message);
                    }
                }
                return false;
            }
            catch (MembershipCreateUserException memberEx)
            {
                throw memberEx;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

- Olvido Password


public void OlvidoPassword(string email)
        {
            try
            {
                MembershipUser user = Membership.GetUser(Membership.GetUserNameByEmail(email));
                mail.MandarMail(user.Email, mail.MailOlvidoPassword(ObtenerUsuarioPorUserName(user.UserName)), "Recordatorio Portal Experian");                    
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

 - Modificar usuario

 public bool ModificarUsuario(Usuario UsuarioModificado)
        {
            try
            {
                Validaciones.ValidarUsuario(UsuarioModificado);
                Usuario user = new Usuario();
                return user.ModificarUsuario(UsuarioModificado);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

- Obtener usuario.

   public Usuario ObtenerUsuarioPorUserName(string UserName)
        {
            try
            {
                Validaciones.ValidarUserName(UserName);
                Usuario usuario = new Usuario();
                return usuario.ObtenerUsuario(UserName);
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

 - Obtener usuarios

  public List<Usuario>; ObtenerUsuarios(Usuario UsuarioAdministrador)
        {
            try
            {
                Validaciones.ValidarUsuario(UsuarioAdministrador);
                Usuario usuario = new Usuario();
                return usuario.ObtenerUsuarios(UsuarioAdministrador);
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

 - Obtener Roles

public string[] ObtenerRoles()
        {
            try
            {
                return Roles.GetAllRoles();
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

 - Desbloquear usuario


public Boolean DesbloquearUsuario(string UserName)
        {
            try
            {
                MembershipUser user = Membership.GetUser(UserName);
                return user.UnlockUser();

            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

 - Activación Caducada

public void ActivacionCaducada(string UserName)
        {
            Usuario user = new Usuario();
            user.EnviarActivacion(Membership.GetUser(UserName).Email, UserName);
        }

 - Cambiar Password


public bool CambiarPassword(string UserName, string OldPassword, string NewPassword)
        {
            MembershipUser user = Membership.GetUser(UserName);
            return user.ChangePassword(OldPassword, NewPassword);
        }

7.- Configuración como web service en la web

 Para configurar la web que llama al servicio hay que indicarle en el web config que se va a utilizar como un webservice en el apartado binding con los siguientes parámetros


 <wsHttpBinding>
         <binding name="WSHttpBinding_IUsuarios" closeTimeout="00:01:00"
          openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
          bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
          maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
          textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
            maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <reliableSession ordered="true" inactivityTimeout="00:10:00"
            enabled="false" />
          <security mode="Message">
            <transport clientCredentialType="Windows" proxyCredentialType="None"
              realm="" />
            <message clientCredentialType="Windows" negotiateServiceCredential="true"
              algorithmSuite="Default" />
          </security>
        </binding>
      </wsHttpBinding>

El apartado client le indicamos la url y los parámetros del cliente que usuremos en la web

 <client>      
       <endpoint address="http://localhost:1780/Usuarios.svc" binding="wsHttpBinding"
        bindingConfiguration="WSHttpBinding_IUsuarios" contract="ServicioUsuarios.IUsuarios"
        name="WSHttpBinding_IUsuarios">        
      </endpoint>
    </client>

 Para mas profanidad en la configuración de WCF ya hay mucha información en google,  sino esto se hace muy extenso.

8.- Web de usuarios

 El diagrama que yo he utilizado es

He creado dos roles uno de Administrador y otro de Usuario, los roles los podemos crear por código en el global.asax del servicio o con la aplicación que nos ofrece visual Studio de configuración web.

 

En el apartado Seguridad podemos gestionar los roles y los usuarios.

 No voy a entrar en las llamadas al servicio, se utiliza como cualquier web service ni en el desarrollo de la web.

9.- Conclusiones

Insertar la gestión de usuarios en una aplicación web es algo rápido y sencillo. Espero que os sea util y cualquier consulta podeis escribirme.

Esto es todo.

Que la fuerza os acompañe