lunes, 18 de febrero de 2008

MediaMonkey extendido con C#


Es un opensource project que creé y subi a Google code, para probar. Aquí está el link https://mediamonkey-cs-tagger.googlecode.com/

Resultados de división en SQL: No se traen decimales por defecto?

Sujeto A: oiga una pregunta estupida de SQL
WarNov: a ver
Sujeto A:
DECLARE @Variable Float
SET @Variable = 170/100
SELECT @Variable

porque no me muestra decimaleS??
Sujeto A: alo??

WarNov: Hágala entonces más breve:
DECLARE @Variable FloatSET @Variable = 170.0/100.0
SELECT @Variable

Sujeto A: chambon
WarNov: CHAMBON SU * ahí no tiene nada de chambón
si ud no puede controlar los valores, entonces use:

DECLARE @Variable Float
SET @Variable = cast(170 as float)/cast(100 as float)
SELECT @Variable

contenta?

miércoles, 13 de febrero de 2008

Closures? Kejeso?

Tomar el framework 2.0, usar sus delegados y métodos anónimos, generar un productor de delegados con un parámetro de configuración y se obtendrá un closure.

Pero... what for?

Ha tenido por ejemplo que calcular un impuesto como el IVA que puede variar según el artículo?
Por ejemplo para una crema dental es de 16% pero para un auto es mayor al 20%. Let's say 25% para el ejemplo.

Entonces el programador bien juicioso se hace el siguiente método:

decimal IvaCalc(decimal tax, decimal amount);

Así pues siempre que uno vaya a llamar al método tiene pasar ambos parámetros:

decimal imp1 = IvaCalc(25d, 25800d);
decimal imp1 = IvaCalc(16d, 4d);
decimal imp1 = IvaCalc(16d, 5800d);

etcétera

Entonces se puede optar por hacer dos métodos... uno por cada tipo de IVA.
Pero si son 10 tipos de IVA hará 10 métodos?

Entonces opta por hacer un objetico calculador de IVA:

public class IvaCalculator
{
decimal _tax;

public IvaCalculator(decimal tax)
{
_tax=tax;
}

public decimal calc(decimal amount)
{
return _tax*amount/100d
}
}


De esta manera basta con instanciar un objeto por cada tipo de IVA y ponerlo a trabajar; esto minimiza la cantidad de código escrita, y es bastante claro:

IvaCalculator autoCalc=new IvaCalculator(25d);
IvaCalculator prodCalc=new IvaCalculator(16d);
decimal imp1 = autoCalc(25800d);
decimal imp1 = prodCalc(4d);
decimal imp1 = prodCalc(5800d);


Pero de nuevo... si son diez tipos distintos de IVA, creará diez instancias de objeto? Es justo tanto empleo de memoria al tener un objeto completo solo para hacer una operación?
No lo creo... precísamente para solventar esta situación son útiles los enclosures.(Entre otras)

Un enclosure permite reflejar la simple funcionalidad de la pequeña clase que diseñamos anteriormente, sin incurrir en el overhead del objeto como tal. Para lograrlo, se requiere una forma de mantener un estado (en este caso, la tasa del impuesto) para no tener que estar pasando el parámetro de configuración en cada llamado. Además se requiere una operación sobre ese estado. Cómo lograrlo sin tener que hacer un objeto?

La operación como tal, se logra con un simple método. Pero este método debe ser configurable en su creación de manera que tenga un estado.

Para que se pueda configurar es necesario que exista una variable en un ambiente léxico (scope) superior al del método, de manera que esta variable pueda ser inicializada sin necesidad de llamar al método. Esta, será pues la variable que indica el "estado" del método:

class....
{
decimal _tax;
public decimal IvaCalc()
{
....
}
}

Pero igual, eso está dentro de una clase. De hecho en C# todo lo que ejecute, ha de estar dentro de una clase. La ventaja ahora, es que vamos a trabajar dentro de la misma clase de ejecución de nuestro flujo. No tendremos que crear más instancias. Así que continuando:

