sábado, 6 de octubre de 2012

[ASP.NET] Web Service - Publicar en IIS Windows XP (2/3)

 

Introducción


Aunque actualmente seguro se cuanta con Win 7, o alguna versión Win 2008 en un servidor, no va a faltar la ocasión que ante alguna prueba simple solo se disponga de Windows XP para montar el server web, quizás para algún uso de red local resulta simple

Se da por hecho que el IIS esta instalado en Windows, lo cual se realiza desde “Add and Remove Programs” en la opción especial “Add and Remove Windows Components”

Algo que debe aclararse antes de empezar es que el IIS de Windows XP no se pueden crear sitios web a los cuales se le asignen un puerto, esto si se puede en Win 7 o alguna las versiones de Win Server

En el IIS de Win Xp solo se pueden crear directorios virtuales que estarán bajo el default en el puerto 80

Este artículo es la continuación de

[ASP.NET] Crear y Publicar - Web Service (1/3)

 

Definición del sitio web mediante Wizard


El primer paso como es de esperar será acceder al IIS, para lo cual se deberá ir al panel del control

SNAGHTML3fac085

Se accede el IIS, sonde podemos ver el sitio por defecto configurado en el puerto 80

image

Para crear el sitio que alojara el servicio solo es necesario acceder al Wizard que nos guiara

image

image

Se define un nombre que identifique el contenido

image

Se define la carpeta donde se alojara el deploy del desarrollo

image

Si se requiere se puede cambiar las opciones de acceso al sitio, se marcaria la opción “Browse” si se requiere permitir listas los archivos si es que no se define ninguna pagina en concreto, se marcaria “Write” en caso de permitir subir archivos al sitio por medio de http

image

image

La finalización dejaría el sitio como hijo del sitio Default, es importante validar que este el icono de la caja gris, lo cual indica que se creo correctamente

image

 

Alternativa creación sitio (sin Wizard)


La alternativa arranca con la creación de la carpeta del sitio que se quiere crear, pero en esta oportunidad debe realizarse dentro del wwwroot

image

Esto permite que al actualiza el IIS se observara la carpeta recientemente creada, esta claro que mientras este solo la carpeta con el icono simple no podrá alojarse un desarrollo que cuente con un web.config y dll independiente, porque como carpeta el root sigue siendo el default

image

el siguiente paso será acceder a las propiedades

image

La opción “Create” será la encargada de convertir esa simple carpeta en un directorio virtual que pueda alijar el desarrollo

image

image

Ahora si la carpeta ha cambiado y esta lista para aceptar la publicación

image

 

Publicar desarrollo


La publicación se podría ver como en dos partes, la primera consiste en obtener la compilación del proyecto web, para esta tarea se dispone la la opción de “Publish” del Visual Studio

image

Se presentara un cuando como el siguiente

image

donde se debería ingresar una carpeta local de la pc donde se ubicara la publicación

Se puede observar el log de la tarea para validar que no se produce ningún error

image

El resultado será la publicación en la carpeta

SNAGHTML4311aa7

Esto debería copiarse y pegarse en la carpeta definida en el IIS

image

Desde el IIS se observara el cambio

SNAGHTML433d10a

Y si se accede desde el browser se podra ver la pagina web

image

y el servicio también

image

 

Configuración


Seguramente se deba realizar en algún momento el cambio de configuración en el sitio

image

Si algo no funciona seguro seria bueno validar que la extensión este definida

image

O validar los tiempos de timeout del sitio

image

Si estamos en desarrollo poder depurar seria de ayuda

image

Pero creo que verificar la seguridad es un punto clave para el funcionamiento

image

El usuario utilizado para impersonar el sitio es el definido en la opción de anónimos, si es necesario acceder a una carpeta de red o ejecutar alguna aplicación con determinados privilegios será necesario validar que ese usuario pueda hacerlo

image

Validar si la aplicación desarrollada cuenta con alguna pagina por defecto  

image

sino se cuenta con una pagina dentro de la lista de default y tampoco se define una, al acceder al sitio este fallara porque no podrá encontrar que pagina cargar,  salvo que se tenga la opción de “Browse” marcada

Validar la versión del framework es muy importante

image

si de casualidad se instalo primero el framework de .net y luego se habilito el IIS quizás haga falta ejecutar el comando

aspnet_regiis –i

para así asociar los componente de .net con el IIS

ASP.NET IIS Registration Tool (Aspnet_regiis.exe)

[ASP.NET] Crear y Publicar - Web Service (1/3)

 

Introducción


Un problema que se presenta regularmente luego de terminado el desarrollo de un proyecto web esta relacionado con la publicación y puesta en marca en un ambiente productivo, la idea del articulo será la de marcar los pasos para la creación y puesta en producción de un servicio web, así como también una página aspx, como veremos tratar un servicio o una pagina requieren los mismo pasos.

