sábado, 3 de noviembre de 2012

Generacion de inserts con Sql Management Studio

Generacion de inserts Sql Management Studio


¿Qué queremos hacer?

Lo que intentamos hacer es crearnos los comandos de insert de los registros de una tabla determinada.

¿Cómo lo hacemos?

Decidí escribir esta simple entrada porque es una funcionalidad tan útil como oculta. Sabiendo incluso que existe la posibilidad de hacerlo no es fácil encontrar donde. Así que vamos paso por paso.


1.Abrir y autenticarse al Sql Management Studio

2.Acceder a la carpeta que muestra las bases de datos

3.Ir por menú contextual a la opción de menú Generate Script como indica la siguiente imagen



4.Next a la pantalla de bienvenida del wizard




5.Selecciona la base de donde necesitas generar los scripts y obviamente luego next



6. Aca viene lo importante, la siguiente pantalla muestra una serie de opciones de configuración que no vamos a ver en detalle en este post y ademas la mayoría se puede deducir simplemente analizando el nombre. Ahora hay una propiedad que nos interesa especialmente, es la propiedad Script Data , la misma deberá ser true si queremos generar los insert.



7.Luego el wizard nos ofrece los tipos de objetos a generar, en mi humilde base de datos, creada para esta entrada solo tengo tablas, selecciono entonces esa opción.




8.El wizard esta ahora interesado en saber cual o cuales tablas quiero generar. Indico entonces la que me interesan





9.Siguiente paso nos pregunta donde queremos dejar el script, básicamente si queremos generar un archivo, si queremos poner el script en el portapapeles o si lo dejamos en una query window de nuestro sql management studio




10.Ahora sumarizado de las opciones tomadas durante la ejecución del wizard y finish para la generación del efectiva del script








11.Finalmente el script generado donde se pueden ver los comandos de script perfectamente generados.





Espero haya sido de utilidad.

miércoles, 17 de octubre de 2012

DB Stress Generator

El DB Stress generator es un software que programe yo para simular algunas situaciones de stress en la base de datos.
A la fecha no es un software comercial, profesional ni mucho menos pero me ayuda bastante y no encontré en el mercado nada que hiciera lo que DB Stress Generator hace y como el lo hace.
Muchas veces desarrolladores simulan carga de trabajo generando consultas desde el administrador de la base de datos pero esto es una sola conexión  una sola instancia. Yo estaba buscando la manera de generar n instancias para ejecutar repetidamente una consulta determinada pero ademas me interesaba mucho que los parámetros fueran random y que oscilaran.

Importante aclarar que solo fue probado con SqlServer 2005/2008

Modo de uso

Configurar la conexión, utiliza ADO para conexión, por tanto la maquina debe tener instalado MDAC.






















Configurar driver, host y base según corresponda





2.Conectar y colocar usuario y contraseña

























Una vez conectado puede presionar el botón “New Stress panel” un nuevo panel aparecerá para realizar el test.




Escriba la query en el editor de texto de la derecha colocando los parámetros de la consulta con el anteponiendo carácter “:” . Puede también abrir un archivo con la consulta previamente guardada.
Presione luego el botón Refresh parameters para que se generen automáticamente los parámetros de la consulta. Luego escriba el valor del parámetro o seleccione alguna de las opciones de random que se despliegan con el combo donde se indica el valor del parámetro  Espero que en futuras versiones se permita traer una valor aleatorio de una columna de una tabla.

Configurada la consulta y los parámetros se debe indicar el nivel de stress indicando cantidad de conexiones y ejecuciones en cada conexión:

#Threads: indica la cantidad de hilos, cada hilo tendrá una conexión a la base de datos.

Sleep between initial exe...: delay en milisegundos para la primera ejecución del thread, la idea es evitar sincronismos de ejecución

Iteration per Thread: cantidad de ejecuciones por hilo

Delay between iterations: espera entre ejecuciones dentro del hilo.

Configurado todo esto darle “Play” para que comience la ejecución.

Para verificar en cualquier momento la cantidad de hilos activos puede ir al panel inferior, solapa Statistics y ver Active Threads, cuando ese valor este en 0 indica que el test finalizo completamente.

Se puede crear varios paneles de stress simultáneamente!!

