Deshabilitas el atributo debug=false cuando pasas tu aplicación Web a producción?

Una buena pregunta verdad? Vamos a analizarla:


<compilation debug=»true/false» />


Que ocurre cuando ponemos el atributo debug=true en nuestra aplicación Web? Pues que el CLR genera información simbólica cada vez que compila nuestras páginas aspx y además deshabilita la optimización del código, esto quiere decir que si no ponemos este atributo a false cuando pasamos nuestra aplicación a producción notaremos que nuestra aplicación es lenta y que consume mas memoria de lo necesario.


Leyendo el blog de Scott , y más en concreto su árticulo Don’t run production ASP.NET Applications with debug=”true” enabled he podido saber que en la versión 2.0 del Framework además los Scripts e imagenes descargadas desde el WebResources.axd handler no son cachedas y eso quiere decir que continuamente serán descargadas por los clientes en cada petición que se realice a una página. Esto puede reducir un poco la experiencia del usuario en cosas como Atlas, controles como TreeView/Menu/Validators, y cualquier otro control de terceras partes que implementan recursos en los clientes.


Muchos cuando lean esto dirán: juer ese atributo no lo conocía y otros sí, pero no se molestaron en buscar que función desempeña y en que puede afectar a una aplicación el ponerle un valor u otro, y seguro que tendrán alguna mala experiencia y sin saberlo hayan buscado soluciones de hardware como ampliar la memoria RAM…


Pues eso, a partir de ahora cuidadín torpedo con el atributo debug.


Salu2.

Clase Base vs Interfaz

Leyendo el libro Programming Microsoft® ASP.NET 2.0 Core Reference(Concretamente en la página 32), Dino Esposito (Un pedazo de máquina [Y]) hace un pequeño comentario al respecto con el que yo estoy totalemente de acuerdo (porque así lo hago día a día en mi trabajo) y es el usar Interfaces o Clases Bases en nuestros desarrollos. Comenta que en las prebetas de ASP.NET 2.0, en la implemetación del modelo basado en proveedores se hacia a través de interfaces pero en la Beta 1 las interfaces fueron remplazadas por Clases Bases ¿Por qúe?

El por qué basicamente es que las interfaces son sólo colecciones lógicas de métodos que solamente contienen la definición de los mismos y no continene nada de código. Una interfaz es buena para encapsular una funcionalidad concreta que puede ser soportada por diferentes tipos (IDisposable, IComparable…) son un ejemplo de ello, mientrás que las clases son más flexibles que las interfaces y soportan versionado, por ejemplo si tu añades un método nuevo a tu clase en la versión 2.0, las clases existentes que derivan seguirán funcionando sin tener que realizar ningún cambio, eso sí, mientras que el nuevo método no se abstracto, mientras que esto mismo en las interfaces no ocurre, pero esto tampoco quiere decir que no se deban usar interfaces nunca y nos pasemos a las clases bases (Al Cesar lo que es del Cesar).

Yo personalmente suelo utilizar Clases Bases (Antes de leer el parrafo de Dino) en mis proyectos para heredar de ellas y las interfaces la implemento cuando necesito utilizar su funcionalidad.


Es mi humilde opinión. espero las vuestras.


Salu2.

Modelo en N Capas ¿Por qué lo usas tú?

