Sector 7G

Sector7G Live Search

About Me

About Me

Windows Live Messenger Presence Gadget

My Photo Albums

My Photo Web Albums

Logos

  • Sector7G
  • ilitia Technologies


Kartones.Net MVF Winner

May 2007 - Posts

Refactorización: Reemplazar un Condicional por Polimorfismo

La aplicación de este patrón de refactorización será posible en los casos donde una sentencia condicional realice diferentes acciones dependiendo del tipo de objeto instanciado.

En esta pieza de código podemos ver que dependiendo del tipo de Consultor que se instancie el salario será calculado de una forma diferente. Uno de los primeros pasos que habrá que dar para refactorizar esta clase será crear una jerarquía de subclases.

Código 

 

Para crear la jerarquía de subclases se puede usar uno de estos dos patrones: Reemplazar código de tipo por subclases o Reemplazar código de tipo con Estado/Estrategia (ya hablaremos de estos dos patrones en otro artículo). Después de aplicar uno de los dos patrones el diagrama más o menos debería quedar así:

 

Diagrama de clases

 

El código resultante sería el siguiente (las clases Architect y ProjectManager serían igual que la de Developer):

Código

El siguiente paso será mover el método payAmount a la clase ConsultantType haciendo uso del patrón de refactorización Mover Método. Hacemos este movimiento porque la clase ConsultantType es la clase padre, de la cual heredan Developer, Architect y ProjectManager. Además, como son necesarios los datos del Consultor debemos pasar un objeto Consultant como argumento del método. El resultado es el siguiente:

 

Código

 

Una vez compilado y verificado que los test siguen en verde ya se puede pasar al último paso del proceso de refactorización. Sólo queda convertir el método payAmount de la clase ConsultantType a abstract e implementarlo en el resto de subclases: Developer, Architect y ProjectManager. Cuando hayamos acabado todo el proceso el código debería quedar de la siguiente manera:

 

Código

 

Para finalizar debemos compilar el código y verificar que todas las pruebas sigan siendo verdes.

Anonymous Types en C# 3.0 en Orcas

Entre una de las muchas características que C# 3.0 incorpora se encuentran los tipos Anónimos.  Los tipos anónimos consisten en definiciones en línea (inline) de tipos, sin necesidad de especificar un nombre de tipo. Sobre todo, el uso de los tipos anónimos es recomendado en escenarios donde se use LINQ.

Cuando se pretende instanciar una variable a través de un tipo anónimo es necesario aplicar otro de las nuevas características que incorpora el lenguaje, la inicialización de objetos y  declarar la variable haciendo uso de la palabra var.

Anonymous Types

Cuando el compilador se encuentra un declaración de este tipo lo que hace es crear un objeto, una entidad, a partir de los campos usados para inicialízalo. El compilador genera un nombre para esta clase, que hereda directamente de Object, y va creando métodos get / set en función de cómo se haya inicializado el tipo. Evidentemente, cada método inferirá el tipo con el que ha sido inicializado, así que Id será de tipo int y Name de tipo string.

Anonymous Types

El resultado de compilar las líneas de código anterior sería el siguiente:

Anonymous Types

En el caso de que dos tipos anónimos sean inicializados con las mismas propiedades y del mismo tipo; el compilador solamente creará una clase y las dos instancias serán del mismo tipo, evitando código repetido.

Anonymous Types

Como se puede ver en el código IL del ejemplo, efectivamente usa el mismo tipo para las dos instancias.

Anonymous Types

Posted: May 26 2007, 11:20 AM by Pedroafa | with no comments
Filed under: , , ,
Versioning a Service. Because Change Happens!

A lo largo del ciclo de vida de un servicio este puede ir cambiando, ya sea porque se ha producido un cambio en las reglas de negocio, porque la información que se necesita ha cambiado o por cualquier otro motivo. Cada cambio dará como resultado una nueva versión del servicio.

Hay dos tipos de cambios. Los cambios que producen una rotura en el contrato del servicio, “Breaking changes”. Este tipo de cambios afectarán al funcionamiento de los clientes ya que se produce una alteración en los mensajes enviados. Los cambios que no producen ninguna alteración en el funcionamiento de los clientes se denominan, “Nonbreaking changes”.

