Question

J'ai une situation très commune ici. Et pendant des années, je n'ai pas trouvé si ce que je fais est DROIT selon les normes de l'industrie. Pensez à une application qui se connecte à la base de données, mais où la chaîne de connexion au lieu d'être stockée dans un fichier / paramètre est passée en tant que paramètre de ligne de commande. au démarrage OU la base de données est consultée au moment du démarrage de l'application.

Eh bien, il devient nécessaire de sauvegarder cette chaîne de connexion quelque part dans le champ d'application de l'application. Le moyen le plus courant que j'ai vu est un module ou une classe globale avec des méthodes get / set pour enregistrer la chaîne de connexion. Une autre façon de le faire serait d'utiliser Singleton. L'une ou l'autre de ces options, mon DAL peut accéder à la chaîne de connexion à l'aide d'une méthode GetConnectionString.

Existe-t-il une MEILLEURE façon de procéder?

Mise à jour: Je ne dispose pas d'un fichier de configuration. Même dans ce cas, il me faudrait une fois la chaîne de connexion lue pour la durée de vie de l'instance d'application. pouvez-vous préciser le " l'injecter dans n'importe quelle classe " partie

Était-ce utile?

La solution

Dans les états globaux généraux, qu’il s’agisse d’une classe globale ou d’un singleton, vous devez éviter autant que possible.

La solution idéale serait que votre application charge la chaîne de connexion en dehors de config et injecter dans toutes les classes qui en ont besoin. En fonction de la taille de votre application, un conteneur IoC semblable à Unity ou Castle Windsor peut aider, mais n’est certainement pas un élément indispensable de la solution.

Si ce n'est pas une option et que vous ne pouvez pas conserver l'état global (à cause de la base de code existante ou autre), j'ignore s'il existe une différence énorme entre les deux approches que vous avez suggérées.

Mise à jour : pour clarifier les choses, oubliez tout ce qui concerne les conteneurs IoC pour le moment, & "Injecter &"; n’est qu’un moyen de fantaisie de dire " passer en paramètre " (soit au constructeur de la classe, soit via une propriété, ou autre chose).

Plutôt que d'avoir une classe d'accès aux données, vous devez demander la chaîne de connexion (à partir d'une sorte de global ou d'un singleton), la faire passer via le constructeur ou une propriété.

Mise à jour n ° 2: Je pense qu'il reste un malentendu quant à la signification de cette approche.

Cela revient essentiellement à savoir si votre classe d'accès aux données ressemble à:

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

ou

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

Ces articles (et en fait, de nombreux articles de ce blog ) détaille un certain nombre de raisons pour lesquelles ce dernier est meilleur que le premier (notamment parce que le premier est presque impossible à tester unitairement).

Oui, à un endroit , il faudra peut-être un type statique responsable de saisir la chaîne de connexion, mais le fait est que vos dépendances sur les méthodes statiques se limitent à cela un seul endroit (qui sera probablement votre méthode principale lors du processus d’amorçage de votre application), plutôt que parsemé de toute votre base de code.

Autres conseils

ça fait plaisir de voir autant de solutions créatives à un problème aussi simple ;-)

faits saillants, de la question OP:

  1. la chaîne de connexion est transmise à la ligne de commande
  2. de nombreux autres composants peuvent avoir besoin d'utiliser la chaîne de connexion

Donc, l'utilisation d'un élément statique n'existe pas. ; qu'il s'agisse d'une variable globale (difficile à faire dans .NET, vraiment, sans classe englobante), d'une classe statique ou d'un singleton, cela n'a pas d'importance. La solution la plus courte serait une classe statique initialisée par la classe Program ayant traité la ligne de commande.

Toutes les autres solutions nécessitent toujours un accès statique à la chaîne de connexion transmise , bien qu'elles puissent le masquer derrière une ou plusieurs couches d'indirection.

Je ne dis pas que vous ne voulez pas habiller la solution de base avec quelque chose de plus sophistiqué, mais ce n'est pas nécessaire et cela n'élimine pas la nature fondamentalement statique / globale de la chaîne de connexion en tant que décrit .

