miércoles, 23 de diciembre de 2009

Obtener las clases derivadas de una clase base

Problemática

Tenemos un conjunto de clases que heredan todas de la misma clase base y al iniciar el programa necesitamos inicializar y configurar una instancia de cada una de las clases hijas. Hasta ahora cuando necesitábamos definir una clase hija teníamos que:
1. Crear la nueva clase.
2. Ir al método que inicializa las clases hijas.
3. Ir al método donde se configuran las clases hijas.

¡Un lío! .. y más si tenemos en cuenta que es algo que se hace esporádicamente.

Nuestro deseo era que cuando alguien necesite implementar la definición de una nueva clase hija simplemente deba hacer eso: definirla y punto, del resto de tareas (inicializar y configurar) que se preocupe el programa.

Solución

La solución pasa por usar Reflection. Realizamos una búsqueda en nuestro Assembly de todas las clases existentes que heredan de la clase base y las instanciamos, ¡Así de fácil!

Ejemplo

Primero creamos la clase base y las derivadas:

Public MustInherit Class ClaseBase

Public MustOverride Sub Metodo()

End Class

Public Class ClaseQueHerada
Inherits ClaseBase

Public Sub New()
Console.WriteLine("Constructor de la ClaseQueHerada")
End Sub

Public Overrides Sub Metodo()
Console.WriteLine("ClaseQueHerada.Metodo")
End Sub

End Class

Public Class OtraClaseQueHerada
Inherits ClaseBase

Public Sub New()
Console.WriteLine("Constructor de la OtraClaseQueHerada")
End Sub

Public Overrides Sub Metodo()
Console.WriteLine("OtraClaseQueHerada.Metodo")
End Sub

End Class

En el código siguiente podemos ver como buscamos las clases derivadas, instanciamos y ejecutamos los métodos comunes y todo en tiempo de ejecución.


Module Main

Sub Main()

'Accedemos al Assembly que estamos ejecutando
Dim CurrentAssembly As Assembly = System.Reflection.Assembly.GetExecutingAssembly()
'Con LINQ obtenemos la lista de classes que heredan de ClaseBase, gracias al método IsSubClassOf
Dim q = From t As Type In CurrentAssembly.GetTypes() _
Where t.IsSubclassOf(GetType(ClaseBase))

For Each o As Type In q
'Para cada una de las clases hijas la instaciamos y ejecutamos el método
Console.WriteLine(o.Name)
Dim der As ClaseBase = CurrentAssembly.CreateInstance(o.FullName, True)
der.Metodo()
Next

Console.Read()

End Sub

Con este método de acceso a las clases hijas te puedes ahorrar mucho trabajo.

jueves, 26 de noviembre de 2009

Hasta el infinito y más allá: Visual Studio online

¿Qué pasa cuando 5 tipos muy freaks se encierran para desarrollar?
Eso es lo que hicieron la gente de CodeRun y apareció esto:

coderun

Estos tipos han desarrollado un ¡Visual Studio online! Todo hecho en Javascript y yo que pensaba que lo había visto todo cuando los de Google hicieron Gmail. Si alguien tiene 5 minutos para jugar que le un vistazo al CodeRun Studio.

¿Y para que sirve esto?

Imagínate que estas en un lugar sin portátil y necesitas acceder a tu código fuente, realizar una corrección y compilar. Con esta herramienta “podríamos hacerlo” siempre y cuando no utilicemos sistemas de control de versiones, pruebas unitarias, etc.

La idea está verde aunque se ha realizado ya un trabajo impresionante pero nos da una idea de lo que podremos llevar a ver en la nube.

Toda la aplicación se ha desarrollado con el CodeRun web Toolkit sus propias librerías de controles que tienen a la venta y que no están nada mal.

viernes, 20 de noviembre de 2009

¿Cómo saber el número de registros de una tabla?


Cuando trabajas con SQL Server a veces es necesario consultar cuantos registros tienes en una tabla concreta de la base de datos. Con el script que encontrareis acontinuación obtendreis fácilmente esta infomación.


SELECT
[TableName] = so.name,
[RowCount] = MAX(si.rows)
FROM
sysobjects so,
sysindexes si
WHERE
so.xtype = 'U'
AND
si.id = OBJECT_ID(so.name)
GROUP BY
so.name
ORDER BY
2 DESC

Visto en How do I get a list of SQL Server tables and their row counts?



Si con lo anterior no tienes suficiones el maestro Pinal Dave nos enseña como obtener además el número de columnas y el tamaño en bytes de cada una de las tablas.
El script sería así:


CREATE TABLE #temp (
table_name sysname ,
row_count INT,
reserved_size VARCHAR(50),
data_size VARCHAR(50),
index_size VARCHAR(50),
unused_size VARCHAR(50))
SET NOCOUNT ON
INSERT #temp
EXEC sp_msforeachtable 'sp_spaceused ''?'''
SELECT a.table_name,
a.row_count,
COUNT(*) AS col_count,
a.data_size
FROM #temp a
INNER JOIN information_schema.columns b
ON a.table_name collate database_default
= b.table_name collate database_default
GROUP BY a.table_name, a.row_count, a.data_size
ORDER BY CAST(REPLACE(a.data_size, ' KB', '') AS integer) DESC
DROP TABLE #temp