Aquí el link de descarga de la aplicación:


https://skydrive.live.com/redir?resid=E40DE9FCAF976AD3!679&authkey=!AO6GrLeGAFEOzkM











jueves, 4 de octubre de 2012

Blackberry browser y ASP.NET

Problemas con ASP.NET y navegadores Blackberry

Y si, me sorprendió que me aplicación que tan bien funcionaba, probada en varios navegadores, presentaba un extraño comportamiento ejecutada sobre un browser de blackberry.
Se presentaban problemas con los postback de los controles, los mismos parecían no ejecutarse.

El problema es fácil de solucionar aunque molesto a esta altura del partido.
La solución requiere de la registración del browser de blackberry en ASP.NET, realmente muy molesto.

Cree un archivo con nombre Blackberry.browser y copie el siguiente texto dentro:

<browsers>
  <browser id="BlackBerry" parentID="Default">
    <identification>
      <userAgent match="BlackBerry(?'model'\d+)/(?'version'((?'major'\d+).
(?'minor'\d+).(?'other'\d+)))" />
    </identification>

    <capabilities>
      <capability name="browser"             value="BlackBerry" />
      <capability name="isMobileDevice"      value="true" />
      <capability name="javascript"          value="true" />
      <capability name="ecmascriptversion"   value="1.3" />          
      <capability name="version"             value="${version}" />
      <capability name="majorVersion"        value="${major}" />
      <capability name="minorVersion"        value="${minor}" />
      <capability name="supportsCss"         value="true" />
      <capability name="frames"              value="false" />
      <capability name="cookies"             value="true" />
    </capabilities>
  </browser>
  
</browsers>


Salvar el archivo en el siguiente directorio:


Ahora el siguiente paso es registrar el browser de blackberry en ASP.NET. Para registrarlo deberá utilizar la aplicación aspnet_regbrowsers.exe con el parámetro -i, como ejemplo:
C:\\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_regbrowsers.exe -i
El path puede cambiar en virtud del path de instalación del SO.

Realmente es muy molesto tener que hacer esta registración previa al deploy, como contrapartida una vez realizada las aplicaciones ejecutadas sobre navegadores blackberry pasan a comportarse normalmente
































SqlServer, plan de consulta y optimización (Tuning) (3)

SqlServer, plan de consulta y optimización (Tuning) (3)


El problema

En la entrada pasada vimos al sql server meter la pata groseramente al intentar estimar la cantidad de registros a recibir en una consulta con datos correlacionados.
Quedo en evidencia el problema y se dijo que dado lo grosero del error dificilmente el motor pudiera escoger un plan optimo de consulta.
El problema radica en que el sql server no entiende que existe relación entre dos columnas, hay múltiples ejemplos de datos correlacionados, edad y altura, peso y tamaño, calidad y precio etc.
La situación es que el sql server calcula la probabilidad de que se cumpla una condición múltiple basicamente como el producto de ambas, esto es correcto y es lo que aprendimos en probabilidad y estadista, la probabilidad de que ocurran dos eventos es el producto de la probabilidad de que ocurra cada uno de ellos.
Lo que también nos enseñaron es que los experimentos deben ser independiente y no deben afectarse mutuamente cosa que aquí no pasa. Difícilmente encontremos una persona con cintura estrecha, que mida menos de 1.6 metros y que pese mas de 100 kg, las tres variables están correlacionadas.
Podemos encontrar ejemplo donde el motor descarta un indice por errores de estimación en columnas correlacionadas, no es tan fácil encontrar ejemplos donde el motor escoja una estrategia de join equivocada.

Hagamos un experimento con la siguiente consulta :



declare @marca char(20)
declare @Modelo char(20)
declare @Motor char(20)
declare @Origen char(20)
declare @Ranking int


set @marca='VW'
set @Modelo='GOL'
set @Motor='VW 1.6'
set @Origen='BRASIL'
set @Ranking=1


select *
from Vehiculo as v1 inner JOIN Vehiculo as v2
On v1.IdVehiculo=v2.IdVehiculo  inner  JOIN Vehiculo as v3
On v2.IdVehiculo=v3.IdVehiculo inner join Vehiculo as v4
On v3.IdVehiculo=v4.IdVehiculo
Where v1.Marca=@marca and
v1.Modelo=@Modelo and
v1.Motor=@Motor and
v1.Origen='BRASIL' and
v1.Ranking=1