Posibles cambios que se podrán dar en el contrato de un servicio:

·         Añadir una nueva operación: No se produce ninguna rotura en el contrato. Simplemente las aplicaciones cliente no tendrán acceso al nuevo método.

·         Eliminar una operación: Produce una rotura en el contrato. Las aplicaciones que usen esta operación se verán afectadas, las que no la usen no se verán afectadas.

·         Cambiar el nombre de una operación: Produce una rotura en el contrato. Las aplicaciones que usen esta operación se verán afectadas, las que no la usen no se verán afectadas.

·         Cambiar el nivel de protección de una operación: Produce una rotura en el contrato. Las aplicaciones clientes no podrán utilizar el método.

·         Añadir un parámetro a una operación: Produce una rotura en el contrato. El método no podrá ser utilizado por las aplicaciones cliente ya que los mensajes SOAP enviados por ellas serán diferentes de los mensajes esperados por el servicio.

·         Reordenar los parámetros de una operación: Produce una rotura en el contrato. El resultado de esta modificación no es fácilmente predecible ya que algunos clientes podrían seguir funcionando.

·         Eliminar un parámetro de una operación: Produce una rotura en el contrato. El resultado de esta modificación no es fácilmente predecible.

·         Cambiar el tipo de los parámetros o el tipo del valor devuelto: Produce una rotura en el contrato. Las aplicaciones clientes podrían seguir funcionando, pero hay un alto riesgo de que los mensajes SOAP se pierdan o sean mal interpretados.

·         Añadir un FaultContract a una operación: Produce una rotura en el contrato. El servicio podrá enviar excepciones que las aplicaciones clientes no sabrán interpretar.

·         Eliminar un FaultContract de una operación: No se produce ninguna rotura en el contrato. Las aplicaciones cliente seguirán funcionando sin ningún problema.

·         Cambiar el nombre o espacio de nombres en el ServiceContract: Produce una rotura en el contrato. Las aplicaciones clientes no podrán enviar mensajes al servicio.

En muchas ocasiones no habrá la posibilidad de actualizar y recompilar las aplicaciones clientes porque no siempre se tiene control sobre ellas; lo normal es no tener este control. Por este motivo, se debe tener siempre presente que el funcionamiento del servicio y el de las aplicaciones clientes debe ser autónomo, es decir, una modificación en el servicio no debe de afectar a las aplicaciones que ya hacen uso de él.

Para poder versionar un servicio lo que se deberá de hacer es crear diferentes versiones del contrato que define el servicio, es decir, diferentes versiones de la interfaz. Con un pequeño ejemplo se podrá ver más claro.

Actualmente el servicio CustomerService expone el siguiente contrato a sus clientes:

public interface ICustomer

{

    List<Customer> GetAllCustomers();

 

    Customer GetCustomerById(int id);

 

    int InsertCustomer(Customer c);

 

    int DeleteCustomer(int id);

 

    int UpdateCustomer(Customer c);

}

Tras varios meses el número de clientes ha crecido notablemente. Se ha decido que el método GetAllCustomers en vez de volver una lista con todos los clientes, devuelva una lista con aquellos clientes cuyo nombre coincida con el especificado por el usuario. Por todo ello, el método se debe actualizar a:

List<Customer> GetAllCustomers(string name);

Para ello se deberá crear una nueva interfaz con el nuevo método.