Se podría considerarse un servicio web (asmx) como una paginas aspx solo que sin una interfaz visual que se desplegué al usuario, los servicio permitirán que otras aplicaciones recuperen información publicada por medio de un protocolo conocido como es el http

En esta primera parte se analizaran:

  • Crea un proyecto asp.net
  • Agregar un web service al desarrollo web
  • Consumir web service
  • Actualizar configuración servicio

El ejemplo en el cual se basara la demostración consiste solo una lista de cursos que se obtiene de una base de datos. Como la publicación de un servicio es idéntica a la de una pagina web aprovecharemos el articulo para contemplar ambos escenarios.

 

Crear proyecto web


El primer paso será la creación del proyecto web

clip_image002

Es importante usar la opción que sea un Web Application

SNAGHTML11bb456

La estructura del proyecto del ejemplo incluye una db en carpeta de datos

clip_image004

Al ejecutar la pagina que lista los cursos se visualizara en el browser el resultado

clip_image006

Como es un ejemplo simple solo se hizo uso de un gridview junto a la ayuda de Entity Framework para resolver el acceso a datos

Por supuesto hasta ahora se hace uso del Visual Studio Developer Server, el cual es muy útil cuando se esta desarrollando, pero solo en esta etapa luego debe publicarse en el IIS

SNAGHTML3c4c21a

image

El Visual Studio creara por nosotros un server virtual en el puerto indicado en la configuración

 

Crear Servicio web


El próximo paso involucra la creación del servicio web, para ello agregaremos un nuevo ítem al proyecto web creado en el paso anterior

clip_image008

 

image

En el servicio se define un webmethod que liste los cursos

 

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
public class SchoolService : System.Web.Services.WebService
{

    [WebMethod]
    public List<CurseDTO> GetCurseList()
    {
        using (SchoolEntities context = new SchoolEntities())
        {
            return (from curse in context.Course
                   select new CurseDTO()
                   {
                       CurseId = curse.CourseID,
                       Title = curse.Title,
                       Department = curse.Department.Name
                   }).ToList();
        }
    }
}

public class CurseDTO
{
    public int CurseId {get; set;}
    public string Title { get; set; }
    public string Department {get; set;}
}

Si queremos ver la pantalla de inicio del servicio para validar que este correcto, solo será cuestión de navegarlo en el browser

clip_image012

image

Y hasta se puede invocar desde allí mismo

image

 

Consumir servicio web


Para consumir un servicio desde una aplicación cliente se agrega una referencia que creara el proxy, el cual no es mas que código para facilitar la invocación al servicio

SNAGHTML110147f

En este caso como el servicio esta en la misma solución se descubre de forma directa, sino será necesario definir la url donde se publico el servicio

SNAGHTML111cec8

Si bien a simple vista pareciera que la referencia al servicio no hizo mucho

SNAGHTML3cdc8ed

Si se habilita la opción superior de la barra del solution explorer se podrá descubrir mucho

SNAGHTML3cf8fa5

La referencia al servicio crea código por nosotros, además de definir una sección en el app.config, en el cual se podrá cambiar la url sin necesidad de recompilar luego de hacer la implementación definitiva del servicio en el IIS

image

Consumir el servicio desde código solo implica crear la instancia del proxy para luego hacer uso del método definido

using School.UI.SchoolSvc;
.
.

CurseDTO[] curseList;
using (SchoolServiceSoapClient svc = new SchoolServiceSoapClient())
{
    curseList = svc.GetCurseList();
}

dgvCourse.DataSource = curseList;

Con esto alcanza para invocar al servicio que hostea el server virtual que crea el VS y cargar el grid con la misma lista que al principio mostramos en la pagina web

Algo que seguramente habrán notado en el código seguro se relaciona con la respuesta del web method, como puede ser que devuelve un array de entidades cuando el código del servicio deja bien claro que era una lista, bien eso se debe a como se configura la referencia, pero puede cambiarse si hace falta.

SNAGHTML3ecbfca

Y se define el tipo de dato para que sea una lista

SNAGHTML3efc7b6

Al recompilar el proyecto el código que genera la referencia al servicio también devuelve una lista.

Código


El ejemplo del código fue creado con Visual Studio 2008 y Sql Server Express 2008 R2

