.net - pattern - singleton c# example code
Global vs Singleton en.NET (6)
Claro que hay una manera. Primero, póngase al día con la inyección de dependencia y técnicas similares, y lea sobre la 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 un patrón Singleton.
A continuación, todos sus servicios DAL obtendrán una referencia al IConfigurationService
:
class FooDal : IFooDal
IConfigurationService ConfigurationService
para que ahora puedan usar IConfigurationService.ConnectionString
para obtener la cadena de conexión.
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 estar almacenada en algún archivo / configuración se pasa como un parámetro de línea de comando al inicio O la base de datos se busca en el momento en que se inicia la aplicación.
Bueno, es 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 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 es usar 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 forma de hacer esto?
Actualización: no tengo un archivo de configuración e incluso si lo hiciera, necesitaría que la cadena de conexión se lea una vez durante la vida de la instancia de la aplicación. ¿Puedes elaborar en la parte "inyectarlo en cualquier clase"
Depende.
Tenga en cuenta que un singleton:
- hace cumplir que exactamente existirá 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, pasar 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 deba 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 menos problemático y no generará los mismos problemas de sincronización / simultaneidad / escalabilidad que el estado mutable global.
Pero a menudo, puede ser preferible tenerlo como una variable local que pasa a los componentes que lo necesitan. Esto se puede hacer de forma manual, simplemente pasándolo al constructor de un objeto que necesita acceso a la base de datos o, en cierta medida, puede ser automatizado 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 las 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 se refactoriza demasiado la base de código completa.
También se vuelve más difícil probar la unidad, una vez más porque el código ni siquiera se compilará a menos que existan algunos datos externos que idealmente desearíamos dejar fuera de nuestra prueba.
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 sin configuración e inyectarla en cualquier clase que lo necesite. Dependiendo del tamaño de su aplicación, un contenedor IoC como Unity o Castle Windsor puede ayudar, pero ciertamente no es una parte requerida de la solución.
Si eso no es una opción y estás atascado manteniendo el 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 has sugerido.
Actualización: solo para aclarar, olvide todo lo relacionado con los contenedores de IoC, "Inject" es solo una forma elegante de decir "pasar como un parámetro" (ya sea al constructor de la clase, a través de una propiedad, o lo que sea).
En lugar de tener una clase de acceso a datos para pedir la cadena de conexión (desde algún tipo de global o singleton), haga que pase a través del constructor o una propiedad.
Actualización n. ° 2: creo que todavía hay un malentendido 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 en ese blog) detallan una serie de razones por las cuales este último es mejor que el anterior (no menos importante porque el primero es casi imposible de probar en una unidad).
Sí, en algún lugar tendrá que haber un tipo estático responsable de tomar la cadena de conexión en primer lugar, pero el punto es que tus dependencias con los métodos estáticos están limitadas a ese punto (que probablemente será tu Método principal en el proceso de arranque de su aplicación), en lugar de rociado en toda la base de código.
Si todo lo que está almacenando es una cadena (posiblemente junto con otras configuraciones de aplicaciones globales), solo usaría una clase estática con un montón de propiedades para contener todo eso. Un singleton es más trabajo de código y mantenimiento, pero es un trabajo innecesario en este caso, ya que su clase de propiedades no va a hacer nada; solo sostén cosas.
como @Anton dijo, necesitas tener una interfaz que exponga algo como
interface IConfigurationService
string ConnectionString
Entonces, cuando una de las personas de su clase necesita su cadena de conexión, usted le proporciona una implementación de IConfigurationService en la construcción que contiene la cadena válida. Debe encontrar un lugar válido para crear su implementación cuando se inicie la aplicación (probablemente el momento en que obtendrá la dirección de la base de datos) y pasarla a la clase que lo necesite.
Aunque podría parecer mucho trabajo en comparación con singleton o global, esto reducirá el acoplamiento en el código, mejorando la reutilización y facilitando las pruebas unitarias, y es bastante sencillo una vez que te convences de que los globales son (en su mayoría) malvados :)
Como se mencionó anteriormente, hay un contenedor IoC que proporcionará el marco para hacerlo, pero puede ser excesivo en su caso, además es bueno usar el patrón para usted antes de dejar el trabajo en una "caja mágica"
es agradable ver tantas soluciones creativas para un problema tan simple ;-)
hechos destacados, de la pregunta OP:
- la cadena de conexión se pasa en la línea de comandos
- muchos otros componentes pueden necesitar usar la cadena de conexión
entonces, no hay forma de evitar el uso de un elemento estático ; no importa si se trata de una variable global (difícil de hacer en .NET, en realidad, sin una clase adjunta), una clase estática o un singleton. 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 comando.
Cada 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 direccionamiento indirecto.
No estoy diciendo que no desee disfrazar 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 se describe .