La ejecución de la consulta otorga la siguiente información,






La estrategia de join del motor es "nested loop", el motor piensa que va a recibir pocos registros y por tanto escoge esta estrategia, seguramente la mas conveniente con conjuntos de 1500 registros (Los que estima recibir).
La realidad demuestra que se reciben 200000, y el join es por IdVehiculo, indice cluster, por tanto los conjuntos están ordenados.
Se ve marcado en rojo la enorme diferencia que existe entre lo que el sql espera recibir y lo que verdaderamente recibe


La solución ( MERGE JOIN )

Existe un mecanismo de join llamado MERGE_JOIN es un mecanismo que es eficiente solo con conjuntos ordenados de determinado tamaño. Si forzáramos MERGE JOIN en conjuntos no ordenados el sql los tendría que ordenar previamente y generalmente esto no da buenos resultados.
No es este el caso donde ex profeso hemos hecho el join por el indice cluster.
Vemos también que no es la opción elegida por nuestro Sql Server, a pesar de que los conjuntos están ordenados la cantidad de registros que espera obtener no a merita el uso de Merge join.
Forzamos entonces al sql a utilizar MERGE_JOIN a sabiendas de que el motor tiene enormes problemas para predecir la cantidad de registros que cumplen la condición.


declare @marca char(20)
declare @Modelo char(20)
declare @Motor char(20)
declare @Origen char(20)
declare @Ranking int


set @marca='VW'
set @Modelo='GOL'
set @Motor='VW 1.6'
set @Origen='BRASIL'
set @Ranking=1


select *
from Vehiculo as v1 inner MERGE JOIN Vehiculo as v2
On v1.IdVehiculo=v2.IdVehiculo  inner MERGE  JOIN Vehiculo as v3
On v2.IdVehiculo=v3.IdVehiculo inner MERGE join Vehiculo as v4
On v3.IdVehiculo=v4.IdVehiculo
Where v1.Marca=@marca and
v1.Modelo=@Modelo and
v1.Motor=@Motor and
v1.Origen='BRASIL' and
v1.Ranking=1




Observemos y comparemos el rendimiento en uno y otro caso


















Mas allá de esta medición puntual en varias pruebas observe que el tiempo de ejecución se reduce a la mitad, en fin los datos correlacionados hicieron desastres con las estimaciones y destruyeron los intentos del motor por obtener un buen plan de consulta.

Consideraciones finales

Para cerrar, mucho cuidado, el problema se da bajo las condiciones actuales de la tabla, pero atención  mas registros, un cambio en la distribución y correlación de los datos, o un cambio en la estructura invalidaría lo expuesto, por eso precaución al forzar el plan de consulta



sábado, 29 de septiembre de 2012

SqlServer, plan de consulta y optimización (Tuning) (2)

SqlServer, plan de consulta y optimización (Tuning) (2)


Datos correlacionados

En la entrada anterior hablamos sobre cuando puede fallar el optimizador, que podría hacer fallar el path suboptimo pensado por el motor.
Se me ha hecho muy fácil engañar algunos motores y hacerles meter la pata, con sql server se complica un poco mas y las situaciones por donde podemos hacer "pinchar" al optimizador son pocas. Una de ellas es tener datos correlacionados.
Para el ejemplo de la entrega vamos a manejarnos con una tabla de vehiculos con campos marca, modelo y motor, la tabla seria la siguiente:

Tabla


