Extendiendo nuestra BBL: Añadiendo validaciones de datos para DataSet Tipados en NET 2.0

Después de un tiempo sin escribir nada por culpa de una rotura de fibras en mi cuadriceps derecho que me ha tenido de baja 2 semanas y media (Tiempo suficiente para empollar las nuevas funcionalidades de NET 2.0 y revisar unos magníficos tutoriales) he empezado a trabajar en casa con las nuevas caracteristicas de ADO.NET y la verdad es que me ha sorprendido bastante con que facilidad se puede crear una capa de acceso a datos y su correspondientes reglas de negocio utilizando para enlazar a la capa de presentación un ObjectDataSource.


En este ejemplo concreto que voy a explicaros a modo de resumen de 2 tutoriales en inglés que os he dejado los enlaces más abajo, como podemos extender las validaciones de datos en nuestra capa de negocios sobre una capa de datos creada con un dataset tipado.


Lo primero es crear nuestra capa de datos con el diseñador de VS 2005 (El primer enlace del final), arrastramos las tablas y creamos los correspondientes TableAdapters, entre ellos para la tabla Products (No voy a comentar nada al respecto sobre como crear la DAL).


Ahora bien, imaginemos que necesitamos validar que el precio del producto que nos introduce el usuario debe ser mayor que 0,  y para ello no debemos hacerlo directamente sobre el código que genera VS 2005:



El porque es sencillo: Como es código autogenerado, cuando por algún motivo tenga que autoregenerarse porque hemos modificado algo sobre nuestro dataset tipado, perderemos automáticamente nuestras modificaciones.


Entonces, ¿Como podemos crear nuestras reglas de negocio para validar dichos datos? Pues con el uso de clases parciales. Por ejemplo, en nuestro caso necesitamos extender la clase ProductsDataTable y para ello utilizaremos el evento ColumnChanging para detectar cuando nos modifican una row del datatable:


   1:  public partial class Northwind
   2:  {
   3:      public partial class ProductsDataTable
   4:      {
   5:          public override void BeginInit()
   6:           {
   7:              this.ColumnChanging += ValidateColumn;
   8:           }
   9:   
  10:           void ValidateColumn(object sender, DataColumnChangeEventArgs e)
  11:           {
  12:              if(e.Column.Equals(this.UnitPriceColumn))
  13:              {
  14:                 if(!Convert.IsDBNull(e.ProposedValue) && (decimal)e.ProposedValue < 0)
  15:                 {
  16:                    throw new ArgumentException(«UnitPrice > 0», «UnitPrice»);
  17:                 }
  18:              }
  19:           }
  20:      }
  21:  }

Como podeis observar es bastante sencillo validar ciertos datos en la capa de negocio que a nivel de esquema no nos lo permite VS 2005, podemos especificar su tamaño o permitir nulos o no, pero no podemos crear checks como el que hemos realizado en nuestra capa de negocio.


Los 2 tutoriales donde he podido aprender sobre este tema:


Creating a Data Access Layer


Creating a Business Logic Layer


He intentado hacer en este artículo un pequeño resumen de las cosillas que he creido mas interesantes.


Un saludo.

Response.Write("Tagged");

Pues yo si que pensé que me libraba pero David Herraiz me ha taggeado, así que os voy a contar 5 cosas sobre mí:



  1. Mi primer contacto con un ordenador fue hace mucho tiempo con un Amstrad CPC 464 (No me acuerdo cuantos años tenía pero era joven) con el que solía pasar mis ratos jugando al Camelot Warriors, Bomb Jack, Kung Fu Master… y mi favorito Target Renegade, pero mi contacto con la programación de verdad se remonta hasta hace muy poquito tiempo, concretamente en el 2001 (Empecé haciendo mis pinitos con VB) y he de decir que desde entonce hasta ahora estoy asombrado de como he crecido profesionalmente en este sector (Muchas horas delante de la pantalla).
  2. Aunque la programación es unos de mis hobbies, he de confesar que el mayor de ellos es pasar el mayor tiempo posible con mi familia (Mis padres, hermanas, sobrinos, cuñados…) amigos y sobre todo con mi novia (Que pedaso de mujer)
  3. Al contario que a David Herraiz, la cocina me apasiona. Cocino todo tipo de platos tradicionales, modernos, postres… y suelo recibir alagos cuando invito a comer/cenar a alguien a casa (Será para que los invite otra vez???).
  4. Hace 4 meses cumplí uno de mis sueños: Comprarme un ático e irme a vivir junto a mi novia…
  5. Por último podría decir que soy un apasionado del vino (Me encantan los caldos) y he empezado hace poquito a crear mi propia bodeguilla.

