viernes, 1 de octubre de 2010

ExpressionControl: Control personalizado para elementos de trabajo de Team Foundation Server

Acabo de publicar en The Code Project un artículo en el que presento un control personalizado para elementos de trabajo de Team Foundation Server. Se trata de un cuadro de texto que tiene asociada una expresión (una fórmula, vamos) en la que pueden tomar parte los campos del elemento de trabajo. Cuando uno de los campos cambia, el valor del campo se recalcula automáticamente:



Si te interesa, aquí puedes echarle un ojo: Expression Control for TFS Work Items

viernes, 27 de agosto de 2010

Pseudo-AOP usando delegados en C#

Estaba yo tan feliz programando un servicio en WCF y me di cuenta de que en todos los métodos de servicio que escribía estaba repitiendo el mismo esquema de código una y otra vez: "Ejecuta todo el método en un bloque try-catch, y si algo va mal, escribe una traza del error y devuelve null (o cero, o el valor por defecto adecuado)". Decidí entonces que debía hacer algo al respecto, primero porque estaba violando el principio DRY (algo inadmisible para alguien que programa con educación), y segundo por vagancia (si se puede ahorrar aunque sea un simple copypaste, hay que hacerlo).

Encontré una solución muy mona que se basa en el uso de un método auxiliar que se encarga del trabajo sucio (capturar la posible excepción y escribir la traza en ese caso) más un delegado que contiene el código que queremos ejecutar realmente. El resultado es algo que, visto de lejos y en una noche sin luna y con niebla, se parece remotamente a la Programación Orientada a Aspectos. Ahora vamos a ello.

Supón que tienes un método (expuesto al exterior por medio de un servicio WCF en este caso, pero no tiene por qué ser así) tal que este:

Bien, quieres añadir control y traza de errores de forma genérica, es decir de forma que no tengas que escribir el mismo código para otros métodos similares. ¿Cómo podemos conseguir esto?

La solución que yo uso pasa por definir un método auxiliar llamado ExecuteMethod, que en su forma más básica es algo así:

Entonces modificamos la firma de nuestro método de servicio de forma que pasa a ser privado, y creamos un método nuevo que invoca a ExecuteMethod pasando el método original como parámetro. Se entiene mejor viendo el código directamente:

Nótese cómo usamos una expresión lambda para pasar de forma limpia y elegante (¡oh, ah!) un delegado que apunta a nuestro método original.

Ahora, podemos aplicar esta técnica de control a todos los métodos que queramos, por ejemplo:


La gracia de esta técnica es que podemos cambiar nuestra estrategia de control de errores y traza cambiando sólo el método ExecuteMethod. Por ejemplo, podemos decidir escribir trazas de error usando el método Debug.Write en un principio, para más adelante usar el registro de eventos de Windows, quedando entonces el método algo así:


También podemos decidir volver a lanzar la excepción en vez de devolver el valor predeterminado del tipo de retorno, o actuar de forma distinta según el tipo de excepción... en fin, las posibilidades son infinitas, o casi.

Para terminar, he aquí la versión de ExecuteMethod para invocar métodos de tipo void (es decir sin valor de retorno):

Esta versión se invoca de la misma forma que la anterior, excepto por supuesto que no es necesaria la palabra return:

En resumen, como dicen por ahí: se non è AOP, è ben trovato.

jueves, 19 de agosto de 2010

Envoltorio para las clases File y Directory

Si en tu código usas algún motor de inyección de dependencias (deberías), sabes que tus clases nunca deben depender de otras clases concretas sino de interfaces, y que los objetos que implementan dichos interfaces deben ser suministrados externamente (típicamente mediante el constructor). Es decir, que en vez de hacer esto:

...tienes que hacer esto otro:

...y usar un inyector de dependencias para registrar la clase apropiada que implemente IMiClaseAuxiliar y para construir los objetos de tipo MiClase. Esto facilita enormemente la creación de pruebas unitarias, especialmente si usas algún mecanismo de mocking.