[ServiceContract(Namespace = "http://customer.com/14/05/2007”,Name = "CustomerService")]

public interface ICustomerV2

{

    [OperationContract(Name = "GetCustomersByName")]

    List<Customer> GetAllCustomers(string name);

}

Cómo se puede ver, además de crear una nueva interfaz también al ServiceContract de la interfaz se le ha añadido un nuevo Namespace. Incluyendo la fecha de creación en el Namespace se podrán controlar las diferentes versiones del servicio. Además,  en el OperationContract se le ha añadido la propiedad Name. SOAP no soporta la sobrecarga de métodos, por eso es considerado como una buena práctica incluir el nombre que el método tendrá en los mensajes SOAP.

Finalmente las dos interfaces deberían quedar así:

[ServiceContract(Namespace = "http://customer.com/10/03/2007", Name= "CustomerService")]

public interface ICustomer

{

    [OperationContract(Name = "GetAllCustomers")]

    List<Customer> GetAllCustomers();

 

    [OperationContract(Name = "GetCustomerById")]

    Customer GetCustomerById(int id);

 

    [OperationContract(Name = "InsertCustomer")]

    int InsertCustomer(Customer c);

 

    [OperationContract(Name = "DeleteCustomer")]

    int DeleteCustomer(int id);

 

    [OperationContract(Name = "UpdateCustomer")]

    int UpdateCustomer(Customer c);

}

 

[ServiceContract(Namespace = "http://customer.com/14/05/2007",Name = "CustomerService")]

public interface ICustomerV2

{

    [OperationContract(Name = "GetAllCustomers")]

    List<Customer> GetAllCustomers(string name);

}

Una vez definidas las interfaces solamente falta implementar el servicio y desarrollar la funcionalidad de cada método.

//TODO: Implementar todos los métodos

public class CustomerService: ICustomer,ICustomerV2

{

    public List<Customer> GetAllCustomers()

    {

        throw new NotImplementedException();

    }

 

    public Customer GetCustomerById(int id)

    {

        throw new NotImplementedException();

    }

 

    public int InsertCustomer(Customer c)

    {

        throw new NotImplementedException();

    }

 

    public int DeleteCustomer(int id)

    {

        throw new NotImplementedException();

    }

 

    public int UpdateCustomer(Customer c)

    {

        throw new NotImplementedException();

    }

 

    public List<Customer> GetAllCustomers(string name)

    {

        throw new NotImplementedException();

    }

}

Para finalizar, hay que añadir un endpoints para la nueva versión del servicio.

<system.serviceModel>

    <services>

        <service name="VersioningServices.CustomerService">

            <endpoint address="http://localhost:8000/CustomerService"

                      binding="basicHttpBinding"

                      contract="VersioningServices.ICustomer" />                

            <endpoint address="http://localhost:8000/CustomerService"

                      binding="basicHttpBinding"

                      contract="VersioningServices.ICustomerV2" />

        </service>

    </services>

</system.serviceModel>

Como se puede ver siguiendo esta estrategia se podrá minimizar el impacto que cualquier cambio puede producir en el servicio.

Posted: May 15 2007, 03:09 PM by Pedroafa | with no comments
Filed under: ,
Implicitly typed local variables en C# 3.0 con Orcas

Una de las características más controvertidas que introduce la nueva versión del lenguaje C# es la posibilidad de declarar variables locales implícitamente. Esta nueva característica rápidamente es asociada erróneamente con los tipos variant de Visual Basic o los tipos var de JavaScript, los cuales pueden almacenar cualquier tipo de objeto.

¿Pero cómo trabajan los tipos implícitos en C# 3.0?. Cuando un variable está siendo declarada con la palabra reservada var, el tipo de la variable es inferido por la expresión usada para inicializar dicha variable.

var i = 5; // tipo int

var s = "Hello"; // tipo string

var d = 1.0; // tipo double

var numbers = new int[] { 1, 2, 3 }; // tipo array de int

var customers = new Dictionary<int, Customer>(); // tipo Dictionary de int y Customer

El ejemplo anterior equivale a esto:

int i = 5;

string s = "Hello";

double d = 1.0;

int[] numbers = new int[] { 1, 2, 3 };

Dictionary<int, Customer> orders = new Dictionary<int, Customer>();

Cuando el tipo es inferido ya no puede ser cambiado. Si por ejemplo a la variable i, del ejemplo anterior, se le intenta asignar s se producirá un error de compilación con el siguiente mensaje: “Cannot implicitly convert type 'string' to 'int'”.

El uso de declaraciones implícitas está sujeto a una serie de limitaciones:

·         Los tipos implícitos deben ser inicializados.

 

var p; //Error: Implicitly typed locals must be initialized.

 

·         Los tipos implícitos no pueden ser inicializados a null.

 

var p = null; //Error: Cannot assign '<null>' to an implicitly typed local.

 

·         Los tipos implícitos no pueden tener múltiples declaraciones.

 

var p = 1, t = 2; // Error: Implicitly typed locals cannot have multiple declarators.

 

·         Los tipos implícitos deben ser inicializados con una expresión. El inicializador no puede ser un objeto o colección de inicializadores.

 

var p = { 1, 2, 3 };       // Error: Cannot assign array initializer to an implicitly typed local.

Posted: May 05 2007, 07:14 PM by Pedroafa | with 4 comment(s)
Filed under: , , ,
AddressAccessDeniedException

El otro día mientras estaba jugando con WCF a intentar a arrancar un servicio sobre una consola en Windows Vista se lanzó una excepción  AddressAccessDeniedException  con el siguiente mensaje de error:

HTTP could not register URL http://+:8090/WCFServiceTest/ProductsServices.svc/. Your process does not have access rights to this namespace (see http://go.microsoft.com/fwlink/?LinkId=70353 for details).

Este error es debido a la actuación del UAC de Windows Vista. Como somos usuarios estándar, aunque pertenezcamos al grupo de administradores, no tenemos privilegios suficientes para hacer que un servicio web escuche en una determinada dirección HTTP. Por este motivo, cuando se intenta ejecutar un servicio de WCF sobre una cuenta de usuario sin estos permisos se lanza una excepción AddressAccessDeniedException.

Hay tres soluciones que se pueden llevar a cabo:

1)      Ejecutar el Visual Studio 2005 con permisos de Administrador (botón derecho Run As Administrator).

2)      Desactivar el control UAC del sistema operativo, lo cual no recomiendo en absoluto.