CREATE TABLE [dbo].[Vehiculo](
[IdVehiculo] [int] IDENTITY(1,1) NOT NULL,
[Dominio] [char](20) NOT NULL,
[Observacion] [text] NOT NULL,
[Marca] [char](20) NOT NULL,
[Modelo] [char](20) NOT NULL,
[Motor] [char](20) NOT NULL,
 CONSTRAINT [PK_Vehiculo] PRIMARY KEY CLUSTERED
(
[IdVehiculo] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

En la misma insertamos aproximadamente 100000 VW GOL POWER con motor VW 1.6, insertamos también alrededor de 50000 de otros vehículos, de otras marcas y modelos 


¿Porque es difícil que se equivoque el sql server?


El sql server maneja estadísticas para tablas con mas de 500 registros con una buena precisión, el sql server sabe en muchos casos, y por ejemplo para nuestra tabla de vehículo  cuantos FIAT, cuando VW o cuantos FORD hay, sabe también cuantos GOL POWER hay y cuantos PALIO (Las estadisticas las fue generando a medida que se ejecutaron selects con condiciones de selección aplicadas a los campos).
Si pidiéramos al sqlserver que nos retorne todos los vehículos PALIO el mismo sabría con buena precisión cuantos hay, escogería una estrategia de búsqueda adecuada.

Lo que no sabe y no puede saber es la correlación que existe entre, por ejemplo, el modelo GOL POWER y la marca VW, hay una relación estricta entre marca y modelo y sí alguien podría decir,

-El problema es que la base esta mal diseñada, deberías tener tabla de modelos y tabla de marcas, ambas relacionadas

Si es cierto para este caso, la realidad es que las bases con las que nos toca trabajar no siempre están bien diseñadas y ademas hay casos donde la relación es muy estrecha pero no definitiva.
Por ejemplo si preguntáramos Franceses que hablan Francés obtendríamos a casi todos los Franceses, pero no necesariamente a todos.
En cualquier caso si pidiéramos al SqlServer que nos retorne todos los VW que son GOL POWER es probable que lo pongamos en un aprieto a la hora de estimar la cantidad de registros que retornaría la consulta, una mala estimación de este tipo desencadena una serie de malas decisiones a la hora de elegir la estrategia de join, la estrategia de búsqueda etc.

En las ultimas versiones de sql server (2008 o superior) existe la posibilidad de explicarle al motor la situación mediante indices especiales, es una opción valida pero algo oscura.
Me da la sensación que esos indices tan específicos pueden generar problemas entre implementaciones o durante el ciclo de vida de la base de datos si no se tiene control de su existencia y de su carácter especifico.

Miren todos como se equivoca el sql server



En la siguiente imagen se puede ver la pésima estimación que hace el sqlserver .






















Si examinamos el plan de consulta el mismo espera recibir algo así como 1125 filas, veamos que fue lo que efectivamente recibimos luego de ejecutar la consulta...















La consulta devolvió 100001 registros, la diferencia es importante en una tabla con 150000 registros, y como se menciono antes ya no se puede esperar mucho del plan de consulta después de semejante error.

En la próxima entrada vamos a ver como este error degenera en la perdida de rendimiento de la consulta.




viernes, 28 de septiembre de 2012

SqlServer, plan de consulta y optimización (Tuning) (1)

SqlServer, plan de consulta y optimización (Tuning) (1)


De mi parte, siempre di la derecha al plan de consulta que el motor escogía, si el rendimiento no es el esperado sera porque diseñe mal la base o porque me faltan indices o directamente porque el software esta trabajando de manera incorrecta. Tengo desarrollos completos, algunos de tiempo real con bastante exigencia al motor donde la cantidad de planes de consulta forzados en todo los módulos del software es 0.

Hace mucho años trabajando para una compañía bastante grande observaba horrorizado como se había instalado la cultura de crear la consulta e indicar porque indice ir.
Meses después tuve mi revancha cuando nos mandaron a todos a hacer un curso de tuning de sql, y si no fue lo primero, lo segundo que dijo el instructor fue “No fuercen el plan de consulta, es muy probable que no tengan el conocimiento y la cantidad de información necesaria para hacerlo”
Los mismo dice microsoft en su msdn, traducción mediante “Típicamente sqlserver encuentra el mejor plan posible, no lo cambie salvo que sea un experto programador o experto administrador de base de datos”.

Probablemente todos nos sentimos expertos programadores, sea así o no, con eso no alcanza. Ademas de la experiencia se necesita conocimiento del modelo, de los indices y de la distribución y naturaleza de la información, en la mayoría de los casos conocer todo esto es poco probable y casualmente el sqlServer (Correctamente configurado) en sus estadísticas tiene esta información con un nivel de confiabilidad superior al 80%.

Entonces si no hay posibilidades de mejora y el motor es perfecto ¿Por que existen los hints?
Bueno de hecho el motor sí se equivoca, poco, pero se equivoca con especial énfasis en las siguientes situaciones:


  1. Datos correlacionados
  2. Poda por heuristica
  3. Bug optimizador
  4. Estadísticas no actualizadas


¿Entonces modifico o no el plan de consulta?

Entiendo que modificar el plan de consulta es la solución mas tentadora a un problema de rendimiento, generalmente parece ser también la solución menos probable hasta puede ofrecer mejoras marginales en muchos casos.
En posteriores entradas voy a intentar desgranar situaciones donde malos diseños, bugs del optimizador, datos correlacionados, podas o malas estadísticas puedan hacer que el sql se equivoque groseramente.




martes, 25 de septiembre de 2012

Desarrollo WAP en Visual Web Developer 2010


Primero, si buscan los templates para desarrollo wap en visual studio 2010 notaran que ya no existen. Los componentes aun vienen pero la gente de microsoft argumenta que ya no tiene sentido incorporar al IDE los mismos, o disponibilizar templates dado que es un tipo de desarrollo obsoleto.
Cualquier dispositivo móvil moderno soporta html básico, el soporte para desarrollo WAP no parece ser una necesidad.
Esto es casi cierto, me ha tocado desarrollar una pequeña aplicación para dispositivos móviles, y los dispositivos móviles no soportaban html, solo WAP.

¿Como podemos encarar este desarrollo?
¿Sera necesario instalar un viejo Visual Studio 2003?
No necesariamente, veamos los pasos para un efectivo desarrollo.

1.Descargar templates



Primero tenemos que descargar los templates para forms WAP, estos template pueden ayudar, se pueden descargar de aquí:


Luego descomprimir y seguir las instrucciones, las cuales básicamente dicen de colocar los templates en la carpeta

[My Documents]\Visual Studio 2010\Templates\ItemTemplates\Visual C#

o si prefiere

C:\Users\FulanoDeTal\Documents\Visual Studio 2010\Templates\ItemTemplates\Visual Web Developer

2.Si todo fue bien cree una Aplicación WEB ASP.NET vacía




 3.Luego Agregar->Nuevo elemento y seleccione Mobile Web Form.




Ahora tenemos nuestro mobile web form, el cual vamos a editar y efectivamente puede ser accedido desde equipos que solo manejan WAP.

La mala noticia:

No estará disponible el diseño visual.




La buena noticia:

Tampoco es estrictamente necesario, la interfaces WAP son tan pobres y la cantidad de componentes tan pocos que no debería quitarnos el sueño no tener el tan cómodo diseñador visual, especialmente para quienes no son totalmente del palo web.

4.Diseñar


Bien, como dijimos no tenemos diseñador visual, ¿Como hacemos?
Bueno ayudarse con el asistente de VS para ir armando el formulario, es muy similar a un formulario html. Colocar el carácter “<” escribir “mob” y alli aparecerán las opciones de componente, como es de esperar muy pocos pero suficientes para lo que tenemos que hacer, que también seguramente sera muy simple (No se le puede pedir mucho a una aplicación WAP).




Recordar agregar las referencias al asembly con los componentes mobile...puedes usar con copia local si te trae problemas



El código abajo muestra como quedaría el formulario. Observe que he creado dos textbox, los he identificado como Runat=”Server”, les he puesto un ID como corresponde y otras propiedades adicionales cuando fue necesario.
También puse un command, algo asi como el botón de asp.net en webforms, he declarado también y codificado la función DoClick y se la he asignado al OnClick del comando. Si no me he equivocado con los nombre cuando se haga click sobre el boton se deberia ejecutar mi evento DoClick. A no tener miedo de usar los textbox del lado servidor, ya están disponibles como indica el código.


<%@ Page Language="C#" AutoEventWireup="true" Inherits="WAPTEST.WAPLogin" Codebehind="WAPLogin.aspx.cs" %>
<%@ Register TagPrefix="mobile" Namespace="System.Web.UI.MobileControls" Assembly="System.Web.Mobile" %>

<html xmlns="http://www.w3.org/1999/xhtml" >
<body>
<mobile:Form id="Form1" runat="server">
Usuario: <mobile:TextBox Runat="server" ID="tbUsuario"></mobile:TextBox>
Password: <mobile:TextBox Runat="server" ID="tbPassword" Password="True"
></mobile:TextBox>
<mobile:Command ID="btnAceptar" Runat="Server" OnClick="DoClick">Aceptar</mobile:Command>
</mobile:Form>
</body>
</html>



  




using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.Mobile;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.MobileControls;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace WAPTEST
{
public partial class WAPLogin : System.Web.UI.MobileControls.MobilePage
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }
    protected void DoClick(object sender, EventArgs e)
    {
      //Codificar aca....
      //Se tiene acceso a los controles sin problemas por ej.  
      //string s = tbUsuario.Text; es perfectamente posible. 

    }



}
}