La cosa pinta de entrada muy simple y muy bonita, pero, ¿qué pasa cuando en vez de objetos propios tenemos que usar las clases de sistema? Por ejemplo si accedemos al sistema de ficheros, ¿cómo simulamos la existencia de determinados ficheros en nuestras pruebas unitarias?

La solución es no usar directamente las clases de sistema implicadas (File y Directory en caso de ficheros), sino crear clases "envoltorio" que tengan los mismos métodos públicos que la clase de sistema original; dichos métodos simplemente delegarán el trabajo en los métodos "reales" de la clase de sistema correspondiente. Creando interfaces para las clases envoltorio, ya podemos usar mecanismos de mocking en nuestras pruebas unitarias.

Como me explico peor que un libro sumergido en cemento, mejor pongo un ejemplo. Si por ejemplo antes teníamos esto:

...ahora definiríamos una clase FileWrapper con su correspondiente interfaz:

...y nuestra clase quedaría:

Pues bien, resulta que alguien más ya había pensado esto y ha creado sendos envoltorios para las clases File y Directory. Os pego el código aquí para que sea de más fácil acceso. De nada.

lunes, 28 de junio de 2010

Parámetros de cualquier tipo en métodos de RIA services

Bueno, pues resulta que estaba yo tan feliz y contento escribiendo unos métodos para un servicio RIA, cuando me he encontrado con un problemilla inesperado. Digamos que yo había implementado un método tal que este:

En el cliente, el método debe ser invocado así:

donde ItemsFilter es una clase que contiene un puñado de criterios de filtrado para los datos que deben ser devueltos. Todo muy bonito y rebosante de felicidad.

Peeeero... resulta que tal código no compila. Da un error como este: Operation named 'GetItems' does not conform to the required signature. Parameter types must be an entity type or one of the predefined serializable types.

¿Y dónde está el problema? Pues en este foro de MSDN, donde la gente se caga en muestra un profundo malestar con el equipo de desarrollo de Microsoft, está explicado ferpectamente: No se pueden pasar tipos arbitrarios como parámetros a los métodos de un servicio RIA, sólo unos cuantos tipos predefinidos (mayormente, cadenas y números).

Por suerte, esto tiene una solución más o menos sencilla: se trata de cambiar el parámetro conflictivo para que sea una cadena en vez de ser del tipo deseado. En el cliente se serializa en XML el objeto en cuestión, en el servidor se deserializa, y todos contentos. Esto funcionará si la clase del parámetro se puede serializar en XML, cosa que para objetos sencillos de transferencia de datos normalmente será cierta.

¿Y cómo conseguimos tal cosa? Pues si quieres saber cómo lo he hecho yo, sigue leyendo.

En primer lugar he creado en el proyecto de servidor una clase auxiliar con métodos estáticos para serializar y deserializar objetos en XML. He llamado al fichero SerializationHelper.shared.cs, de esta forma lo tengo disponible también en el cliente. Este es el código de la clase:

A continuación he modificado el código del método en el servicio para que admita una cadena como parámetro, a partir de la cual se reconstruye el objeto:

Y en cuanto al cliente, el código de invocación es ahora este:

...y con este un tanto incómodo pero útil truquillo, ya podemos seguir RIAcizando felices y contentos. La pregunta es: ¿por qué no hace esto ya el compilador? ¿Qué llevó a los diseñadores del entorno RIA a imponer una limitación tan tonta? Misterio misterioso.

jueves, 24 de junio de 2010

WCF RIA services + inyección de dependencias

Tengo que confesarlo: soy un fan del patrón inyección de dependencias. Y también me gusta bastante la tecnología WCF RIA services, que estoy usando ahora en un proyecto. Problemilla: ambas cosas no casan muy bien a priori, puesto que a no ser que se indique lo contrario, las clases que proporcionan los servicios RIA (las clases derivadas de DomainContext) son instanciadas automáticamente por el motor de ASP.NET, asumiento que tienen un constructor sin parámetros. Pero claro, la inyección de dependencias se consigue precisamente pasando las dependencias como parámetros del constructor (hay otras formas, pero tampoco casan muy bien con la creación automática de la clase).