[C#]
 

domingo, 23 de septiembre de 2012

Como evitar el uso del switch (2/2)

 

Introducción


Ester artículo será una expansión y mejora del anterior

Como evitar el uso del switch (1/2)

Básicamente se plantearan dos cambios significativos

  • definir una clase base, donde poder definir código repetitivo
  • el combobox resuelva de forma directa la instancia de calculo

 

Definir una clase base


Una de de los principales problemas que presenta a simple vista es el código similar en cada implementación de las clases de calculo, en la mayoría de los casos el bloque

imagen1

es idéntico, salvando lo resaltado en círculos que varia en cada implementación, lo resaltado se trata justamente de las funciones de fecha que el plazo determina en cada caso

Ahora bien, como se podría evitar repetir código ?

Para poder lograrlo realizaremos varias modificaciones, la primer será cambiar la Interface por una clase abstract, esto nos permitirá definir una clase base que puede definir código reutilizable

 

public abstract class ResolverPlazo
{
    public abstract PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota);

    protected PazoPagoResult Calcular(DateTime fechainicio, 
                                    decimal monto, 
                                    decimal montocuota, 
                                    Func<DateTime, int, DateTime> formulafechaFin, 
                                    Func<DateTime, int, DateTime> formulafechaItem)
    {
        //
        //Se calculan la cantidad de cuotas
        //
        int cantcuotas = Convert.ToInt32(Math.Floor(monto / montocuota));

        //
        // Se define la entidad de respuesta
        //
        PazoPagoResult result = new PazoPagoResult()
        {
            FechaFin = formulafechaFin(fechainicio, cantcuotas)
        };

        //
        //crea la lista de Pagos
        //
        for (int cuota = 1; cantcuotas >= cuota; cuota++)
        {
            ItemPago item = new ItemPago()
            {
                Fecha = formulafechaItem(fechainicio, cuota),
                Monto = montocuota * cuota
            };

            result.ListaPagos.Add(item);
        }

        return result;
    }
}

Hay algunos puntos importantes por resaltar, el primero se define un método como abstract, cada clase concreta será responsabilidad desarrollarlo, pero además para ayudar se define un método adicional que actuaria como témplate para el calculo, define el código que detectamos como repetitivo y añade un punto de extensibilidad por medio del uso de Func<>

En los lugares donde se requiere especificidad que solo la clase concreta conoce se deja el lugar abierto para poder hacerlo, básicamente el Func<> es un delegado que recibe una fecha y un valor numérico, y espera como respuesta una fecha

Las clases concretas se ven ahora beneficiadas, reduciéndose notablemente el código

 

public class SinPlazoPago : ResolverPlazo
{
    public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota)
    {
        return new PazoPagoResult()
        {
            FechaFin = fechainicio,
            ListaPagos = null
        };
    }
}

public class UnSoloPago : ResolverPlazo
{
    public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota)
    {
        return new PazoPagoResult()
        {
            FechaFin = fechainicio,
            ListaPagos = new List<ItemPago>()
            {
                new ItemPago(){ Fecha = fechainicio, Monto = monto }
            }
        };
    }
}

public class Semanal : ResolverPlazo
{
    public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota)
    {
        return base.Calcular(fechainicio,
            monto,
            montocuota,
            (fecha, cuotas) => fecha.AddDays(7 * cuotas),
            (fecha, cuota) => fecha.AddDays(7 * cuota));

    }
}

public class Quincenal : ResolverPlazo
{
    public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota)
    {
        return base.Calcular(fechainicio,
                            monto,
                            montocuota,
                            (fecha, cuotas) => fecha.AddDays(15 * cuotas),
                            (fecha, cuota) => fecha.AddDays(15 * cuota));
    }
}

public class Mensual : ResolverPlazo
{
    public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota)
    {
        return base.Calcular(fechainicio,
                            monto,
                            montocuota,
                            (fecha, cuotas) => fecha.AddMonths(1 * cuotas),
                            (fecha, cuota) => fecha.AddMonths(1 * cuota));
    }
}

public class Bimestral : ResolverPlazo
{
    public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota)
    {
        return base.Calcular(fechainicio,
                monto,
                montocuota,
                (fecha, cuotas) => fecha.AddMonths(2 * cuotas),
                (fecha, cuota) => fecha.AddMonths(2 * cuota));
    }
}

public class Trimestral : ResolverPlazo
{
    public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota)
    {
        return base.Calcular(fechainicio,
                               monto,
                               montocuota,
                               (fecha, cuotas) => fecha.AddMonths(3 * cuotas),
                               (fecha, cuota) => fecha.AddMonths(3 * cuota));
    }
}

public class Semestral : ResolverPlazo
{
    public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota)
    {
        return base.Calcular(fechainicio,
                               monto,
                               montocuota,
                               (fecha, cuotas) => fecha.AddMonths(6 * cuotas),
                               (fecha, cuota) => fecha.AddMonths(6 * cuota));
    }
}