3)      Ejecutar el siguiente comando: “netsh http add urlacl url=http://+:8090/ user=DOMAIN\UserName” para asignar permisos sobre un dirección HTTP a una cuenta de usuario determinada.

Posted: May 04 2007, 06:43 PM by Pedroafa | with no comments
Filed under: ,
Extension Methods en C# 3.0 con Orcas

Extension Methods es otra de las nuevas novedades que acompañan a C# 3.0. Extension Methods son métodos estáticos que pueden ser llamados usando instancias de objetos existentes. Por eso, es posible extender la funcionalidad de tipos incluidos en el CLR, añadiéndoles nuevos métodos para facilitar el trabajo. Para ello no es necesario crear subclases ni recompilar los tipos originales.

Un ejemplo de una situación donde podría ser útil, puede ser la siguiente. En ASP.NET 2.0 cuando se trabaja con roles y una acción sólo puede ser realizada por los usuarios de un determinado rol,  se suele utilizar el siguiente código para determinar si el usuario pertenece al grupo que sí puede ejecutar esa acción:

protected void Page_Load(object sender, EventArgs e)

{

    if (User.IsInRole("Administrador"))

    {

        //Realizar la siguiente operación.

        //Esta operación sólo puede ser ejecutada por los administradores del sistema.

    }

}

Pero si se trabaja con múltiples roles, una aproximación más elegante podría ser crear una clase estática con un método para cada rol que pregunte si el usuario pertenece a ese rol.

public static class Roles

{

    public static bool IsUserAdministrator(IPrincipal user)

    {

        if (user.IsInRole("Adminitrator"))

        {

            return true;

        }

 

        return false;

    }

 

    public static bool IsUserCustomer(IPrincipal user)

    {

        if (user.IsInRole("Customer"))

        {

            return true;

        }

 

        return false;

    }

}

 

protected void Page_Load(object sender, EventArgs e)

{

    if (Roles.IsUserAdministrator(User))

    {

        //Realizar la siguiente operación.

        //Esta operación sólo puede ser ejecutada por los administradores del sistema.

    }

}

Con esta refactorización hemos conseguido que el código sea mucho más legible y fácil de mantener.  Con la ayuda de los Extension Methods puede mejorarse considerablemente, ya que es posible hacer que la propia clase Page.User contenga los métodos IsUserAdministrator e IsUserCustomer. Para convertir estos métodos en extensiones de la clase User sólo hace falta añadir la palabra clave this delante de los tipos pasados como parámetros. El código debería quedar de la siguiente manera: