Pregunta

Tengo una situación muy común aquí. Y durante años no he encontrado si lo que estoy haciendo es CORRECTO según los estándares de la industria. Considere una aplicación que se conecta a la base de datos, pero donde la cadena de conexión en lugar de almacenarse en algún archivo / configuración se pasa como un parámetro de línea de comando al inicio O se explora la base de datos en el momento en que se inicia la aplicación.

Bueno, se hace necesario guardar esa cadena de conexión en algún lugar dentro del alcance de la aplicación. La forma más común en que lo he visto hacer es un módulo o clase global con métodos get / set para guardar la cadena de conexiones. Otra forma en que lo haría sería usando Singleton. Cualquiera de las opciones, mi DAL puede acceder a la cadena de conexión cuando lo necesita a través de un método GetConnectionString.

¿Hay una MEJOR manera de hacer esto?

Actualización: no tengo un archivo de configuración e incluso si lo tuviera, necesitaría leer la cadena de conexión una vez durante la vida de la instancia de la aplicación. ¿Puedes elaborar el & "; inyectarlo en cualquier clase &"; parte

¿Fue útil?

Solución

En general, el estado global, ya sea una clase global o un singleton, debe evitarse siempre que sea posible.

La solución ideal sería que su aplicación cargue la cadena de conexión fuera de la configuración y inyecte en cualquier clase que lo necesite. Dependiendo del tamaño de su aplicación, un IoC contenedor como Unity o Castle Windsor puede ayudar, pero ciertamente no es una parte necesaria de la solución.

Si esa no es una opción y está atrapado en el mantenimiento del estado global (debido a la base de código existente o lo que sea), no sé si hay una gran diferencia entre los dos enfoques que ha sugerido.

Actualización: Solo para aclarar, olvide todo lo relacionado con los contenedores IoC por ahora, " Inject " es solo una forma elegante de decir " pasar como parámetro " (ya sea al constructor de la clase, o mediante una propiedad, o lo que sea).

En lugar de tener una clase de acceso a datos, debe solicitar la cadena de conexión (de algún tipo de global o singleton), pasarla a través del constructor o una propiedad.

Actualización # 2: creo que todavía hay algunos malentendidos sobre lo que implica este enfoque.

Básicamente se trata de si su clase de acceso a datos se ve así:

public class DataAccessClass
{
    public DataAccessClass()
    {
        _connString = SomeStaticThing.GetConnectionString();
    }
}

o

public class DataAccessClass
{
    public DataAccessClass(string connString)
    {
        _connString = connString;
    }
}

Estos artículos (y, de hecho, muchos de los artículos de ese blog ) detalla una serie de razones por las cuales este último es mejor que el primero (sobre todo porque el primero es casi imposible de probar por unidad).

Sí, en algún lugar tendrá que haber algún tipo estático responsable de tomar la cadena de conexión en primer lugar, pero el punto es que sus dependencias de métodos estáticos se limitan a eso un punto (que probablemente sea su método principal en el proceso de arranque de su aplicación), en lugar de esparcirse por toda su base de código.

Otros consejos

es agradable ver tantas soluciones creativas a un problema tan simple ;-)

hechos destacados, de la pregunta de OP:

  1. la cadena de conexión se pasa en la línea de comandos
  2. muchos otros componentes pueden necesitar usar la cadena de conexión

entonces, no hay forma de evitar el uso de un elemento estático ; si es una variable global (difícil de hacer en .NET, realmente, sin una clase que lo encierra), una clase estática o un singleton realmente no importa. La solución de ruta más corta sería una clase estática inicializada por la clase Program que procesó la línea de comandos.

Cualquier otra solución aún requeriría acceso estático a la cadena de conexión pasada , aunque pueden ocultar esto detrás de una o más capas de indirección.

No estoy diciendo que no quieras vestir la solución básica con algo más elegante, pero no es necesario y no elimina la naturaleza fundamentalmente estática / global de la cadena de conexión como descrito .