Visto en SQL SERVER - Query to find number Rows, Columns, ByteSize for each table in the current database - Find Biggest Table in Database

martes, 21 de julio de 2009

Convertir tus objetos en XML directamente

En algunas ocasiones es necesario guardarse un objeto en su estado actual con todas sus propiedades por ejemplo para guardar en un fichero log, o para transferir por algún protocolo que sólo admite texto, etc. En casos como estos suele ser muy práctico utilizar las clases de serialización que vienen nativas en el Framework.net.

Imaginad que tenemos la típica clase Persona que representa un objeto de negocio de nuestra aplicación:

   1:  Public Class Persona
   2:   
   3:      Private _Nombre As String
   4:      Private _Apellidos As String
   5:   
   6:      Public Property Nombre() As String
   7:          Get
   8:              Return _Nombre
   9:          End Get
  10:          Set(ByVal value As String)
  11:              _Nombre = value
  12:          End Set
  13:      End Property
  14:   
  15:      Public Property Apellidos() As String
  16:          Get
  17:              Return _Apellidos
  18:          End Get
  19:          Set(ByVal value As String)
  20:              _Apellidos = value
  21:          End Set
  22:      End Property
  23:   
  24:      Sub New()
  25:          Me.Nombre = String.Empty
  26:          Me.Apellidos = String.Empty
  27:      End Sub
  28:   
  29:  End Class

Para pasar un objeto de esta clase a String necesitamos utilizar las clases System.Xml.Serialization.XmlSerializer y System.IO.StringWriter de la siguiente forma:

   1:  Dim PersonaSerializada As String
   2:  Dim Yo As New Persona()
   3:  Yo.Nombre = "Yo"
   4:  Yo.Apellidos = "Mismo"
   5:   
   6:  Using sw As New StringWriter()
   7:      Dim serialitzador As New XmlSerializer(GetType(Persona))
   8:      serialitzador.Serialize(sw, Yo)
   9:      PersonaSerializada = sw.ToString()
  10:  End Using

Y el resultado que tendríamos sería:

<?xml version="1.0" encoding="utf-16"?>
<Persona xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Nombre>Yo</Nombre>
<Apellidos>Mismo</Apellidos>
</Persona>

Este es un método práctico y rápido para guardar el estado de un objeto en un momento concreto y puede ser muy útil tanto para poder realizar trazas de un procedimiento concreto o para montar un sistema de gestión de excepciones avanzado .. imaginad que cada vez que se produce una excepción tratando un objeto de negocio nos guardamos la información de la excepción y además el objeto entero.

A tener en cuenta:

  • El objeto System.Xml.Serialization.XmlSerializer también incluye el método Deserialize para cargar el objeto de negocio a partir de un String.
  • Si cambiamos las referencias al objeto System.IO.StringWriter por un System.IO.StreamWriter podemos guardar el objeto serializado en un fichero XML directamente.
  • Los procesos de serialización suelen tener un coste elevado para el sistema así que es conveniente utilizarlo en entornos donde no prime el rendimiento de la aplicación … ¿existe hoy día algún sitio donde no sea imprescindible el rendimiento? .. :-P

lunes, 20 de julio de 2009

Asociar descripción a una Enum

Las Enum nos ofrecen la posibilidad de asociar un nombre descriptivo a un conjunto de valores relacionados entre si, estos valores son numéricos.

Public Enum EstadoCivil 
Soltero
Casado
End Enum

Muchas veces cuando tenemos una Enum tenemos la necesidad de obtener el nombre que le hemos dado a uno de los valores como si fuera un String (pe. “Soltero”) para mostrarlo en un mensaje por pantalla o registrarlo en algún fichero, etc.

Acceso directo al valor

Si queremos acceder directamente al literal que le hemos asociado al valor simplemente debemos utilizar el método ToString() del valor:

EstadoCivil.Soltero.ToString("G")

Esta línea de código nos devolvería el String “Soltero”.

Acceder a la descripción detalla del valor

Si lo que queremos es poder acceder a una información mucho más detallada, deberemos tirar de Reflection. Para empezar deberíamos ampliar la definición de la Enum.

Public Enum EstadoCivil
<Description("Un soltero")> Soltero
<Description("Un casado")> Casado
End Enum

Y la forma de acceder sería la siguiente:

Public Function ObtenerDescripcionEstadoCivil(ByVal estado As EstadoCivil) As String

Dim InformacionEstado As FieldInfo = GetType(EstadoCivil).GetField(estado.ToString("G"))
Dim Descripcion As DescriptionAttribute = InformacionEstado.GetCustomAttributes(GetType(DescriptionAttribute), False)(0)

Return Descripcion.Description

End Function

La llamada a esta función nos daría acceso a la descripción que le hemos asignado.

Etiquetas de Technorati: ,,