Bueno, ¿y cómo "se indica lo contrario"? La solución está explicada en esta entrada de StackOverflow: básciamente se trata de decirle a la clase DomainService que use una factoría personalizada cada vez que necesite una instancia del servicio, cosa que se hace desde el constructor estático se la clase Global de ASP.NET. Esa factoría personalizada es una clase que proporcionamos nosotros y desde la que podemos crear la instancia del servicio a nuestro gusto.

Veamos pues cómo extender esta idea para usar inyección de dependencias en nuestros servicios RIA.

El proyecto

He creado un solución de Visual Studio sencilla de ejemplo para ilustrar el concepto. Se compone de dos proyectos Silverlight (la aplicación principal y una biblioteca de clases para el acceso a los servicios), más un proyecto web (que aloja la aplicación Silverlight) y un proyecto de bliblioteca de clases que aloja el servicio (derivado de DomainService). La idea es usar inyección de dependencias para suministrar una capa de acceso a datos a la clase que implementa el servicio.

La solución tiene tal que este aspecto:


En enlace RIA está establecido entre el proyecto Client.Services de la parte cliente y el proyecto Services de la parte servidor, quedando pues las dependencias entre proyectos tal que así:


Además de las dependencias entre proyectos, es necesario añadir un par de referencias más al proyecto MyRiaApp.Web:
  • System.ServiceModel.DomainServices.Server.dll, necesario para poder acceder a la clase DomainService.

  • Microsoft.Practices.Unity.dll, se trata de Unity, el inyector de dependencias de Microsot, que es por cierto el que se usa en el proyecto.
El proyecto MyRiaApp.Services expone un servicio RIA llamado MyRiaDomainService que contiene métodos expuestos al cliente por medio del enlace RIA. Para simplificar, en este proyecto de ejemplo sólo se expone un método, que devuelve un entero con un contador de elementos que supuestamente son objetos útiles para la aplicación. Este es el código del servicio:
Tal como he mencionado al principio, el servicio usa una capa de datos para aislar el acceso al motor de persistencia (rimbombante nombre para lo que es la base de datos), representado por un interfaz llamado IDataAccessLayer cuyo código es este:

Un posible esqueleto de implementación de IDataAccessLayer podría ser este:

Estamos usando el mecanismo de inyección de dependencias con la clase MyRiaDomainService, a la que pasamos una instancia de IDataAccessLayer en el constructor; y también con la clase IDataAccessLayer, a la que pasamos la cadena de conexión. Ahora se trata de hacer que ASP.NET use el inyector de dependencias (Unity, en este caso) para resolver las idems a la hora de crear instancias del servicio.

Inyectando

Para conseguir tal cosa, añadiremos al proyecto web una clase Global.asax en la que pegaremos el siguiente código (resalto las partes más interesantes):

¿Qué hemos hecho aquí? Pues un par de cosas:
  • Le hemos dicho a ASP.NET que use la clase MyAppDomainServiceFactory cada vez que necesite crear una instancia de un servicio RIA.

  • Hemos creado una instancia de UnityContainer, que queda disponible para toda la aplicación por medio de Global.DependencyInjector.

  • Hemos creado una instancia de DataAccessLayer, que hemos registrado en el inyector de dependencias.

  • Hemos configurado MyAppDomainServiceFactory para que obtenga las instancias de los servicios a partir del inyector de dependencias.
De esta forma, cada vez que se requiera una instancia de un servicio, será el inyector de dependencias quien lo proporcione, encargándose de resolver cualquier dependencia existente (la dependencia de IDataAccessLayer en MyRiaDomainService, en este caso.)

Nótese que una ventaja adicional de este sistema es que funcionará incluso con servicios que no tengan ninguna dependencia. Por ejemplo, en paralelo a MyRiaDomainService tenemos un AuthenticationDomainService que sólo tiene un constructor sin parámetros. Unity será capaz de crear una instancia de dicho servicio sin problemas, simplemente no le inyectará ninguna dependencia.