Si tout ce que vous stockez est une chaîne (éventuellement avec de nombreux autres paramètres d'application globaux), j'utiliserais simplement une classe statique avec un tas de propriétés pour contenir tout cela. Un singleton est plus de code & Amp; travaux d’entretien, mais c’est un travail inutile dans ce cas, car votre classe de propriétés ne fera rien; tenez juste les choses.

Bien sûr, il y a un moyen. Tout d’abord, familiarisez-vous avec injection de dépendance et des techniques similaires, et consultez couche de service puisque c'est ce dont vous avez besoin ici.

En ce qui concerne une couche de service, vous avez besoin d'un type de service de configuration, qui sera le module que certaines parties de votre application interrogeront pour obtenir des informations de configuration - chaînes de connexion, URI, etc.:

interface IConfigurationService
    string ConnectionString
    bool IntegratedSecurity
    bool EncryptProfiles

Il devrait être assez simple qu'il n'y ait qu'une seule instance de IConfigurationService dans votre système, mais ceci est obtenu avec un conteneur DI de choix, pas avec le modèle Singleton.

Ensuite, tous vos services DAL recevront une référence au IConfigurationService.ConnectionString:

class FooDal : IFooDal
    IConfigurationService ConfigurationService

afin qu'ils puissent maintenant utiliser <=> pour récupérer la chaîne de connexion.

Cela dépend.

N'oubliez pas qu'un singleton:

  • force à ce qu’une seule instance de la classe existe et
  • fournit un accès global

Un global fournit uniquement un accès global, mais ne garantit pas le nombre d'instanciations.

Et la dernière option, bien sûr, consiste à utiliser une variable locale. Autrement dit, transmettez les informations de configuration sous forme de paramètre au constructeur ou à l'endroit souhaité.

Choisir entre les deux premiers devrait être assez facile. Est-ce un désastre s'il existe deux instances de la classe? Je dirais probablement pas. Je souhaiterais peut-être instancier ma propre classe de configuration pour fournir des options distinctes à un composant spécifique de l'application ou pour initialiser mes tests unitaires.

Il existe très peu de cas où vous avez besoin de garantir l’existence d’une seule instance. Pour cette raison, un global peut être un meilleur choix qu'un singleton.

Le choix entre local et global est plus délicat. Il est généralement préférable d'éviter les états mutables globaux. L’état immuable pose moins de problèmes et n’entraînera pas les mêmes problèmes de synchronisation / concurrence / évolutivité que l’état mutable global.

Mais souvent, il peut être préférable d’en faire une variable locale que vous transmettez aux composants qui en ont besoin. Cela peut être fait manuellement, en le passant simplement au constructeur d'un objet nécessitant un accès à une base de données, ou dans une certaine mesure automatisé avec un conteneur IoC.

Mais dans tous les cas, s'il n'est pas global, votre code devient alors plus générique et plus portable. Si il doit exister pour que le code fonctionne, il contient des dépendances masquées sur les classes et des données globales. Dans ce cas, il ne peut pas être facilement utilisé dans d'autres projets, ou si vous refactorisez trop la base de code entière.

Il devient également plus difficile de procéder à des tests unitaires, encore une fois, car le code ne sera même pas compilé à moins de disposer de données externes que nous aimerions idéalement laisser de côté de notre test.

comme @Anton a dit, vous devez avoir une interface qui expose quelque chose comme

interface IConfigurationService
    string ConnectionString

Ensuite, chaque fois qu'une de vos classes a besoin de votre chaîne de connexion, vous lui fournissez une implémentation de IConfigurationService lors de la construction contenant la chaîne valide. Vous devez trouver un emplacement valide pour créer votre implémentation au démarrage de votre application (probablement l'heure à laquelle vous obtiendrez l'adresse de votre base de données) et le transmettre à la classe qui en aura besoin.

Même si cela peut sembler beaucoup de travail comparé à singleton ou global, ceci réduira le couplage dans votre code, améliorant ainsi la réutilisabilité et facilitant les tests unitaires, et est assez simple une fois que vous vous êtes convaincu que les globals sont (principalement) pervers: )

Comme mentionné précédemment, il existe un conteneur IoC qui fournira le cadre pour le faire, mais il pourrait être excessif dans votre cas, plus il est agréable d'utiliser le modèle pour vous-même avant de laisser le travail à une magie & case "

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top