Buenos pues para los que no me conocéis que sois muchos ya sabéis algo más sobre mí.


Voy a taggear a:



Salu2

Saber si una página aspx que devuelve una imagen, la devuelve o no???

Diariamente colaboro respondiendo dudas a la gente que se pasa por los Foros de MSDN y por las NewsGroup de ASP.NET y el otro día salío un post en el cual querían saber cuando una página aspx devolvía una imagen correcta o directamente no la devolvía porque no existía. Era algo como muestra el siguiente código:


   1:  //Página que devuelve la imagen
   2:  public void descarga(string filepath, string filename)
   3:  {
   4:      Response.Clear();
   5:      Response.ContentType = «application/gif»;
   6:      Response.Flush();
   7:      Response.WriteFile(filepath);
   8:      Response.End();
   9:  }
  10:   
  11:  //Pagina que la recoge y la muestra en un ImageButton
  12:  public void cargarImagenes(String nombre)
  13:  {
  14:      String url = @»http://servidor/website3/Imagen.aspx?nombre=» +nombre;
  15:      IBPeriodico.ImageUrl = url;
  16:  }

Como se puede observar en el método cargarImagenes, no se puede saber si te devuelve una imagen o no, aunque si puedes poner una imagen de no disponible en el caso que la página aspx no devuelva una imagen, pero el caso es que necesitaban saber si retornaba una imagen o no. Pues me puse manos a la obra y se me ocurrio lo siguiente:


   1:  string url =
   2:  «http:/servidor/aplicacionDevuelveImagen.aspx?nombreImagen=loqsea»;
   3:  HttpWebRequest request = 
   4:  (HttpWebRequest)WebRequest.Create(url);
   5:  HttpWebResponse response;
   6:  response = (HttpWebResponse)request.GetResponse();
   7:  System.IO.Stream receiveStream = response.GetResponseStream();
   8:  try
   9:  {
  10:      Image img = Image.FromStream(receiveStream);
  11:  }
  12:  catch(Exception ex)
  13:  {
  14:      //La imagen devuelta no es válida y por 
  15:      //tanto podemos tomar una decisión sobre
  16:      //que hacer
  17:  }

La idea es que como el ContentType de la página aspx devuelve una imagen y con un objeto HttpWebResponse disponemos de un Stream y la clase Image tiene un método para crear una imagen a partir de un Stream, pues se me ocurrio que si la página no devuelve ninguna imagen al crear la imagen fallaría y así es.


No se si es una manera óptima o correcta de hacerlo pero la verdad es que funciona y no me parecía mal solución del todo.


Un saludo.

Off Topic: DotNetSolidario

Hola a todos,

Soy Pedro Pozo, webmaster de CLIKEAR.COM y de DOTNETSOLIDARIO.COM, algunos
ya me conoceis y espero que a partir de ahora me conozcais todos ya que
intentare pasarme por aqui mas habitualmente.

Primero queria contaros que el año pasado ya consegui llevar a cabo mi
primer proyecto solidario a traves de DOTNETSOLIDARIO, en concreto he
conseguido que  la empresa VASS done el dinero para la creacion de una
escuela en Anantapur (India). Este dinero ha sido donado a la Fundacion
Vicente Ferrer que se encargara de crear la escuela en Anantapur.

Pero tambien queria comentaros que quiero llevar a cabo este año 2007 otro
proyecto solidario que estoy dando a conocer a traves de DOTNETSOLIDARIO y
que es mucho mas ambicioso y que consiste en la creacion de un hospital en
Anantapur. Pero no quiero que sea solo una iniciativa a nivel individual
sino que me gustaria que muchas mas personas colaboraran en conseguir este
objetivo.