Un apunte más. Estamos obteniendo la cadena de conexión para la capa de datos a partir del archivo de configuración Web.config (estamos haciendo una "inyección de dependencias manual" en este caso). Pego aquí el contenido de dicho archivo (la cadena de conexión es un ejemplo, claro), que de paso muestra los elementos necesarios para que funcionen los servicios RIA:

Conclusión

El patrón inyección de dependencias ayuda a conseguir código legible y mantenible. Ahora ya sabes cómo usarlo en conjunción con los servicios RIA, lo cual sin duda incrementará tu felicidad en varios enteros, o no.

jueves, 17 de junio de 2010

Twiteando desde el MSX en japonés... o casi

Como ya dije en mi blog principal, hace poco he desarrollado un cliente de Twitter para MSX llamado MSX trivial tweeter. Una de las principales carencias del mismo era la imposibilidad de escribir en japonés, dada la complejidad de convertir el juego de carácteres SHIFT-JIS a UTF8.

Pues bien, al final me he puesto a ello y he implementado soporte para escribir en japonés desde MSX trivial tweeter.




La conversión del juego de caracteres la hago mediante un fichero de 64K que contiene la conversión a UTF16 de todos los posibles caracteres del SHIFT-JIS (almaceno en UTF16 y convierto a UTF8 en tiempo real; si no, el fichero debería ser de 128K). Hay mucho espacio vacío en el fichero y se podría reducir bastante su tamaño usando algún método de compresión, pero de momento no me voy a complicar más la vida con este asunto.

Bueno, espero que no hayais descorchado el champán todavía porque ahora viene la parte chunga. Resulta que tanto InterNestor Lite como DenYoNet se dan de sopapos con el modo Kanji de los MSX (de los Turbo-R al menos). La captura que habéis visto más arriba está hecha con un emulador en modo MSX2+, y ya con ese entorno empiezan las cosas raras: tengo que activar el modo Kanji (CALL KANJI desde BASIC) antes de instalar el InterNestor, si no, lo único que conseguimos es un precioso cuelgue.

Pero en Turbo-R (tanto emulado como real), no hay manera. Ni antes ni después: el modo Kanji y cualquiera de los softwares TCP/IPeros que he desarrollado no pueden funcionar juntos de ninguna de las maneras.

¿Y esto por qué pasa? Pues parece ser que por culpa de la forma que tiene la Kanji ROM del Turbo-R de gestionar la BIOS extendida, y no soy el primero que tiene este problema (por lo visto el MEMMAN, conocido y veterano gestor de memoria desarrollado por obsoletos holandeses, tampoco funciona con el modo Kanji activado). El código de dicha ROM asume, al instalarse, que el contenido previo del gancho de la BIOS extendida es un salto interslot (de esos tan monos que usan una instrucción RST30h). Pero claro, esto no tiene por qué ser así: en un gancho puedes poner lo que quieras, incluso una simple instrucción JP; y de hecho eso es lo que hace el InterNestor, y apostaría la melena a que el MEMMAN hace lo mismo.

¿Que no me creéis? Pues fijaos en el código desensamblado de la Kanji ROM, que obtuve hace tiempo por medio de canales oscuros y truculentos (Persicop, si me lees, tenemos que vernos un día para montar un concurso de calvas). Esta parte del código es la que inicializa el gancho de la BIOS extendida:

Fijaos que para empezar, este código mete datos en el inicio de SLTWRK, así a saco. Es decir, en vez de averiguar primero cuál es su número de slot y a partir de ahí calcular cuál es la zona de SLTWRK que le toca, pues no, usa directamente la zona destinada al slot 0-0. ¡Con dos cojones arrojo y valentía!