Si todo lo que está almacenando es una cadena (posiblemente junto con un montón de otras configuraciones de aplicaciones globales), simplemente usaría una clase estática con un montón de propiedades para contener todo eso. Un singleton es más código & Amp; trabajo de mantenimiento, pero es un trabajo innecesario en este caso, ya que su clase de propiedades no va a HACER nada; solo sostén las cosas.

Claro que hay una manera. En primer lugar, póngase al día con inyección de dependencia y técnicas similares, y lea sobre capa de servicio ya que eso es lo que necesita aquí.

En lo que respecta a una capa de servicio, necesita algún tipo de Servicio de configuración, que será el módulo que las partes de su aplicación consultarán para obtener información de configuración: cadenas de conexión, URI, etc.:

interface IConfigurationService
    string ConnectionString
    bool IntegratedSecurity
    bool EncryptProfiles

Debería ser bastante sencillo que solo haya una instancia de IConfigurationService en su sistema, pero esto se logra con un contenedor DI de elección, no con el patrón Singleton.

A continuación, todos sus servicios DAL obtendrán una referencia a IConfigurationService.ConnectionString:

class FooDal : IFooDal
    IConfigurationService ConfigurationService

para que ahora puedan usar <=> para obtener la cadena de conexión.

Depende.

Tenga en cuenta que un singleton:

  • exige que exista exactamente una instancia de la clase, y
  • proporciona acceso global

Un global solo proporciona acceso global, pero no garantiza el número de instancias.

Y la opción final, por supuesto, es usar una variable local. Es decir, pase la información de configuración como un parámetro al constructor o donde sea necesario.

Elegir entre los dos primeros debería ser algo fácil. ¿Es un desastre si existen dos instancias de la clase? Yo diría que probablemente no. Es posible que desee crear una instancia de mi propia clase de configuración para proporcionar opciones separadas a un componente específico de la aplicación, o para inicializar mis pruebas unitarias.

Hay muy pocos casos en los que necesita para garantizar que exista exactamente una instancia. Por esta razón, un global puede ser una mejor opción que un singleton.

La elección entre local y global es más complicada. El estado mutable global generalmente se evita mejor. El estado Inmutable es un problema menor y no generará los mismos problemas de sincronización / concurrencia / escalabilidad que el estado global mutable.

Pero a menudo, puede ser preferible tenerlo como una variable local que se pasa a los componentes que lo necesitan. Esto se puede hacer manualmente, simplemente pasándolo al constructor de un objeto que necesita acceso a la base de datos, o hasta cierto punto se puede automatizar con un contenedor IoC.

Pero en cualquier caso, si no es global, entonces su código se vuelve más genérico y más portátil. Si tiene dependencias ocultas en clases y datos globales que deben existir para que el código funcione, entonces no se puede usar fácilmente en otros proyectos, o si refactoriza demasiado la base de código completa.

También se vuelve más difícil realizar una prueba unitaria, nuevamente porque el código ni siquiera se compilará a menos que existan algunos datos externos que nos gustaría dejar fuera de nuestra prueba.

como dijo @Anton, debes tener una interfaz que exponga algo como

interface IConfigurationService
    string ConnectionString

Luego, cuando uno de su clase necesita su cadena de conexión, debe proporcionarle una implementación de IConfigurationService en la construcción que contenga la cadena válida. Debe encontrar un lugar válido para crear su implementación cuando se inicie su aplicación (probablemente el momento en que obtendrá la dirección de su base de datos) y pasarla a la clase que la necesita.

Aunque parezca mucho trabajo en comparación con singleton o global, esto reducirá el acoplamiento en su código, por lo tanto, mejorará la reutilización y facilitará las pruebas unitarias, y es bastante sencillo una vez que se convenza de que los globales son (en su mayoría) malvados: )

Como se mencionó anteriormente, hay un contenedor de IoC que proporcionará el marco para hacerlo, pero puede ser excesivo en su caso, además es bueno usar el patrón por sí mismo antes de dejar que el trabajo sea un " magic cuadro "

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top