Para conseguir los fondos para la creacion de este hospital se me ha
ocurrido crear una cuenta en GOOGLE ADSENSE y que todo el que este
interesado en colaborar ponga la publicidad ADSENSE de esta cuenta en su
web. De esta forma se puede colaborar de una forma muy simple y entre todos
podemos llegar a conseguir un proyecto solidario muy bonito.

Comentaros que todo lo recaudado a traves de esta cuenta ADSENSE sera
entregado para la creacion de este Hospital a la Fundacion Vicente Ferrer.
Es decir que se entregara el dinero recaudado al 100%. Para conseguir la
mayor transparencia posible os ire enviando cada semana un pantallazo con
todo lo recaudado en su totalidad y lo recaudado por cada uno de los
colaboradores.

Mi intencion no es que sea una accion individual sino un proyecto en el que
participemos muchas personas, por eso lo he llamado «Hospital DOTNET», y
tambien sera un proyecto abierto a cualquier sugerencia y comentario que
cualquiera de los colaboradores quiera hacer.

Espero vuestra respuesta y vuestros comentarios y sugerencias y los que
esteis interesados en colaborar mandarme un email a pedro@clikear.com y os
enviare el codigo ADSENSE para que lo pongais en vuestra web y comenzar asi
el proyecto.

Saludos.

Problemas con CauseValidation en Microsoft.Web.UI.WebControls.Toolbar

Si has utilizado alguna vez en tus desarrollos el control Toolbar de los Internet Explorer WebControls, habrás podido observar que cuando pulsas sobre un ToolbarButton no causa validación en el cliente y si en vuestras páginas tenéis controles de tipo RequiredFieldValidator, CompareValidator… no validarán los datos de la página y en mi caso a uno de mis clientes le gusta bastante la barrita, con lo cual no me quedó otra que buscar como podía hacerlo.


No me gusta atribuirme lo que no es mío, voy a hacer referencia a la página donde encontré la solución:


http://forums.asp.net/thread/905318.aspx


Pero he de decir que ese ejemplo está un poco incompleto aunque me abrió la mente para desarrollarlo por completo, y en este post lo explicaré:


Como bien dice BillKrat, la idea es añadir un atributo al ToolBar para identificar que queremos utilizar nuestro propio click:


   1:  <iewc:Toolbar id=»tBar» useClickOnClientBefore=»true» runat=»server»>

En mi caso lo he llamado useClickOnClientBefore (en el ejemplo useMyClick).


Una vez echo esto necesitamos modificar la función javascript que dispara un ToolBarButton al ser pulsado y para ello vamos a editar el archivo toolbar.htc que está en mi caso en C:Inetpubwwwrootwebctrl_client1_0. Buscamos la función: function f_FireButtonClick(oCell) y modificamos su código por:


   1:  if (typeof(useClickOnClientBefore) != «undefined» && useClickOnClientBefore==»true»)
   2:      ClickOnClientBefore(oCell);
   3:  else
   4:      evButtonClick.fire(f_CreateEventObject(oCell));

Si has mirado el link que puse antes y pruebas el ejemplo te encontrás un error si tienes 2 toolbar y en una de ellas no pusieses el atributo useMyClick, y te dirá que useMyClick no está definido, por eso yo compruebo que useClickOnClientBefore está definido en el Toolbar y sino lanzó el click normal del ToolBar.


Una vez hecho esto, necesitamos crearnos en un archivo de script o en la misma página la función ClickOnClientBefore, que será la que controle los click de los botones:


   1:  <script language=»javascript»> 
   2:      function ClickOnClientBefore(oButton)
   3:      { 
   4:          switch(oButton.id)
   5:          {
   6:              case «enviar»:
   7:                  if (typeof(Page_ClientValidate) == ‘function’) 
   8:                  {
   9:                      if (Page_ClientValidate())
  10:                      {
  11:                          __doPostBack(‘tBar’,0)
  12:                      }
  13:                  }
  14:                  break;
  15:          } 
  16:      } 
  17:  </script>

Y ya en el servidor podemos recoger el evento:


   1:  private void tBar_ButtonClick(object sender, System.EventArgs e)
   2:  {
   3:      this.OnButtonClick((ToolbarButton)sender);
   4:  }
   5:   
   6:  private void OnButtonClick(ToolbarButton tButton)
   7:  {
   8:      switch(tButton.Index)
   9:      {
  10:          case 0:
  11:              Response.Write(«Has Pulsado Enviar»);
  12:              break;
  13:      }
  14:  }