Además, no se guarda el primer byte del contenido previo del gancho. Se guardan el segundo, tercero y cuarto, asumiendo que contienen un número de slot y una dirección de memoria. Y claro, siguiendo esta lógica, cuando toca llamar al gancho antiguo, pues esto es lo que hace:

Toma castaña pilonga. Naturalmente, si se te ocurre poner un gancho que NO sea un salto interslot, pues tienes un cuelgue monumental en cuanto intentes usar el modo Kanji.

Aún hay cosas que no me quedan claras. Por ejemplo no sé por qué no funciona con la DenYoNet, que modifica el gancho de la BIOS extendida pero poniendo un salto interslot en principio "legal". Sea como fuere, esto es un putadón serio y molesto inconveniente.

Entonces, ¿es imposible twitear en japonés desde un Turbo-R? Bueno, se puede, dando un rodeo. La estrategia a seguir sería:
  1. Activar el modo Kanji y volcar el mensaje a enviar en un fichero de texto:


  2. Resetar el MSX e instalar el InterNestor (o no, si usas DenYoNet).

  3. SIN activar el modo Kanji, enviar el mensaje aprovechando la capacidad del MSX trivial tweeter para leer la salida de otro programa:



En efecto, un soberano coñazo un procedimiento ciertamente engorroso. No creo que a nadie le den muchas ganas de usar el programa en estas condiciones.

En fin, cuando pueda ya investigaré a ver si el problemilla este se puede solucionar. Mientras tanto habrá que olvidarse de twitear (fluidamente al menos) en japonés desde un MSX (Turbo-R al menos, no estoy seguro de si pasa en más modelos).

martes, 15 de junio de 2010

Pseudo-enumeraciones con valores de cadena en C#

El lenguaje C# nos permite definir enumeraciones para dotar de un nombre significativo a una serie de valores numéricos. En ocasiones nos encontramos con que nos iría bien poder definir enumeraciones cuyos valores no sean numéricos (típicamente cadenas), y en ese caso podemos definir constantes, pero por desgracia las constantes se definen a nivel de clase y por tanto no son reutilizables.

La buena noticia es que podemos crear algo parecido a enumeraciones con tipos no numéricos sin más que definir una clase con campos estáticos de sólo lectura. Verbigracia:

Lo malo es que perdemos todas las capacidades de manipulación de claves y valores que nos ofrece la clase Enum, pero si realmente nos hace falta tal cosa, podemos montarnos a mano los métodos que necesitemos usando un poco de reflexión (ejemplo para .NET 3.5 o superior, que como soy muy vago uso LINQ):

Con unas modificaciones mínimas, esto sirve para enumeraciones de cualquier tipo, no sólo de cadenas. O también se podría modificar la clase StringEnum para que sea genérica, cosa que dejo como ejercicio para el (hipotético) lector.

viernes, 11 de junio de 2010

Inicialización de WebContext en un proyecto de RIA services

Cuando usamos la autenticación integrada en un proyecto de RIA Services mediante la inclusión de una clase de tipo Authentication Domain Service al proyecto de servidor, podemos acceder a los servicios de autenticación y al usuario actual desde la aplicación Silverlight usando la clase WebContextBase. Si nuestra solución es del tipo "Aplicación de RIA services", Visual Studio genera automáticamente una clase llamada WebContext que nos facilita el acceso a WebContextBase. Al inicio de la aplicación tenemos que añadir una instancia de WebContext a la lista de objetos persistentes de la aplicación, lo cual puede hacerse en el constructor de App.xaml.cs tal que así (suponiendo que usamos autenticación por formularios):

A partir de ese momento podemos acceder a los servicios de autenticación mediante WebContext.Current.

Peeeero, si en vez de una aplicación RIA estamos usando una biblioteca de clases RIA, la cosa se complica un pelín, aunque no mucho. Para empezar, tenemos que crear la clase WebContext a mano, lo cual no tiene mucho misterio:

Además, en el código de inicialización hay que añadir una línea más, que crea una instancia explícita de la clase DataContext correspondiente (suponiendo que el servicio se llama AuthenticationDomainService):

