Novembre 2016

Volume 31, numéro 11

Cet article a fait l'objet d'une traduction automatique.

À la pointe : Code First et initialisation de la base de données

Par Dino Esposito | Novembre 2016

Dino EspositoBien que le terme « DevOps » est relativement nouveau et a atteint récemment englobe de nombreuses activités plus — notamment automatisé de test et de déploiement, je pense que le premier exemple d’une opération automatisable développeur est identique à celle du logiciel lui-même. Je fais référence à la possibilité pour créer et initialiser la base de données lors de l’installation d’application spécifique. Plusieurs éditeurs de logiciels développement des systèmes verticales qui sont ensuite vendus à une variété de clients et adaptées à leurs besoins.

Les aspects qui peuvent être personnalisés varient selon les caractéristiques du produit et du domaine d’entreprise, mais utiliser dire que n’importe quelle application logicielle verticale doit au minimum à une base de données spécifiques au client. Par conséquent, la base de données doit être créée avec les tables et les schémas requis par le contexte et remplis avec des données ad hoc.

Pas toutes les tâches requises peuvent toujours être automatisées et intégrées dans le produit lui-même. Imaginons, par exemple, l’importation des données existantes. Si les données à importer se trouvent dans les fichiers Excel ou les bases de données héritées, les probabilités sont qu’une importation doit être générée de manière à traiter les données et les charger dans le nouveau stockage. Toutefois, si vous avez employées Entity Framework 6.x Code First dans la couche d’accès aux données de l’application, au moins la création de schémas de base de données et de tables est facile à automatiser et peut arriver en toute transparence de la première exécution de l’application.

Dans cet article, je résumerai certaines fonctionnalités qui sont disponibles dans Code First depuis le début de la perspective d’une application de plusieurs clients. En particulier, je me concentrerai sur comment créer et remplir une base de données et comment définir par programme la chaîne de nom et de connexion. 

Disposition de la base des schémas de Table

Supposons que vous avez un tout nouveau projet Visual Studio déjà lié au package NuGet Entity Framework 6.x. L’étape suivante, que vous souhaiterez peut-être prendre consiste à créer une bibliothèque de classes d’accès aux données ou au moins un dossier distinct dans le projet actuel enregistre tous les fichiers d’une certaine façon approuvent l’utilisation de la fonctionnalité d’accès aux données. En fonction des règles d’Entity Framework, vous devez disposer d’une classe DbContext qui représente le point d’entrée dans le sous-système de gestion de données de l’application. Voici un exemple de ce type une classe spécifique à l’application :

public class RegionsContext : DbContext
{
  ...
}

Aux yeux de l’application, la classe dérivée de DbContext n’est pas plus et non inférieure à la base de données. La classe doit exposer un certain nombre de propriétés DbSet < T >, une pour chaque collection d’entités gérées par l’application. Une propriété DbSet < T > est logiquement équivalente à une table dans une base de données physique :

public class RegionsContext : DbContext
{
  public DbSet<Region> Regions { get; set; }
}

L’effet net de l’extrait de code ici est de permettre l’application de fonctionner avec une base de données relationnelle qui contient une table nommée régions. Qu’en est-il du schéma de la table ? Le schéma est déterminé par la présentation publique de la classe de la région. Code première offre à la fois une plage des attributs et la syntaxe fluent pour définir un mappage entre les propriétés de la classe et les colonnes de la table sous-jacente. De la même façon, vous pouvez définir des index, les clés primaires, les colonnes d’identité, les valeurs par défaut, et tout ce qui la base de données vous permet de configurer les tables et les colonnes. Voici une version minimale de la classe de zone :

public class Region
{
  public Region()
  {
    Languages = "EN";
  }
  [Key]
  public string RegionCode { get; set; }
  public string RegionName { get; set; }
  public string Languages { get; set; }
  ...
}