Aca el resultado en un navegador


Aca en un navegador wap






















miércoles, 20 de junio de 2012

WebServices y certificados vencidos

Perdí varias horas de pruebas y repruebas por un problema que ya había tenido pero que tarde días en recordar. Según algunos informes, internet paso a ser una extensión de nuestra memoria y en general muchas situaciones se olvidan rápidamente para dejar lugar a cuestiones mas inmediatas.
El problema era la imposibilidad de consumir un servicio https y que tenia un certificado vencido. Tarde mucho tiempo en recordad la solución como para dejarla pasar y no registrarla en el blog.
La cuestion se resuelve seteando una simple opcion que no esta tan visible ni es tan accesible asi que aqui el codigo con la explicacion.

var
  r:Integer;
  msg:string;
  ss:ServiceSoap;
  RIO : THTTPRIO;
begin
  { Place thread code here }
  //Creo un componentes http RIO
  RIO := THTTPRIO.Create(nil);
//Aca lo importante, colocar la opcion soIgnoreInvalidCerts para que haga caso omiso de certificados  //vencidos y que siga trabajando
RIO.HTTPWebNode.InvokeOptions :=
            [soPickFirstClientCertificate ,soIgnoreInvalidCerts, soAutoCheckAccessPointViaUDDI];

  //Pasar el componentes THTTPRIO como parametro, y listo funciona!!
  ss :=GetServiceSoap(false, defURL, RIO);

  try

    r := ss.MiMetodo('Hola mundo')