Y con esto ya podemos validar datos en cliente o lanzar alertas, emsajes de confirmación o cualquier código javascript que se te ocurra. Adjunto el código de ejemplo con el archivo toolbar.htc, no obstante si alguien conoce una manera mas efectiva que no dude en comentarlo.


Salu2.

Diferencia entre usar String.Format y ToString

Hoy cuando he llegado al trabajo me he hecho una de esas preguntas que rápidamente me hacen abrir el VS tirar unas líneas de código y comprobar el IL a ver que ocurre.

La pregunta que me he hecho hoy es:

¿Que diferencia existe entre usar String.Format y ToString?

Para ello he hecho este programita:

   1:  using System;
   2:   
   3:  namespace PruebasBoxing
   4:  {
   5:      /// <summary>
   6:      /// Descripción breve de Test.
   7:      /// </summary>
   8:      public class Test
   9:      {
  10:          [STAThread]
  11:          static void Main(string[] args)
  12:          {
  13:              double d = 12.45;
  14:              string formato1 = String.Format("{0:C}", d);
  15:              string formato2 = d.ToString("C");
  16:          }
  17:      }
  18:  }

Y el ensamblado lo he abierto con ildasm:

   1:  .method private hidebysig static void  Main(string[] args) cil managed
   2:  {
   3:    .entrypoint
   4:    .custom instance void [mscorlib]System.STAThreadAttribute::.ctor() = ( 01 00 00 00 ) 
   5:    // Code size       41 (0x29)
   6:    .maxstack  2
   7:    .locals init ([0] float64 d,
   8:             [1] string formato1,
   9:             [2] string formato2)
  10:    IL_0000:  ldc.r8     12.449999999999999
  11:    IL_0009:  stloc.0
  12:    IL_000a:  ldstr      "{0:C}"
  13:    IL_000f:  ldloc.0
  14:    IL_0010:  box        [mscorlib]System.Double
  15:    IL_0015:  call       string [mscorlib]System.String::Format(string,
  16:                                                                object)
  17:    IL_001a:  stloc.1
  18:    IL_001b:  ldloca.s   d
  19:    IL_001d:  ldstr      "C"
  20:    IL_0022:  call       instance string [mscorlib]System.Double::ToString(string)
  21:    IL_0027:  stloc.2
  22:    IL_0028:  ret
  23:  } // end of method Test::Main

Si nos fijamos en la línea 14 se está haciendo un boxing del double con lo que se está creando un nuevo tipo referenciado que se pasa a la función String.Format que como vemos recibe un object, y como sabemos este boxing produce una sobrecarga que además hace que el GC sea necesario para limpiar esa referencia cuando deje de utilizarse, cosa que con ToString no ocurre.

Salu2.

Reflexionando con Reflection…

Hoy un compañero me ha hecho la siguiente consulta:


«Un compañero mio tiene una clase creada con muchas propiedades (unas 40 o asi). En el codigo de asp.net tiene un datatable con una fila y varias columnas, y el nombre de esas columnas coincide con cada una de las propiedades. Tiene que introducir en cada propiedad su correspondiente valor del datatable, por ejemplo asi
with claseprueba
         .propiedad1 = oDT.rows(0).item(0).item(«propiedad1»)
         .propiedad2 = oDT.rows(0).item(0).item(«propiedad2»)
         …..


El caso es que claro, al ser 40 pues meter todo eso a pelo es trabajo pesado aparte de quedar muy feo en el codigo, y dando gracias porque en vez de 40 no son 400. Yo creo que debe haber alguna forma de recorrer todas las propiedades de la clase para poder simplificar esto. En principio pensé en hacer un for each pero como no lo he tocado nunca pues no consigo nada.»


Pues manos a la obra le he preparado una solución de Consola de ejemplo para que viese como hacerlo:


Dada una clase Prueba con 3 propiedades, recorremos dichas propiedades, obtenemos el nombre y las establecemos un valor que en este caso es el nombre de la propiedad en concreto:



   1:  Imports System.Reflection
   2:   
   3:  Module Module1
   4:   
   5:      Sub Main()
   6:          Dim prueba As Prueba = New Prueba
   7:          Dim mitype As Type = Type.GetType(«Reflection.Prueba»)
   8:          Dim members As MemberInfo() = mitype.GetMembers()
   9:          For Each member As MemberInfo In members
  10:              If member.MemberType = MemberTypes.Property Then
  11:                  Dim p As PropertyInfo = CType(member, PropertyInfo)
  12:                  p.SetValue(prueba, member.Name, Nothing)
  13:              End If
  14:          Next
  15:          Console.WriteLine(«Valor Propiedad A: {0}», prueba.PropiedadA)
  16:          Console.WriteLine(«Valor Propiedad B: {0}», prueba.PropiedadB)
  17:          Console.WriteLine(«Valor Propiedad V: {0}», prueba.PropiedadC)
  18:          Console.Read()
  19:      End Sub
  20:   
  21:  End Module
  22:   
  23:  Class Prueba
  24:      Dim a As String
  25:      Dim b As String
  26:      Dim c As String
  27:   
  28:      Public Property PropiedadA() As String
  29:          Get
  30:              Return a
  31:          End Get
  32:          Set(ByVal Value As String)
  33:              a = Value
  34:          End Set
  35:      End Property
  36:   
  37:      Public Property PropiedadB() As String
  38:          Get
  39:              Return b
  40:          End Get
  41:          Set(ByVal Value As String)
  42:              b = Value
  43:          End Set
  44:      End Property
  45:   
  46:      Public Property PropiedadC() As String
  47:          Get
  48:              Return c
  49:          End Get
  50:          Set(ByVal Value As String)
  51:              c = Value
  52:          End Set
  53:      End Property
  54:  End Class

Fijandonos en el bucle, obtenemos las propiedades de la clase Prueba y mediante el objeto PropertyInfo establecemos el valor a la propiedad correspondiente, así para el caso del grid haríamos:



   1:  For Each member As MemberInfo In members
   2:      If member.MemberType = MemberTypes.Property Then
   3:          Dim p As PropertyInfo = CType(member, PropertyInfo)
   4:          p.SetValue(claseprueba, oDT.rows(0).item(0).item(member.Name), Nothing)
   5:      End If
   6:  Next

Prometo escribir algo más sobre Reflection ya que para la certificación tuve que empollar un poco. como veo que en el preview se corta os pongo un zip con el código.


Y con esto y un bizcocho hasta mañana a las 8:30 que entro a currar.

Cuento de Navidad: El Analista

Había una vez en un reino de la capital (Llamémosle R) un analista que hacía unos análisis malignos que condenaban a los pobres programadores que allí vivían. Cierto día, llamó a uno de sus programadores (Llamémosle A)  y le dijo:


– El rey me ha mandado hacer un análisis, debemos crear una tabla que contemple las entradas y salidas de productos de R, el código de R aparecerá siempre y ademas debemos reflejar el reino origen y el reino destino (Obligatoriamente R debe aparecer en uno de los 2 porque siempre actuará como origen o como destino nunca como los 2) y otro campo de tipo char que indique ‘S’ Salida o ‘E’ entrada, de tal forma la tabla quedará así:














Cod Reino Cod Reino Origen Cod Reino Destino Tipo Movimiento
001 001 002 E


Pero «A» se armó de valor sacó su ingenio y le contesto:


– No es que no quiera hacerlo, sí hay que hacerse se hace pero hacerlo pa ná es tontería, así que viendo que el primer Cod Reino era redundante y viendo que el Cod Reino Origen y Cod Reino Destino se podían combinar en un solo Cod Reino (Puesto que R siempre existirá) , ya que el campo Tipo Movimiento indicaba al reino si era el origen o el destino en el movimiento, propuso la siguiente tabla:










Cod Reino Tipo Movimiento
002 E

Pero si permites movimientos entre otros reinos que no sean R:











Cod Reino Origen Cod Reino Destino Tipo Movimiento
002 003 E


Pero el Analista contratacó:


– El que manda soy yo y punto.


Y colorín colorado este cuento se ha acabado.