En l’état, tous les enregistrements de la table Regions aura trois colonnes nvarchar (max) (code de région et RegionName langues) et code de région sera définie en tant que colonne de clé primaire. En outre, n’importe quelle instance nouvellement créée de la classe de la région a la valeur « EN » est défini sur la propriété de langues. Il s’agit d’un moyen pour vous assurer que « EN » est la valeur par défaut pour la colonne chaque fois qu’un nouvel enregistrement est ajouté par le biais du code. Notez cependant que définir une valeur dans le constructeur d’une solution de Code First, comme c’est le cas ici n’ajoute pas automatiquement des liaisons de valeur par défaut dans la configuration de base de données sous-jacente.

Nom de la base de données

Dans une solution de Code en premier, toutes les connexions à la base de données passent par la classe dérivée de DbContext, qui ouvre et ferme les connexions de manière appropriée lors de l’exposition toujours publiquement la connexion en tant que propriété de votre code prendre le contrôle total sur les opérations d’ouverts et de fermeture. Ce que sur les détails de la chaîne de connexion et, plus important, comment fournir des détails d’une manière paramétrique ?

Lorsque vous créez une classe dérivée de DbContext, vous devez fournir un constructeur. Voici un exemple courant :

public RegionsContext(string conn) : base(conn)
{
  ...
}

La classe DbContext possède un constructeur qui accepte la chaîne de connexion en tant que paramètre, la chose la plus simple à faire est de refléter les fonctionnalités du constructeur sous-jacent via le constructeur de votre classe dérivée. Intelligemment, la classe DbContext contient une logique pour traiter la chaîne passée. Toute chaîne que vous passez qui est sous la forme nom = XXX est supposé pour indiquer que la chaîne de connexion peut être trouvée dans l’entrée XXX dans la section connectionstrings du fichier de configuration de l’application (autrement dit, web.config pour un projet Web). Sinon, n’importe quelle chaîne passée est supposée pour être le nom de la base de données à créer. Dans ce cas, plus les détails de la chaîne de connexion, telles que les informations d’identification et l’emplacement du serveur, doivent se trouver dans le bloc defaultconnectionfactory que de la section entityFramework dans le fichier de configuration. Notez que chaque fois que vous ajoutez le package d’Entity Framework pour un projet Visual Studio, le fichier de configuration est modifié en mode silencieux pour prendre en charge de la section entityFramework. Figure 1 affiche la liste connexe, modifiée un peu par souci de clarté.

Figure 1 exemple de fichier Web.config modifié pour prendre en charge tout d’abord de Code d’Entity Framework

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="entityFramework"
      type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection,..." />
  </configSections>
    <startup>
      <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
  <connectionStrings>
    <!-- Your explicit connection strings -->
  </connectionStrings>
  <entityFramework>
    <defaultConnectionFactory type=
      "System.Data.Entity.Infrastructure.SqlConnectionFactory,
      EntityFramework">
    <parameters>
    <parameter value="Data Source=(local); Integrated Security=True;" />
    </parameters>
  </defaultConnectionFactory>
  <providers>
    <provider invariantName="System.Data.SqlClient"
      type="System.Data.Entity.SqlServer.SqlProviderServices, ..." />
    </providers>
  </entityFramework>
</configuration>

La plupart des exemples que vous découvrez Code First est basée sur une chaîne de connexion fixe et constante référencé à partir du fichier de configuration soit passée explicitement à la classe de contexte. L’effet net est que Code First crée la base de données à l’aide de la connexion fournie chaîne la première exécution de l’application. Examinons un peu plus profondément cet aspect.

La classe DbContext prend en charge quatre stratégies d’initialisation, comme répertorié dans Figure 2.

Stratégies de l’initialisation de la base de données Code First figure 2

Stratégie Description
CreateDatabaseIfNotExists

Vérifie si la base de données existe et qu’il le crée s’il ne peut pas trouver. Si la base de données existe mais a un schéma incompatible il lève une exception.