Tanto el parámetro como la operación deben quedar encapsulados dentro de un mismo ambiente léxico dentro de la clase, para que tengamos la unidad requerida para poder generar instancias (pero no de una clase que es lo que queremos instanciar, sino de algo más light que llamaremos "enclosure"). Para esto es necesario poder hacer referencia al método dado.

Qué nos permite hacer referencia a métodos? Si... correcto: los delegados. Entonces generamos un ambiente léxico que tenga al delegado referenciando al método y al parámetro de configuración. Obviamente si estamos dentro de una clase y no queremos generar otra, ese ambiente léxico es un nuevo método. Este método deberá retornar el delegado apuntando a la operación ya configurada como es debido, pero además el método al que apunte el delegado deberá estar declarado inmediatamente, de manera que el parámetro de configuración del mismo sea accesible a éste, desde el método que lo contiene.

En teoría suena bien. Pero cómo lograr que un delegado apunte a un método que se declara "en línea" junto con éste?

Ahí es donde entran los métodos anónimos. Son precisamente eso: Métodos declarados en línea donde son requeridos por los delegados. Generalmente, los delegados se han inicializado con el nombre de un método que cumpla el contrato de su firma. Ahora (Framework 2.0 en adelante), no es necesario declarar el método aparte para poderlo referenciar luego por un delegado (lo que impediría acceder al dichoso parámetro de configuración) sino en línea junto con la declaración del delegado:

delegate int MyDelegate(int a, int b);
MyDelegate myDel = delegate (int a, int b)
{
.....
return ....
};

No olvide el ";".

Ya con este conocimiento, podemos generar el método que queremos:

delegate decimal ivaCalc_(decimal amount);
ivaCalc_ ivaCalcProducer(decimal tax)
{
return delegate(decimal amount)

{
return amount*tax;
};
}


Exótico no?

Pero a pesar de ello, logramos lo deseado: El método toma una variable de configuración. Así tenemos estado y manipulación de estado. Lo que haría una clase. Entonces ahora como se operaría es muy parecido al uso de clases anterior; solo que ahora no hay overhead por creación de nuevos objetos. Lo que se crean son nuevos delegados. Uno por cada tipo de tasa en este caso. Ellos "comen" mucho menos que un objeto:

IvaCalc_ ivaCalc16=ivaCalcProducer(16d);
IvaCalc_ ivaCalc25=ivaCalcProducer(20d);
decimal imp1 = ivaCalc25(25800d);
decimal imp1 = ivaCalc16(4d);
decimal imp1 = ivaCalc16(5800d);

Y listo... gracias a lo anterior por fin le ví un uso práctico a toda esa parafernalia mística de métodos anónimos que vino con el framework 2.0. Aunque sé que otra gran utilidad es en las expresiones lambda que trataré de abordar en otro post, pero pues, primero el 1 y luego el 2.

viernes, 8 de febrero de 2008

Snack Overflow Exception

Digo, Stack...

El error, StackOverFlowException.
Más de una semana sin solución. Buscaron en internet a ver... nada.
Los StackOverFlow Exception no se pueden capturar con un try catch. Ni lo intenten. Cuando sucede la aplicación está más muerta que al cerrarla.
Finalmente, me preguntaron....
"Eso es que tienen una función recursiva con el punto de parada mal diseñado" les dije.
Pero no había tal.
Entonces me puse a mirar...
en la primera ocasión no vi nada anormal en realidad. Solo un par de cosas para optimizar el código. Pero nada como para decir que el misterioso snack dejara de aparecer.
Una y otra vez se repetía. Y lo peor, es que era un proceso bastante largo, que siempre fallaba luego de la primera horta. Así que hacer debug era un infierno total.
En la segunda ocasión que me senté a mirarle el código, tampoco vi nada raro; se me ocurrió que depronto había mucha memoria desperdiciada... pero nótenlo: eso nunca tiene que ver con el stack. Snack es una cosa y Stack es otra y Memoria no tiene que ver con ellos. El Stack almacena el listado de funciones que se han ejecutado. Y tiene un tamaño entre 500 y 1000. Así que si llamamos más de ese número, el stack secillamente se llena y la aplicación colapsa.