Hala, ya podeis RIAcizar tranquilos incluso desde bibliotecas de clases.

viernes, 1 de septiembre de 2006

Recursos incrustados y propiedades extendidas

Los recursos incrustados (embedded resources in inglish) son un mecanismo que nos permite incluir ficheros dentro de un ensamblado. Constituyen una forma limpia y eficaz de distribuir ficheros con una aplicación, cuando no queremos que dichos ficheros pululen por el directorio de la aplicación y puedan por tanto ser chafardeados por los juanquercillos de turno (deberán ser juanquers de cierta categoría si quieren ver algo).

Incluir recursos incrustados desde Visual Studio 2005 es muy sencillo: basta agregar (o crear) los ficheros deseados en el proyecto, abrir la ventana de propiedades de los mismos, y en Acción de generación seleccionar Recurso incrustado. Para recuperar estos ficheros una vez generado el ensamblado, viene al rescate la clase Assembly, en concreto sus métodos GetManifestResourceNames y GetManifestResourceStream.

Veamos un ejemplo. Tomemos un proyecto chorra tal que este:


Supongamos que queremos que todo lo que hay bajo el directorio DatabaseChangeScripts se almacene en el ensamblado como recursos incrustados. No tenemos más que seleccionar todos los ficheros (ya sabes, Shift o Ctrl y clics a discteción), botón derecho, propiedades y:


El siguiente código muestra cómo obtener la lista de todos los recursos incrustados del ensamblado:

Si copias este código en el método Main de Program.cs, al ejecutar el proyecto desde el propio Visual Studio verás lo siguiente en la ventana de resultados:


Fíjate en el interesante detalle de que la jerarquía de carpetas se mantiene en el nombre del recurso. Esto da pie a numerosas e interesantes piruladas, como la que os voy a mostrar ahora. Pero antes, veamos cómo recuperar el contenido de un recurso incrustado una vez conocido su nombre:

En este caso, suponemos que el recurso en cuestión es un fichero de texto, por tanto ponemos su contenido en una cadena.

Pues bien, una estructura como la del ejemplo es la que yo uso para mantener un control de versiones en la base de datos que usan las aplicaciones en las que trabajo. Cada vez que necesito hacer un cambio con respecto a la estructura que tiene la base de datos ya instalada al cliente, creo una nueva carpeta de versión y pongo dentro todas las secuencias de comandos SQL necesarias para la actualización. Al inicio de la aplicación compruebo la versión de la base de datos, y si no es la última, leo las secuencias de comandos incrustadas y las ejecuto, tras lo cual actualizo el número de versión de la base de datos (por supuesto, todo con las pertinentes transacciones de por medio, de forma que nunca queden actualizaciones a medias).

Para guardar el número de versión en la base de datos uso una propiedad extendida. Las propiedades extendidas de SQL Server son atributos globales a nivel de base de datos, no dependen de ninguna tabla ni procedimiento almacenado y constan de un nombre y un valor (en realidad ambién es posible establecer propiedades extendidas para otros objetos, no sólo para las propias bases de datos).

Para crear una propiedad extendida hay que usar el siguiente procedimiento almacenado del sistema (el nombre y el valor son ejemplos):

Para borrar una propiedad extendida existente:

Para modificar el valor de una propiedad extendida:

Finalmente, para consultar el valor de una propiedad extendida:

Si tienes el SQL Server Management Studio, también puedes controlar las propiedades extendidas visualmente, mediante la ventana de propiedades de la base de datos:


Pues hala, dada la tremenda utilidad de los recursos incrustados, incrustaos bien esta parrafada en el cerebro (¿lo pillais? ¿Lo pillais? Bueno, al menos no me pegueis muy fuerte...)

lunes, 28 de agosto de 2006

La ley de Demetrio

He aquí un claro ejemplo de lo gracioso que puede llegar a ser traducir frases en inglés. En este caso, todo un señor paradigma de la programación orientada a objetos (The Law of Demeter) nos queda reducido a algo que parece el título de una película setentera de Pajares y Esteso.