...................
...................


domingo, 29 de abril de 2012

Carga dinámica de paquetes y reflexión (Una agradable sorpresa)

Introducción

Hacia años que no tenia que meterme con las características de reflexión de delphi. Había trabajado bastante en .Net pero en Delphi poco y nada. Sabia que en la version 2010 de delphi se había evolucionado mucho en este sentido pero no sabia cuanto y a priori imagina las restricciones obvias de un lenguaje compilado.
La reflexión seria como la capacidad que tiene nuestro compilador de analizar y accionar sus elementos en tiempo de ejecución.
En lo que nos toca en esta entrada apuntamos a analizar una clase totalmente desconocida para investigar sus método y eventualmente ejecutarlos.

¿Para que puede servir?

Bueno, seguramente habrá mas casos el aqui expuesto, lo que aquí intentamos es simplemente cargar una serie de bpls, investigar sobre las clases y métodos que las mismas contienen y exponerlos. ¿Para que puede servir esto?, en mi caso particular lo que quiero es que se carguen las bpls a modo de funcionalidad agregada, luego quien configura el sistema determina que método se ejecuta según condiciones, pero los métodos disponibles se cargan dinamicamente de las bpls con las que se compra el sistema. Para mejor, se puede ir agregando bpls con mas funcionalidad sin afectar el core del producto.
Al decidir el configurador/analista que se ejecuta y cuando yo tendré que buscar y ejecutar un método mediante su nombre y para eso necesito las capacidades de reflexión que encuentro mas que aceptables en la versión actual de Delphi.
Los ejemplos de abajo han sido pelados de todas las cuestiones de bases de datos que tiene que ver con guardar los métodos disponibles y recuperar el método a ejecutar. Solo nos concentramos en lo importante: Como bucear en las bpls y como instanciar una clase y ejecutar un método por sus nombre.

¿Como cargar las bpls?