Remarque : Il s’agit de l’initialiseur par défaut.

DropCreateDatabaseIfModelChanges Crée la base de données si elle n’existe pas. Si la base de données existe mais a un schéma incompatible, puis supprime la base de données crée un nouveau.
DropCreateDatabaseAlways Chaque fois que vous exécutez l’application, il supprime d’une base de données et recrée.
Initialiseur personnalisé

Classe d’initialiseur personnalisé que vous écrivez pour travailler hors simplement le comportement que vous souhaitez qu’aucune des autres options celle que.

Remarque : Vous devez utiliser cette option pour ajouter du contenu principal à la base de données.

Selon le comportement par défaut CreateDatabaseIfNotExists, chaque fois que la classe de contexte est créée, il vérifie si la base de données référencée existe et est accessible. Si ce n’est pas le cas, il le crée. Si la base de données existe et est accessible, mais n’a pas un schéma compatible avec la disposition des classes d’entité publique, il lève une exception. Pour supprimer l’exception, vous devez modifier la classe d’entité ou, plus probablement, modifier le schéma de la base de données via l’interface ou un script de migration d’Entity Framework de programmation de base de données.

Je trouve cette option convient particulièrement lorsque l’application atteint le niveau de production. Cependant, durant la phase de développement, je préfère l’option DropCreateDatabaseIfModelChanges, qui vous protège essentiellement des opérations de maintenance de base de données : Vous vous contentez d’ajuster les classes d’entité comme il convient et Entity Framework résout la base de données la prochaine fois que vous appuyez sur F5 dans Visual Studio. Pour activer la stratégie d’initialisation de votre choix, vous ajoutez la ligne suivante au constructeur de la classe DbContext personnalisée :

Database.SetInitializer<YourDbContext>(
  new DropCreateDatabaseIfModelChanges<YourDbContext>());

Notez que vous pouvez également définir l’initialiseur de base de données dans le fichier de configuration, ce qui peut s’avérer utile si vous prévoyez d’utiliser différentes stratégies de développement et de production.

En résumé, Code First vous permet d’écrire une application qui crée automatiquement de toutes ses tables de base de données de la première fois que vous l’exécutez. En d’autres termes, il vous suffit est copier des fichiers et des fichiers binaires et puis lancez-le. Ce comportement, cependant, fonctionne à son niveau optimal si le système est généré pour un seul client. Lorsque vous disposez d’un système de plusieurs client, nous vous conseillons est utiliser un utilitaire d’installation.

Une des raisons d’adopter une approche légèrement différente est que vous souhaiterez nommer la base de données différemment ; par exemple, vous souhaiterez ajouter un préfixe spécifique au client pour le nom. Figure 3 montre la structure d’un tel utilitaire de ligne de commande. Le programme prend le préfixe du client à partir de la ligne de commande, met en forme le nom de la base de données comme il convient et déclenche ensuite la classe dérivée de DbContext, qui recrée la base de données et la remplit avec des données initiales appropriées.

Nom client spécifique à la figure 3 de la base de données

class Program
{
  static void Main(string[] args)
  {
    var prefix = args[0];
                // boundary checks skipped
    var dbName = String.Format("yourdb_{0}", prefix);
    using (var db = new YourDbContext(dbName))
    {
      // Fill the database in some way
    }
  }
}

Remplissage initial de la base de données

N’importe quel système conçu pour répondre aux besoins de plusieurs clients dans le même domaine d’entreprise doit avoir un nombre de tables dédiés au stockage des options et des préférences de différent les clients. Ces informations doivent être fournies d’une certaine façon lors de l’installation du logiciel. Dans la pratique, partie de la charge initiale de la base de données est partagée par toutes les installations, mais une partie est spécifique au client. La partie qui dépend des données du client est généralement importée à partir de sources externes et nécessite une routine ad hoc, s’il s’agit d’un script quelconque ou code compilé. Selon le contexte, il peut même être hors de réfléchir sur place d’un mécanisme d’injection de dépendance pour généraliser la structure des importateurs au programme d’installation initialise la base de données. Comme contenu de la base de données statique est concerné, cependant, Code First a des services ad hoc à proposer.