Teniendo esto muy presente, decidí volver a chequear el código una tercera vez...
y oh sorpresa cuando al fin encontré lo que estaba pasando!!!!

Había un método que al final del mismo, tenía un disparador de evento. Otra clase capturaba ese evento y en respuesta lanzaba la ejecución de otro proceso distinto que antes de terminar volvía a lanzar ese evento y así sucesivamente. Todo esto siempre se ejecutaba con el mismo thread. Así que cada vez, el método se quedaba esperando a que se ejecutara el siguiente. Por esto el stack comenzaba a llenarse hasta colapsar, porque las funciones no terminaban, pero si se llamaba a otras.

Es algo para nada obvio. Pero al fin por pura intuición se descubrió. Lo ridículo del asunto, era el mensaje que representaba el evento disparado: "Terminé" Entonces otra clase leía que había terminado y ponía a correr otro proceso. Que sentido tiene eso? Era muy difícil programar a la clase externa que corriera un proceso y luego otro y otro y ya? No lo creo. Sencillamente a algunos diseños les falta ingeniería de verdad.

jueves, 7 de febrero de 2008

The Literal Control Spirit

This simple but powerful control, sometimes generates false expectatives, around itself. I've seen in some forums, people using'em instead the Labels, cause "Performance Issues". But; nevertheless the Literal is simpler than the Label control, the performance enhacements got with the former control is not very significative.

Then, which is the power of the Literal Control? The importance of Literal, beyond performance, is that you can write html formatted text (i.e. A paragraph with
and stuff like this... have u tried to make a line change in a label containing lots of text and needing to be formatted -making bold some words in instace?) in a more controllable way than using Response.Write as an example.