El siguiente código revisa una carpeta determinada y bpl que encuentra bpl que carga por medio del LoadPackage.


 DynamicPackageNames :=  TStringList.Create;
 sda := TDirectory.GetFiles(ExtractFilePath( Application.ExeName)+'PlugIns\', '*.bpl');

  for s in sda do begin
      hndl  := LoadPackage(s);
      DynamicPackageNames.Add(s);
  end;


Ahora lo bueno, analizo las clases, pero solo lo hago para los librerias cargadas de las bpls.

....

c : TRttiContext;
m : TRttiMethod;
c : TRttiContext;
t : TRttiType;
p:TRttiPackage;
...
for p in c.GetPackages do begin
    //Solo las cargadas de las bpls...
    if DynamicPackageNames.Find(p.Name, Index) then begin
        //Recorro las clases
         for t in p.GetTypes do begin
           //Recorro los metodos
           for m in t.GetMethods do begin
              //Esto elimina metodos de clases anteriores, como por ejemplo de la
              //Classe TComponent
              if t.Name=m.Parent.ToString then begin
                //Recorro los parametros de los metodos
                for param in m.GetParameters do begin
                end;
              end;
            end;
          end;
        end;
      end;
    end;  


Con el código anterior, se puede bucear hasta el nivel deseado en las clases presentes en la bpls cargadas dinamicamente.

Cuando encuentro lo que busco, ¿Como lo ejecuto?

 c := TRttiContext.Create;
 ty := c.FindType('MiClase') as TRttiInstanceType;
 if Assigned(ty) then begin
   //Encontro la clase que buscaba
   m := ty.GetMethod('MiMetodo');
   if Assigned(m) then begin
      //Encontro el metodo que buscaba
      //Construyo una instancia de la clase en cuestion
      SL := ty.GetMethod('Create').Invoke(ty.MetaclassType,[Self]);
      //Ejecuto el metodo
      m.Invoke(SL,  [param1, param2, paramx]);
      //Libero el objeto creado
      SL.AsObject.Free;
   end;
end;


Espero que el código aquí expuesto sea de utilidad ya que no se encuentra con facilidad documentación sobre estos temas. Me parece mas importante el código expuesto que explicar todos los casos donde se puede utilizar la reflexión.




martes, 6 de marzo de 2012

IOUtils

Introducción
Esta entrada va a ser sumamente corta, estoy preparando algunas cosas mas complejas como carga dinámica de paquetes o reflection pero eso me va a llevar algo mas de mi escaso tiempo.
Mirando el código de un amigo hace unos días, amigo que tiene varios años de experiencia en desarrollo Delphi note algo de desconocimiento sobre las capacidades incluidas dentro de la herramienta en las ultimas versiones.
Se veía mucha programación con le tema de codificación y decodificación de fechas y también mucho código a mano para lo que voy a mostrar ahora, y lo muestro porque justamente lo tuve que hacer para un proyecto en el trabajo y rápidamente me acorde de lo largo y complicado que lo había hecho mi amigo.

El objetivo

El objetivo es sencillo, borrar, mover o lo que sea con algunos archivos que cumplen determinada característica. En este caso particular es simplemente borrar archivos viejos.

Las Herramientas

Vieja forma, sacada de internet y que casi todos los desarrolladores Delphi hemos usado para encontrar archivos...

 procedure FileSearch(const PathName, FileName : string) ;
 var
   Rec : TSearchRec;
   Path : string;
 begin
   Path := IncludeTrailingPathDelimiter(PathName) ;
   if FindFirst (Path + FileName, faAnyFile - faDirectory, Rec) = 0 then
    try
     repeat
      ListBox1.Items.Add(Path + Rec.Name) ;
     until FindNext(Rec) <> 0;
   finally
    FindClose(Rec) ;
   end;
 end; 

Nueva Forma....busco archivos, si son viejos los borro

  cachePath := ExtractFilePath( Application.ExeName ) + 'AuxFiles\';
  for s in TDirectory.GetFiles(cachePath) do begin  //<-Observar la forma de recorrer el array "For in"
    if TFile.GetCreationTime(s)<IncDay(now, -3) then begin
      TFile.Delete(s);
    end;
  end;

En las próximas entradas quiero mostrar como cargar packages de manera dinámica y lo que veo mas importante, poder crear clases y llamar a métodos sin siquiera conocerlos previamente.
Nos vamos a ayudar con el mejorado (En delphi 2010) RTTI para cumplir con el objetivo.