La susodicha ley especifica un método para construir clases lo más desacopladas e independientes posible, de forma que la reutilización de componentes y el mantenimiento de aplicaciones resulten tareas algo más sencillas. Concretamente, la ley dice que un método de una clase sólo debería invocar métodos pertenecientes a:
  1. El propio objeto.
  2. Los objetos creados por el método.
  3. Los objetos pasados al método como parámetros.
  4. Los campos y propiedades del propio objeto.
Si seguimos esta ley a rajatabla, esto implica, por ejemplo, que no podemos invocar métodos de objetos que nos son devueltos al invocar otros métodos. Esto puede llegar a ser complejo de cumplir.

Otra consecuencia más "razonable" es la ausencia de variables globales. La información de contexto apropiada debería estar contenida en el propio objeto como una propiedad. Aunque esto también parece complejo de cumplir, ahora veremos un ejemplo de cómo hacerlo de forma sencilla.

Tomemos por ejemplo el recurso global por excelencia de cualquier aplicación (por lo menos del tipo de aplicación que yo desarrollo): la conexión a la base de datos. Supongamos que necesitamos un objeto SqlConnection omnipresente, accesible desde cualquier lugar de la aplicación. Lo típico en estos casos es crear una clase estática de estado y almacenar la conexión como una propiedad de la misma. Entonces, las clases que necesitan acceso a la base de datos no tienen más que leer la susodicha propiedad de la susodicha clase.

Pues bien, según Demetrio, esto está MAL.

En primer lugar, la conexión no debería ser accesible a cualquier clase de la aplicación, sino sólo a aquellas de la capa de acceso a datos. Pero eso es otro tema, y tampoco quiero ponerme excesivamente repelente.

En segundo lugar, toda clase que necesite acceso la base de datos debe tener una propiedad que albergue la conexión, de esta guisa:

De esta forma, si en el futuro queremos reutilizar la clase en otro proyecto, podremos hacerlo sin problemas, puesto que no habrá ninguna dependencia de datos globales.

Y tu dirás, "¡Pero yo creo instancias de esa clase en mil sitios en mi código! ¿Tengo que establecer la conexión cada vez? ¡Qué pesadilla!" Y razón no te falta. Pero existe una solución muy sencilla: crear una propiedad estática que contendrá el valor predeterminado para la propiedad, es decir, el valor que tendrán las instancias nuevas si no se especifica lo contrario. Verbigracia:

Ahora sólo tienes que establecer el valor de la propiedad estática una vez, en el método Main que da inicio a la aplicación:

...y si para una instancia en particular quieres establecer otra conexión, puedes hacerlo tranquilamente mediante su propiedad Connection.

Y como hoy estás un poco gruñón, volverás a quejarte: "Vale, pero cada vez que creo una clase nueva con acceso a datos, tengo que acordarme de inicializarle la conexión predeterminada en el Main. Y yo tengo muchas clases y bla bla..." Tranquiiilo, que esto también tiene una solución facilona. Es hora de que la reflexión venga al rescate.

No, no se trata de que nos volvamos filósofos de repente. La reflexión es el mecanismo de .NET que nos permite obtener información sobre los componentes de cualquier ensamblado: podemos listar las clases existentes, los miembros de una clase, invocar métodos especificando su nombre, etc.

Usando reflexión, podemos decirle a nuestra aplicación lo siguiente: "Búscame todas las clases del ensamblado actual que tengan una propiedad llamada DefaultConnection, y les estableces la propiedad con este valor". Dicho y hecho:

Una solución más limpia sería crear un interfaz (por ejemplo IConnectable) que tuviera como propiedad la conexión y su alter-ego estático, y hacer que las clases con derecho a acceso a la base de datos implementaran ese interfaz. Pero eso te lo dejo como ejercicio (que además ya estoy cansado de escribir).

Conclusión: haz caso a Demetrio, que es más viejo que tú y sabe mucho.

Más información aquí.