public class Anual : ResolverPlazo
{
    public override PazoPagoResult Calcular(DateTime fechainicio, decimal monto, decimal montocuota)
    {
        return base.Calcular(fechainicio,
                               monto,
                               montocuota,
                               (fecha, cuotas) => fecha.AddYears(1 * cuotas),
                               (fecha, cuota) => fecha.AddYears(1 * cuota));
    }
}

Solo las dos primeras clases (que no tienes lógica en la calculo de los plazos de pago) quedaron como se definen originalmente, mientras que el resto hace uso de la funcionalidad definida en la clase base, nótese como las expresiones lambda en los parámetros del delegado permite variar el calculo de la fecha en cada caso.

 

Resolver de forma directa la clase de calculo


Este caso quizás no aplique en todas los casos ya que se requiere de entorno en donde los controles puedan conservar las instancias de los ítems con que con cargados, en este caso por tratarse de un ejemplo con Windows Application puede aplicarse, pero si seria un entorno web no se podria ya que los controles no conservan estado, se debería seguir usando la implementación original.

Lo que se pretende es poder quitar el método que usa el Dictionary<> para definir que clase concreta resolverá el calculo, el primer cambio se realiza en la clase que se usara para cargar el combo

public class Plazo 
{
    public int Id { get; set; }
    public string Desc { get; set; }
    public ResolverPlazo Instancia { get; set; }
}

se agrega una propiedad que define la instancia de la clase que resuelve el calculo de plazos para ese ítem

El siguiente cambio impacta en la forma como se devuelve la lista de ítems, se define nueva la propiedad con la instancia concreta de cada implementación de calculo

 

public static class PlazosHelper
{

    public static List<Plazo> ObtenerPlazos()
    {
        return new List<Plazo>()
        {
            new Plazo() { Id=1, Desc="Un solo pago", Instancia = new UnSoloPago()  },
            new Plazo() { Id=2, Desc="Semanal", Instancia = new Semanal()  },
            new Plazo() { Id=3, Desc="Quincenal", Instancia = new Quincenal()  },
            new Plazo() { Id=4, Desc="Mensual", Instancia = new Mensual()  },
            new Plazo() { Id=5, Desc="Bimestral", Instancia = new Bimestral()  },
            new Plazo() { Id=6, Desc="Trimestral", Instancia = new Trimestral()  },
            new Plazo() { Id=7, Desc="Semestral", Instancia = new Semestral()  },
            new Plazo() { Id=8, Desc="Anual", Instancia = new Anual()  }
        };
    }

    public static List<Plazo> ObtenerPlazosConItemOpcional()
    {
        List<Plazo> plazos = ObtenerPlazos();
        plazos.Insert(0, new Plazo() { Id = 0, Desc = "<<<Seleccione>>>", Instancia = new SinPlazoPago() });
        return plazos;
    }

}

Solo queda cambiar la forma como se recupera el ítem del combo y se invoca el método de calculo

 

private void Calcular()
{
    errorProv.Clear();

    decimal monto = 0;
    if (!decimal.TryParse(txtMonto.Text, out monto))
    {
        errorProv.SetError(txtMonto, "Debe ingresar un valor numerico");
        return;
    }

    decimal montoporcuota = 0;
    if (!decimal.TryParse(txtMontoPorCuota.Text, out montoporcuota))
    {
        errorProv.SetError(txtMontoPorCuota, "Debe ingresar un valor numerico");
        return;
    }
   
    //
    // Recuperamos la instancia de la entidad bindeada al combo
    //
    ResolverPlazo calcularPlazo = ((Plazo)cmdPlazo.SelectedItem).Instancia;

    //
    // se invoca de forma directa la operacion de calcular
    //
    PazoPagoResult result = calcularPlazo.Calcular(dtpFechaInicio.Value,
                                                        monto,
                                                        montoporcuota);

    txtResultado.Text = result.FechaFin.ToShortDateString();

    dgvListaPagos.DataSource = result.ListaPagos;

}

La clave esta en estas dos líneas

//
// Recuperamos la instancia de la entidad bindeada al combo
//
ResolverPlazo calcularPlazo = ((Plazo)cmdPlazo.SelectedItem).Instancia;

//
// se invoca de forma directa la operacion de calcular
//
PazoPagoResult result = calcularPlazo.Calcular(dtpFechaInicio.Value,
                                                    monto,
                                                    montoporcuota);

Como cada ítem del combo permite recuperar la clase con que fue creado, se puede castear al tipo concreto para así recuperar la instancia de la clase que posee la lógica de calculo de las cuotas, con eso se evita usa un método adicional que defina un diccionario de correspondencias entre un id y su instancia

Ya no se requiere el SelectedValue, porque no se trabaja con ningún id o código, sino que se accede directo a la instancia de la clase concreta, acortando las interacciones

 

Código


[C#]