Initialiseurs personnalisés

Pour les données de choses dans la base de données pendant le processus d’initialisation, il est nécessaire de créer un initialiseur de base de données personnalisée comme décrit dans Figure 2. Un initialiseur personnalisé est une classe qui hérite d’un des initialiseurs prédéfinis tels que DropCreateDatabaseIfModelChanges. Impérativement uniquement à cette classe substitue la méthode Seed :

public class YourDbInitializer : DropCreateDatabaseAlways<YourDbContext>
{
  protected override void Seed(YourDbContext context)
  {              
    ...
  }
}

Dans l’implémentation de la méthode d’amorçage, vous exécutez le code qui remplit les tables de la base de données à l’aide de DbContext fourni pour y accéder. C’est tout de celui-ci.

Si vous planifiez une application de plusieurs clients, définition d’un initialiseur personnalisé est un bon déplacement, car elle vous offre un point unique de se concentrer sur la forme que la forme initiale de la base de données sur une base par client. L’initialiseur est une classe simple c#, donc elle peut être habilitée à l’aide des outils de l’injection de dépendance pour vous connecter à des éléments spécifiques de la logique que vous pouvez importer des données à partir de simplement où elles se trouvent.

Enfin, les initialiseurs de base de données peuvent être désactivés entièrement afin que le programme d’installation de la base de données reste une opération complètement distincte, peut-être même géré par un autre INFORMATIQUE ou de l’équipe des opérations de développement. Pour indiquer à l’infrastructure de Code First pour ignorer les initialiseurs, vous devez suivre le code dans le constructeur de la classe DbContext personnalisée :

Database.SetInitializer<YourDbContext>(null);

Par exemple, il s’agit d’une option de sécurité à utiliser lors de la publication de mises à jour aux systèmes existants. La désactivation des initialiseurs garantit que vous perdrez jamais les données existantes quelle.

Pour résumer

À la fin de la journée, Code First, écriture d’applications mutualisées et des clients multiples n’est plus difficile que les applications spécialement écrites pour une configuration connue et le client. Tout ce qui est requis est un peu de connaissances sur l’attribution de chaînes de connexion et le processus d’initialisation. Dans Entity Framework Core, les principes fondamentaux, reste inchangées même si les détails de finalement fonctionnement sont différents. En particulier, la nouvelle classe DbContext propose une méthode substituable OnConfiguring par le biais duquel vous connectez le contexte au fournisseur de base de données de choix et la transmettre informations d’identification et ainsi de suite.


Dino Esposito est l’auteur de « Microsoft .NET :  Conception d’Applications pour l’entreprise » (Microsoft Press, 2014) et « Applications Web modernes avec ASP.NET » (Microsoft Press, 2016). Un développeur technique pour les plateformes .NET et Android chez JetBrains, dans le monde entier manifestations du secteur, Esposito partage sa vision du logiciel à software2cents@wordpress.com et sur Twitter : @despos.

Merci à l'expert technique Microsoft suivant d'avoir relu cet article : Andrea Saltarello (andrea.saltarello@manageddesigns.it)
Andrea Saltarello est architecte entrepreneur et les logiciels de Milan, Italie, qui a toujours adore écrire du code des projets réels pour obtenir des commentaires sur ses décisions de conception. En tant que formateur et intervenant, il a plusieurs engagements rémunérés pour les cours et des conférences en Europe, tels que TechEd Europe, DevWeek et architecte logiciel. Il est MVP Microsoft depuis 2003 et a été récemment été nommé directeur régional Microsoft. Il est passionné de musique et est consacré au Depeche Mode, avec lequel il a été amoureux depuis écoute « Tout compte » pour la première fois.