Thus, you put the literal control wherever you want and control some behavior over it (Like it's content, if it is visible or not, etc).Sadly, style sheet controlling is not available for this control (There is a lack of the property CssStyle which is present in most of common controls as labels).

But is logic u need to keep the consistence of your web site and don't create a font mixing which will be worst for the user feelling who will prefer read your page written by pencil. A simple way to do this, is make the Literal be part of a Panel. The panel will acept the css property and the Literal, will inherit this appearence.

Transmitiendo Variables de Aplicación entre Capas

(Y haciéndolas dinámicas)

Sea el escenario de una típica aplicación 3-tier para un sistema de información. Hay una capa de presentación, una de negocios y la de datos. En la capa de datos según lineamientos plenamente aceptados, tenemos las cadenas de conexión a las bases de datos que se usarán. Si la aplicación se desarrolla con el framework 2.0, es normal que las capas de datos y negocio, queden representadas por DLLs que luego son consumidas por una aplicación windows o web, por ejemplo. Entonces cada cliente se comunica con sus capas servidoras que tienen la conexión a base de datos y saben en dónde están los datos para operar.

Hasta ahí no hay ningún problema. Cuando la aplicación ya está lista para producción, generalmente se usará una base de datos distinta a la usada en desarrollo y quizá en pruebas.
Este cambio, gracias a la arquitectura del framework 2.0, solo implicaría cambiar el archivo de configuración (App.config) de la DLL de capa de datos. Se abre con un editor de texto, y se edita el XML con la nueva cadena de conexión, y ya está. Todos los clientes quedan conectados a la nueva fuente. Pero qué sucede si se crea una aplicación de capas, que no necesariamente tiene la misma conexión de base de datos para todos los clientes? Cómo lograr que cada cliente genere una cadena de conexión dinámica que se pueda cambiar a discreción permitiendo que la aplicación funcione correctamente?

En el framework 2.0 las cadenas de conexión son tratadas como variables de alcance de aplicación. El otro tipo de alcance es de usuario. Cuando la variable es de alcance de usuario, esa misma, puede ser modificada en tiempo de ejecución. Entonces sería fácil crear métodos para que los clientes de presentación de alguna manera transmitieran los nuevos valores de las cadenas de conexión a las otras capas. No obstante, como mencioné anteriormente, las cadenas de conexión son de alcance "aplicación" y estas variables no se pueden modificar en tiempo de ejecución. Así que para un cliente es imposible modificarlas transmitiendo nuevos valores mientras está corriendo. Así que el método de crear código que transmita los cambios requeridos, no es factible. La solución, aunque es trivial, no es intuitiva y Visual Studio no provee una manera directa para lograrla. Para encontrarla y entender de qué se trata, suponga que llamamos a la cadena de conexión strCon1. Entonces, queda guardada dentro de los settings de la DLL de datos como strCon1 y tiene determinados valores de conexión.

Además permitiría cambiar la cadena de conexión dinámicamente, antes de iniciar la ejecución de la aplicación (recordemos que ésta es una variable de aplicación). Al distribuir la aplicación, se puede hacer con o sin el archivo de configuración de la aplicación. Si se distribuye sin este, cuál será entonces la cadena de conexióna usarse? Se usaría la última presente al compilar la librería (Esto gracias a la compilación del archivo de propiedades en la clase Settings.settings).

Como es conocido, cada capa en el framework, vendría representada por un assembly (dll) por separado. Es decir, cada capa es un proyecto dentro de la solución y cada proyecto de estos, tiene sus propias propiedades y variables de usuario y aplicación. Como ya se mencionó estas variables van en archivos App.config. Al compilar la solución, dentro de cada carpeta de salida de los proyectos queda un archivo App.config para las dlls, y un archivo miaplicacion.exe.config para los ejecutables en el caso de aplicaciones windows o web.config en el caso de web services o aplicaciones web ( en estos ultimos dos casos, el archivo queda en el root del sitio web).

Lo que uno como desarrollador distribuiría, sería únicamente los compilados y salidas de los proyectos componentes de la solución. Entonces irían en nuestro ejemplo dos dll (una de datos, la otra de negocio) y el ejecutable. Además irían los archivos de configuración, que en el caso de las dll se llamarían igual.

Pero si se llaman igual, cómo se soluciona este problema? Fácil; basta con combinar los archivos de configuración en uno solo con el mismo nombre y listo. Pero es fundamental que los nombres de las variables (por ejemplo de cadenas de conexión) permanezcan invariables.

En mi búsqueda de esta solución, una de las cosas que intenté fue agregar una variable de aplicación a la capa de presentación, que tuviera el mismo nombre de conexión y los mismos parámetros que la definida en la capa de datos usando la interfaz de Visual Studio. De esta manera se produciría un miaplicacion.exe.config, con la cadena de conexión que usa la capa de datos y así cada vez que yo cambiase este archivo y reiniciase la aplicación, el sistema tomaría los cambios y funcionaría de acuerdo a ellos.
Al ver con más detalle el resultado, descubrí que el nombre de la variable no había quedado exactamente igual que el usado por la capa de datos. Y en la práctica, este hecho no es muy fácil de identificar.

Así que dado que uno puede estar tentado a agregar el setting usando la interfaz de Visual Studio, puede incurrir en este error si no se fija lo suficientemente bien. Lo mejor, es no agregar el setting de esta manera. Sino agregar a mano (o editar a mano si ya existe), un archivo App.config y escribir el setting exactamente como se usó en la capa de datos.Cuando así lo hice, con felicidad observé como finalmente los cambios en el archivo XML se veían reflejados en la aplicación, cuando esta empezaba a correr. Esto me dió la felxibilidad requerida para configurar cada cliente con una fuente de datos distinta a la usada en el desarrollo de la aplicación.

Conclusión: Para propagar las variables de configuración desde las capas más exteriores a las más interiores dentro de un sistema n-tier usando framework 2.0, basta con escribirlas todas en el archivo de configuración de la capa más externa, de manera que sean absolutamente idénticas a las usadas en las capas internas. Y no es posible hacer esto, solo con la ayuda de la interfaz gráfica de Visual Studio 2005, pues se requiere editar el archivo manualmente y luego incluirlo en el deployment de la aplicación.

No agregue tablas con el mismo nombre

QUÉ SUCEDE AL AGREGAR UNA TABLA A UN DATASET QUE CONTIENE YA UNA TABLA CON EL MISMO NOMBRE?

Esta duda se me originó cuando en una aplicación Web, desde las capas superiores le enviaban a la de presentación un DataSet con datos de tablas referenciales (es decir, aquellas que no sufrían mucho cambio); dada su naturaleza decidí poner este dataset en el singleton de la aplicación. Pero sucede que luego decidieron que las tablas de referencia en realidad iban a cambiar de cuando en vez. Así que había que proveer un mecanismo para informar al singleton que su dataset había cambiado y se debería recargar una(s) de sus tablas. No obstante dada la separación de capas y que no existe en la capa de presentación acceso a los TableAdapters, la única solución fue bajar de nuevo la tabla ya actualizada.
Para esto recurrí primero a dataSet.Tables["nombre"]=servicio.GetTable["nombre"];pero es erróneo porque dataSet.Tables["nombre"] es de solo lectura y no se puede cambiar.
Así que luego ví:dataSet.Tables.Add(table);
pero como se supone que la tabla ya existe, no sabía que consecuencias implicaría hacer esto. Busqué en la yuda y no encontré nada fácilmente. Así que decidí que sería más rápido investigar por mi propia cuenta.Lo que sucede es que al intentar agregar una tabla con el nombre de una que ya existe, se produce una excepción que arroja más o menos ese mismo mensaje. Así que no es que se produzca un "merge" de los datos, ni que halla un reemplazo del obketo tabla como tal. Sencillamente, no se puede.
La solución, bastante sencilla: Si existe un Add, obviamente ha de existir un Remove. De esta manera, usando el Remove anteriormente al Add, para la misma tabla se obtiene el reemplazo con los datos actualizados.
Sin embargo, el problema que originó buscar esta solución, obviamente está basado en un sitio Web mal concebido desde sus principios, pues esta última solución no es la mejor, pues lo ideal, sería que el cambio se hiciera en la capa local y luego en la capa superior, o que solo se trajeran los registros actualizados de la capa superior, y no de nuevo toda la tabla.
Pero por premuras de tiempo y errores sucesivos en el diseño, me vi forzado a buscar esta solución no muy grata. Espero poder convencer de usar lo último que en realidad sería más rápido que volver a traer todos los datos.

lunes, 4 de febrero de 2008

Don JSon

Algunas aplicaciones web requieren AJAX y requieren que su tamaño de página ha de mantenerse bajo límites estrictos. Entonces no es posible usar las librerías de AJAX .NET dado el considerable peso que le agregan a la página, sino usar directamente el objeto XmlHTTPRequest. Adicionalmente es necesario considerar el peso impuesto por los datos que se transfieren desde el server al cliente luego de usar AJAX. En primera instancia uno podría pensar en XML. Pero como se sabe, los archivos se hacen muy grandes con información que finalmente no es la relevante, así que se genera más peso para las páginas. Por este motivo, actualmente se está difundiendo rápidamente el uso de JSON.

JSON es un sobconjunto del lenguaje JavaScript, que permite representar objetos a través del paradigma llave-valor. En síntesis es como un "fatless" XML, que es mucho más liviano. Y que además tiene la gran ventaja que puede ser interpretado directamente por JavaScript, de manera que tanto el peso de la página como la ejecución se mejorar sustancialmente, ya que no hay que usar librerías para manejo de XML en JavaScript ni hacer tediosos recorridos a través del texto.

Para JSON hay librerías disponibles en casi todos los lenguajes, incluido C#. De esta manera, cualquier objeto puede ser traducido a de C# a texto JSON en el server, luego transferido al cliente usando Request.Write después de un llamado AJAX, e interpretado inmediatamente por el browser, sin mayor overhead.

En C# 3.0 (disponible en VS2008) muchas de estas características están siendo implementadas de forma nativa y ofrecen beneficios incalculables a la hora de desarrollar, por lo que es recomendable desde ya comenzar a usar estos elementos que han existido desde hace mucho tiempo, pero que solo ahora con el auge de “la interpretación – AJAX, Silverlight, etc- han entrado de lleno en el mundo Microsoft.


El sitio oficial es
http://www.json.org
Pero para no devanarse los sesos y entender rapidito el asunto está: http://www.secretgeek.net/json_3mins.asp; en tres minutos sabrán qué es.

El web service se reventó.

O no supimos configurar el HttpRuntime
O lo encargamos de lo que no era

El webservice protagonista de este post fue creado y usado para lo que no debería ser. Exponía una funcionalidad de una aplicación web administradora de contenidos. Como esa era muy compleja "pa meterle la mano" entonces la dejaron intacta, solo que le "samparon" el web service, para que la aplicación propia pudiese usar el dichoso administrador de contenidos.

En teoría funcionaba bien. Pero ante cargas significativas: "The thread have been aborted". Y nadie sabía qué era. Mejor dicho, se "totiaba". Entonces me llamaron a ver que era. Y lo primero que observé, es que el pobre webservice exponía unas operaciones que operaban sobre la base de datos. Y en general eran operaciones costosas. Pero lo peor, es que siempre el webservice tenía que quedarse esperando la respuesta del servidor de DB.

"Eso es el timeout del webservice" dije.
"Si, pero ya le pusimos hasta 5000 horas y nada!!"
Ouch!!!

Entonces tratemos de optimizar las operaciones de la DB.
Le pasé el profiler, el DBEngine Tunning Advisor, implementé y borré índices y en efecto. La vaina mejoró.
Operaciones de 9 minutos pasaron a 3 y en otros casos operaciones de 3 min pasaron a 1 seg (y eso porque el reporte no mostraba menos de 1 seg)

Pero llegamos al punto en que por más optimización que hicimos, el error seguía pasando y el thread seguía siendo abortado.

Cuál thread? Pues el del webservice. Entonces insistí: "Eso tiene que ser algún timeout"
El sujeto directamente implicado entonces muy juicioso esa noche googleó y googleó hasta encontrar un nuevo parámetro de timeout. Y tome!!! "Le pegó al perrito" (Lamento no haber sido yo, así la recompensa para mí hubiese sido mayor. Pero me agrada haberlo guiado por el camino de la luz... veo una luz veo una luz!!!)

El httpruntime es un parámetro de configuración en el webconfig y jugar con sus valores puede ser catastrófico o genial. (En este punto cuando he nombreado web.config obvio que sabrán que estoy hablando de asp.net. especficamente del 2.0).

Señores, se los presento aquí.

Allí encontramos como atributo a executionTimeOut y fue el que le pusieron como en 5000 y nada. Por qué?
Uno diría que aquí es donde le puede decir al webservice que espere más antes de "morirse". En efecto así es. Lo que pasa es que con el framework 2.0 vino nuevo otro atributo: shutdownTimeout. El primer atributo aplica sobre la aplicación como tal. Pero el segundo aplica sobre el worker process. Así que si está muy bajo para lo que uno quiere, el worker process es "bajado" por ASP.net cuando el tiempo expira y adivinen que pasa "The thread has been aborted".

Lo pusieron en 1000 y por fin pudieron hacer su prueba completa!!!

Concluyendo: Para evitarse problemas, no ponga a los webservices a esperar respuestas a trabajos completos. Esto margina el rendimiento de la red, y en general de todos los recursos del sistema. Eso mándelos a ejecutar algo y que se desprendan de una. Y luego ponga un observer a que mire si el otro ya acabó el trabajo. O quizá mantenga otro tipo de suscripción.

Si ya la embarró y no hay tiempo para cambios o definitivamente la terquedad o condiciones del negocio no permiten más, acuda al httpruntime y sus dos timeouts; esto puede apagar el incendio.

viernes, 1 de febrero de 2008

La piscina de Sql Connections

Cuando se crea una conexión SQL en ASP.NET, por defecto siempre irá al POOL de conexiones administrado por el framework. Es así como ni al cerrarla ni haciéndole dispose, esa conexión realmente se cierra en el SQL Server como tal. El pool de conexiones existe para mejorar en general el rendimiento de las aplicaciones, dado que el hecho de abrir una conexión es bastante costoso. Así que aunque lógicamente la conexión está ConnectionString es requerida, se sirve una de las ya existentes en el pool de una manera más rápida que creandola desde 0 (from the scratch).

Si no se desea este comportamiento, basta con incluir en la conexión o en el WebConfig POOLING=FALSE

El pool para un ConnectionString en particular se puede configurar con el número máximo y mínimo de conexiones. Jugar con estos valores puede mejorar o empeorar el rendimiento de la aplicación. El valor por defecto es de 100.

Existe un error bastante grotesco que puede asustar mucho si no se entiende de connection pools:

A transport-level error has occurred when sending the request to the server. (provider: Shared Memory Provider, error: 0 - No process is on the other end of the pipe.)

Ocurre por ejemplo si en el pool no hay conexiones disponibles, debido a que han sido terminadas por un ente distinto a la aplicación (alquien desconectó el cable de red, apagaron el server, le hicieron kill a las conexiones, etc.)

No ocurre porque la operación esté tardando demasiado. Y el que ocurran excepciones por éste último motivo no se arregla aumentando el timeout de la conexión. El timeout de la conexión se refiere al tiempo de espera para logran conexión con el server de db, antes de que una excepción sea lanzada.

El tiempo de timeout por defecto es de 15 segundos. Y en el connectionstring está medido siempre en segundos. Siempre ha tener valores positivos o 0. De lo contrario una excepción surge. Si es 0, significa que nunca se lanzará excepción por incapacidad de alcanzar al server. Esto no es buena idea, porque el sistema se puede volver irresponsivo, y no habría indicios de por qué sucede esto.

Lo que viene es tomado de http://www.ondotnet.com/pub/a/dotnet/2004/02/09/connpool.html

My final thought, expressed in code (of course), is a handy method that will return the value of any connection string parameter passed into it. I'll leave it to you to improve the DAC with it:

private string GetParameterValue(string conStr, string searchParam)
{
// searchParam, e.g., "Max Pool Size" or "Incr Pool Size"
int posBegin = 0;
int posEnd = 0;
char[] delimiter = {';'};
string paramVal = string.Empty;

posBegin = conStr.IndexOf(searchParam);
if (posBegin > -1)
{
// add one for '=' char
posBegin += searchParam.Length + 1;
if (conStr.LastIndexOfAny(delimiter) > posBegin)
// somewhere in the middle
posEnd = conStr.IndexOf(';', posBegin);
else
// at end of string
posEnd = conStr.Length;
paramVal = conStr.Substring(posBegin, (posEnd - posBegin));
}
return paramVal;
}

Para que los pool funcionen correctamente, debe existir un manejo responsable de las conexiones. Cómo se manejan responsablemente? Cerrándolas cada vez que no se vayan a requerir más.

SqlConnection conn = new SqlConnection(myConnectionString);
try
{
conn.Open();
doSomething(conn);
}
finally
{
conn.Close();
}


Como se vé si hay un fallo, igual se cierra la conexión.

Teniendo en cuenta: http://www.15seconds.com/issue/040830.htm

"Close and Dispose methods of Connection object are equivalent. Neither one gives you any specific advantages over the other. "

Por tanto se puede usar también de una forma más elegante y ostentosa:

using (SqlConnection conn = new SqlConnection(myConnectionString))
{
conn.Open();
doSomething(conn);
}


El using en exte contexto libera todos recursos declarados, por medio de Dispose.