Me acaban de llegar las MSDN Flash Newsletter y acabado de leer un artículo de Ted Neward que se llama Arquitectura pragmática Disposición en capas y después de leerlo no me queda muy claro que intenta decir este señor (A lo mejor me equivoco y no he sabido interpretar bien el articulo, que me suele suceder a menudo[+o(]), pues yo no uso el sistema en 3 capas (que no niveles, leeré Patterns of Enterprise Application Architecture, de Martin Fowler para enterarme bien de cual es la diferencia) no porque me lo dijo mi profesor o porque todo el mundo lo hace así, sino porque me parece la mejor manera de estructurar y desacoplar mis aplicaciones Web, ya me facilitan fundamentalmente el mantenimiento de las mismas y la reutilización de varios de esos componentes en otras aplicaciones.


DiceTed Neward que esto implica un coste de rendimiento adicional y le preguntaría ¿Usted cree que es mejor poner todo el código para cargar los datos de una factura en el evento de un button_click?. Luego también hace una comparación, que no la veo mucho sentido y es comparar a un desarrollador UNIX con un desarrollador .NET, está claro que los 2 son desarrolladores, pero creo que las plataformas (fundamentalmente) difieren bastante, además cuando estube desarrollando bajo UNIX con ShellScript, estaban mas relacionadas con operaciones de administración de sistemas y tampoco veo a un desarrollador de UNIX haciendo un shellscript en capas [:P].


Bueno pues esta es mi humilde opinión, espero como siempre críticas, correcciones y opiniones.


Salu2.

Cargando datos en formularios aspx con AJAX y Visual Studio 2003

No todos tenemos la suerte de poder trabajar con VS 2005 y ASP.NET AJAX, ASP.NET AJAX Control Toolkit para facilitar el desarrollo de aplicaciones ricas en cliente y de vez en cuando algún cliente te pide cosas como: «Quiero que cuando el usuario introduzca un código en la caja de texto se carguen una serie de datos en la página…» y piensas: «Si me pide esto para una página, no va a ser la única en la que me lo pidan», con lo cual te pones a pensar en como te montas algo para automatizar esa labor.


Bueno, pues el caso anterior fue mi caso y decidí atacarlo de la siguiente manera:


Primero: Crear una librería javascript para manejar AJAX a pelo (como decía José María Alarcón en algunos de sus post).


Segundo: Crear una página aspx que recepcione esas llamadas y devuelva la info en formato XML:


Aquí uno de los puntos fuertes fue el diseño del XML, en el creo un campo llamado name que corresponde con el nombre del txt a cargar y su valor, para que en el cliente el código javascript sepa a que campos debe cargar cada valor. Se pueden devolver todos los valores que se quiera en el xml (Incluir los campos que se quieran en la SQL, en una llamada a SAP…), ya que el código javascript del cliente se encargará de cargar solo aquellos que existan:

<?xml version=»1.0″ ?>
<response>
<txt name=»CustomerID»>ALFKI</txt>
<txt name=»CompanyName»>CLH</txt>
<txt name=»ContactName»>Maria Anders</txt>
<txt name=»ContactTitle»>Sales Representative</txt>
<txt name=»Address»>Obere Str. 57</txt>
<txt name=»City»>Berlin</txt>
<txt name=»Region»></txt>
<txt name=»PostalCode»>12209</txt>
<txt name=»Country»>Germany</txt>
<txt name=»Phone»>6578</txt>
<txt name=»Fax»>030-0076545</txt>
</response>

Otra cosa importante es que los campos de la página aspx se deben llamar como los de la base de datos anteponiéndoles el prefijo txt. Lo he hecho así para no tener que modificar mucho código en servidor, además que es realmente fácil e intuitivo a la hora de leer el fichero XML y saber que se trata de la estructura de la tabla Customers de la DB Northwind.


Y por último: Crear una función javascript que procese la respuesta XML de la página aspx y cargue los datos:

if (xmlHttp.readyState == 4 || xmlHttp.readyState == ‘complete’)
{
if (xmlHttp.status == 200)
{
objXml = new ActiveXObject(«MSXML2.DOMDocument.3.0»);
objXml.async = false;
objXml.loadXML(xmlHttp.responseText);
rootNode = objXml.documentElement;

if(rootNode.childNodes.length > 0)
{
for (iCont=0; iCont<rootNode.childNodes.length; iCont++)
{
var txt = document.getElementById(‘txt’+rootNode.childNodes(iCont).getAttribute(«name»));
if (txt != null)
{
document.getElementById(‘txt’+rootNode.childNodes(iCont).getAttribute(«name»)).value = rootNode.childNodes(iCont).text;
}
}
}
else
{
var camposTexto = document.forms[0].elements;

for (x = 0; x < camposTexto.length; x++)
{
if (camposTexto[x].type==’text’)
{
document.getElementById(camposTexto[x].name).value = »;
}
}
alert(‘No se han encontrado coincidencias’);
}
}
}


Bueno espero que os haya parecido interesante y espero críticas y comentarios.


Salu2.

If (Aplicacion.EnProduccion == TypeError.Cuelgue || Aplicacion.EnProduccion == TypeError.Casque) return David Salgado

Hoy he asistido al evento organizado por El grupo de usuarios de .NET de madrid que trataba sobre MAD:NUG: SOS: Analizando casques y cuelgues en aplicaciones .NET, y he de decir que ha sido desde mi punto de vista un éxito, tanto por la ponencia de David Salgado (Una pu… máquina) que me ha hecho recobrar la esperanza sabiendo que un desarrollador puede salvar su pescuezo frente a un casque o un cuelgue en un servidor de producción, como por la asistencia (70 y pico personas). Ha comentado Miguel que subirá a la página del grupo www.madriddotnet.com la presentación y David nos ha comentado que intentará preparar unos ejemplo sobre esos volcados de memoria.


Yo por el momento me voy a descargar las Debugging Tools For Windows para empezar a cacharrear.


Salu2.


 

Component Art: Snap for ASP.NET: Gratis!!!

Seguro que muchos de vosotros conocéis las suite de controles para ASP.NET de ComponentArt, pero para los que no, os quería comentar que tienen un control totalmente gratuito para poder utilizar en vuestras aplicaciones ASP.NET (Yo lo he utilizado más de una vez) que es el control Snap.



Para descargarlo debéis registraros con un nombre de usuario y un mail válido.


Un saludo.

Rellenar un formulario creado con Adobe con iTextSharp desde ASP.NET

Otra vez de vuelta con iTextsharp y es que esta libería no deja de sorprenderme. Esta vez necesitaba rellenar un formulario creado con Adobe Acrobat con los datos de un formulario ASP.NET y me puse manos a la obra para crear algo que automatizase esta tarea. Para automatizar esta tarea tenemos que tener en cuenta lo siguiente:


Los campos Textfield del formulario Adobe y los campos TextBox del formulario ASP.NET deben llamarse de igual manera


Es decir, si en Adobe el campo se llama txtValor en ASP.NET será txtValor.


Una vez dicho esto os presento a la función que realiza este proceso:

private void FillPDF(string formName, string pathPDF)
{
MemoryStream _MemoryStream = new MemoryStream();
PdfReader reader = new PdfReader(pathPDF);
PdfStamper stamper = new PdfStamper(reader, _MemoryStream);

foreach (Control c in this.FindControl(formName).Controls)
{
if ((c is TextBox))
{
TextBox txt = (TextBox)c;
foreach (string name in stamper.AcroFields.Fields.Keys)
{
if (name.EndsWith(c.ID + «[0]»))
{
stamper.AcroFields.SetField(name, txt.Text);
}
}
}
}
stamper.FormFlattening = true;
stamper.Close();

HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.ClearHeaders();
HttpContext.Current.Response.ContentType = «application/pdf»;
HttpContext.Current.Response.AddHeader(«Content-Disposition», «inline;filename=recibo.pdf»);
HttpContext.Current.Response.BinaryWrite(_MemoryStream.ToArray());
HttpContext.Current.Response.End();
}



La función basicamento lo que hace es recorrer los controles del formulario ASP.NET y sólo se queda con los TextBox, busca su equivalente en el formulario PDF y cuando lo encuentra establece el valor.


Salu2 y espero que os resulte útil.

Creando un Wrapper para Data Access Application Blocks

Son varias las aplicaciones donde utilizamos DataSet tipados o no y siempre es un poco tedioso estar generando las sentecias CRUD respectivas para cada DataSet, por eso me he decidido a crear un Wrapper para DAAB de las Enterprise Library que encapsule esta funcionalidad y automatice el proceso de actualización de la base de datos. A continuación os muestro el código;


1. Cargamos el DataSet:

public void FillDataSet(DataSet dsInfo, string sql, string [] tablas)
{
_db.LoadDataSet(CommandType.Text,
sql,
dsInfo,
tablas);
}
2. Creamos la función que actualizará la base de datos:
public void UpdateDataSet(DataSet dsInfo)
{
DbCommand insertCommand = null;
DbCommand insertCommandParameters = null;
DbCommand deleteCommand = null;
DbCommand deleteCommandParameters = null;
DbCommand updateCommand = null;
DbCommand updateCommandParameters = null;

StringBuilder sbConstructor1 = new StringBuilder();
StringBuilder sbConstructor2 = new StringBuilder();
StringBuilder sbConstructor3 = new StringBuilder();

string sSQLInsert = «INSERT INTO {0} VALUES ({1})»;
string sSQLUpdate = «UPDATE {0} SET {1} WHERE {2}»;
string sSQLDelete = «DELETE FROM {0} WHERE {1}»;

foreach (DataTable dtTable in dsInfo.Tables)
{
ArrayList arrLstPrimaryKeys = new ArrayList(dtTable.PrimaryKey);

if (arrLstPrimaryKeys.Count < 1)
{
throw new Exception(String.Format(«Table: {0} not contains Primary Keys», dtTable.TableName));
}

insertCommandParameters = _db.GetSqlStringCommand(sSQLInsert);
deleteCommandParameters = _db.GetSqlStringCommand(sSQLDelete);
updateCommandParameters = _db.GetSqlStringCommand(sSQLUpdate);

foreach (DataColumn dtColumn in dsInfo.Tables[dtTable.TableName].Columns)
{
_db.AddInParameter(insertCommandParameters, dtColumn.ColumnName, GetDbType(dtColumn.DataType.ToString()), dtColumn.ColumnName, DataRowVersion.Current);
_db.AddInParameter(deleteCommandParameters, dtColumn.ColumnName, GetDbType(dtColumn.DataType.ToString()), dtColumn.ColumnName, DataRowVersion.Current);
_db.AddInParameter(updateCommandParameters, dtColumn.ColumnName, GetDbType(dtColumn.DataType.ToString()), dtColumn.ColumnName, DataRowVersion.Current);

sbConstructor1.Append(String.Format(«@{0},», dtColumn.ColumnName));

if (arrLstPrimaryKeys.Contains(dtColumn))
{
sbConstructor2.Append(String.Format(«{0} = @{1},»,
dtColumn.ColumnName,
dtColumn.ColumnName));
}
else
{
sbConstructor3.Append(String.Format(«{0} = @{1},»,
dtColumn.ColumnName,
dtColumn.ColumnName));
}
}
sSQLInsert = String.Format(sSQLInsert, dtTable.TableName,
sbConstructor1.ToString().Substring(0, sbConstructor1.Length – 1));

sSQLDelete = String.Format(sSQLDelete, dtTable.TableName,
sbConstructor2.ToString().Substring(0, sbConstructor2.Length – 1));

sSQLUpdate = String.Format(sSQLUpdate, dtTable.TableName,
sbConstructor3.ToString().Substring(0, sbConstructor3.Length – 1),
sbConstructor2.ToString().Substring(0, sbConstructor2.Length – 1));

insertCommand = _db.GetSqlStringCommand(sSQLInsert);
SetParameters(ref insertCommand, ref insertCommandParameters);

deleteCommand = _db.GetSqlStringCommand(sSQLDelete);
SetParameters(ref deleteCommand, ref deleteCommandParameters);

updateCommand = _db.GetSqlStringCommand(sSQLUpdate);
SetParameters(ref updateCommand, ref updateCommandParameters);

int rowsAffected = _db.UpdateDataSet(dsInfo,
dtTable.TableName,
insertCommand,
updateCommand,
deleteCommand,
UpdateBehavior.Transactional);
}
}



Muchos os preguntaréis: ¿Por qué utiliza 6 comandos? ¿Está loco?, bien la explicación es sencilla:


Como la Query se va creando dinámicamente, hasta el final no tengo disponible todos los parámetros que formarán la query, entonces sino utilizase esos comandos auxiliares la colección de parametros estaría vacía puesto que al llamar al método GetSqlStringCommand con la sentencia sin parámetros no sabrá los que tiene. Entonces pensé, sino utilizo comandos auxiliares tendré que hacer otro bucle para sacar nuevamente los parámetros, simplemente por eso (Espero haberme explicado)


3. Crear un método que pase los parámetros del comando auxiliar al principal:

private void SetParameters(DbCommand command, DbCommand command2)
{
ArrayList arrLstParameters = new ArrayList(command2.Parameters);
command2.Parameters.Clear();
command.Parameters.AddRange(arrLstParameters.ToArray());
}

4. Crear un método que nos convierta los tipos de Datos de .NET (System.) a los tipos SQL System.Data

private DbType GetDbType(string stype)
{
switch (stype)
{
case «System.String»:
return DbType.String;
case «System.Int16»:
return DbType.Int16;
case «System.Int32»:
return DbType.Int32;
case «System.Int64»:
return DbType.Int64;
case «System.UInt16»:
return DbType.UInt16;
case «System.UInt32»:
return DbType.UInt32;
case «System.UInt64»:
return DbType.UInt64;
case «System.Decimal»:
return DbType.Decimal;
case «System.Double»:
return DbType.Double;
case «System.DateTime»:
return DbType.DateTime;
case «System.Guid»:
return DbType.Guid;
case «System.Boolean»:
return DbType.Boolean;
case «System.Byte»:
return DbType.Byte;
case «System.SByte[]»:
return DbType.Binary;
case «System.SByte»:
return DbType.SByte;
case «System.Single»:
return DbType.Single;
case «System.Object»:
return DbType.Object;
default:
return DbType.Object;
}
}


Y con esto estaría todo listo, pero para DataSet no tipados necesitáis crear la primary key antes de llamara la método UpdateDataset ó saltará la excepción diciendo que no existe clave primaria.

Seguro que este código se puede mejorar, por eso lo he posteado, para que entre todos los que se apunten podamos crear una librería para reutilizarla en nuestras aplicaciones.

Espero muchas críticas, porque eso significará que os interesa.


Código
Script de la base de datos

Salu2.

II Conferencia Española de Usuarios de SharePoint

Acabo de llegar de la conferencia y no quiero perder tiempo en contaros mi experiencia en las 3 sesiones a las que he asistido:

  1.  MOSS 2007 & Office 2007 Better Together: En español como se integran Office 2007 y Sharepoint 2007, que me ha sorprendido muchísimo los workflows sobre los documentos de las bibliotecas, el poder firmar documentos desde Office y Groove 2007, la verdad una integración perfecta.
  2. Site Model, Security and Management in 2007: Un poco aburrida pero en ella he conocido que en Sharepoint 2007 la seguridad se puede basar en la seguridad por formularios de ASP.NET 2.0 con el modelo de proveedores [:O]
  3. Personalizando Sites con SharePoint Designer 2007: Impartida por Carlos Segura, que me ha sorprendido bastante como se puede personalizar SharePoint.

La verdad es que ha sido un evento muy interesante aunque he trabajado muy poco con SharePoint me he podido hacer con una idea de lo que se avecina.

Salu2.