IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Introduction par l'exemple à Entity Framework 5 Code First


précédentsommairesuivant

IV. Etude de cas avec MySQL 5.5.28

IV-A. Installation des outils

Les outils à installer sont les suivants :

  • le SGBD : [http://dev.mysql.com/downloads/] ;
  • un outil d'administration : EMS SQL Manager for MySQL Freeware [http://www.sqlmanager.net/fr/products/mysql/manager/download].

Dans les exemples qui suivent, l'utilisateur root a le mot de passe root .

Lançons MySQL5. ici, nous le faisons à partir de la fenêtre des services Windows [1]. En [2], le SGBD est lancé.

Image non disponible

Nous lançons maintenant l'outil [SQL Manager Lite for MySQL] avec lequel on va administrer le SGBD [3].

Image non disponible
  • en [4], nous créons une nouvelle base ;
  • en [5], on indique le nom de la base ;
Image non disponible
  • en [5], on se connecte en tant root / root ;
  • en [6], on valide l'ordre SQL qui va être exécuté ;
Image non disponible
  • en [7], la base a été créée. Elle doit maintennat être enregistrée dans [EMS Manager]. Les informations sont bonnes. On fait [OK] ;
  • en [8], on s'y connecte ;
  • en [9], [EMS Manager] affiche la base, pour l'instant vide.

Nous allons maintenant connecter un projet VS 2012 à cette base.

IV-B. Création de la base à partir des entités

Nous créons le projet console VS 2012 [RdvMedecins-MySQL-01] [1] ci-dessous :

Image non disponible
  • en [2], on ajoute des références au projet via NuGet ;
Image non disponible
  • en [3], on ajoute la référence EF 5 ;
  • en [4], elle est désormais dans les références ;
Image non disponible
  • en [5], on recommence pour ajouter cette fois [MySQL.Data.Entities] qui est un connecteur ADO.NET pour Entity Framework. Pour trouver le paquetage, on peut se faire aider par la zone de recherche [6] ;
  • en [7], apparaissent deux références [MySQL.Data.Entities] et [MySQL.Data], la dernière étant une dépendance de la première.

Maintenant, on va construire le projet [RdvMedecins-MySQL-01] à partir du projet [RdvMedecins-SqlServer-01].

Image non disponible
  • en [1], on copie les éléments sélectionnés ;
  • en [2], on les colle dans le projet [RdvMedecins-MySQL-01] ;
  • en [3], parce qu'il y a plusieurs programmes avec une méthode [Main], il nous faut préciser le projet de démarrage du projet.

A ce stade, la génération du projet doit réussir. Maintenant, nous allons modifier le fichier de configuration [App.config] qui configure la chaîne de connexion à la base de données et le DbProviderFactory . Il devient le suivant :

[App.config]
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
  </entityFramework>

  <!-- chaîne de connexion-->
  <connectionStrings>
    <add name="monContexte"
         connectionString="Server=localhost;Database=rdvmedecins-ef;Uid=root;Pwd=root;"
         providerName="MySql.Data.MySqlClient" />
  </connectionStrings>
  <!-- le factory provider -->
  <system.data>
    <DbProviderFactories>
      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL"
          type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.5.4.0, Culture=neutral, PublicKeyToken=C5687FC88969C44D"
        />
    </DbProviderFactories>
  </system.data>

</configuration>
  • ligne 17 : la chaîne de connexion à la base MySQL [rdvmedecins-ef] que nous avons créée ;
  • ligne 24 : la version doit correspondre à celle de la référence [MySql.Data] du projet [1] :
Image non disponible

Il y a un peu de configuration également dans le fichier [Entites.cs] où on précise le nom des tables ainsi que le schéma auquel elles appartiennent. Celui-ci peut changer selon les SGBD. C'est le cas ici, où il n'y aura pas de schéma. Le fichier [Entites.cs] évolue comme suit :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
[Table("MEDECINS")]
  public class Medecin : Personne
  {...}

  [Table("CLIENTS")]
  public class Client : Personne
  {...}

  [Table("CRENEAUX")]
  public class Creneau
  {...}

  [Table("RVS")]
  public class Rv
  {...}

Exécutons le programme [CreateDB_01] [2]. On obtient l'exception suivante :

[Exception]
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
Exception non gérée : System.Data.MetadataException: Le schéma spécifié n'est pas valide. Erreurs :
(11,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
(23,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
(33,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
(43,6) : erreur 0040: Le type rowversion n'est pas qualifié avec un espace de noms ou un alias. Seuls les types primitifs peuvent être utilisés sans qualification.
   à System.Data.Metadata.Edm.StoreItemCollection.Loader.ThrowOnNonWarningErrors
()
   ....
   à RdvMedecins_01.CreateDB_01.Main(String[] args) dans d:\data\istia-1213\c#\d
vp\Entity Framework\RdvMedecins\RdvMedecins-MySQL-01\CreateDB_01.cs:ligne 15

La même erreur apparaît quatre fois (lignes 2-5). Le type rowversion fait penser au champ ayant l'annotation [Timestamp] dans les entités :

 
Sélectionnez
1.
2.
3.
[Column("TIMESTAMP")]
    [Timestamp]
    public byte[] Timestamp { get; set; }

Nous décidons de remplacer ces trois lignes par les suivantes :

 
Sélectionnez
1.
2.
3.
    [ConcurrencyCheck]
    [Column("VERSIONING")]
    public DateTime? Versioning { get; set; }

On change le type de la colonne qui passe de byte[] à DateTime? . On fait cela parce que MySQL a un type [TIMESTAMP] qui représente une date / heure et qu'une colonne ayant ce type est automatiquement mise à jour par MySQL à chaque fois que la ligne est mise à jour. Cela nous permettra de gérer les accès concurrents.

L'annotation [Timestamp] ne peut s'appliquer qu'à une colonne de type byte[]. On la remplace par l'annotation [ConcurrencyCheck]. Ces deux annotations gèrent toutes deux la concurrence d'accès. Nous faisons cela pour les quatre entités puis nous réexécutons l'application. Nous obtenons alors l'erreur suivante :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
Exception non gérée : MySql.Data.MySqlClient.MySqlException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'NOT NULL,        `ProductVersion` mediumtext NOT NULL);

ALTER TABLE `__MigrationH' at line 5
   à MySql.Data.MySqlClient.MySqlStream.ReadPacket()
   à MySql.Data.MySqlClient.NativeDriver.GetResult(Int32& affectedRow, Int32& insertedId)
   ...
   à RdvMedecins_01.CreateDB_01.Main(String[] args) dans d:\data\istia-1213\c#\d
vp\Entity Framework\RdvMedecins\RdvMedecins-MySQL-01\CreateDB_01.cs:ligne 15

La ligne 1 indique une erreur de syntaxe sur le SQL exécuté par MySQL. Comme celui-ci n'a pas été généré par nous mais par le provider ADO.NET de MySQL, nous ne pouvons pas corriger ce point. Néanmoins, on peut constater que des tables ont été créées [1] ci-dessous :

Image non disponible
  • en [2], on voit la structure de la table [clients] [3].

Il y a plusieurs modifications à faire sur la base générée :

  • le type de la colonne [VERSIONING] ne convient pas. Il faut lui donner le type MySQL [TIMESTAMP] ;
  • on se rappelle que la table [rvs] a une contrainte d'unicité. Elle n'a pas été créée par cette génération ;
  • le connecteur ADO.NET de SQL Server avait généré des clés étrangères avec la clause ON DELETE CASCADE. Le connecteur ADO.NET de MySQL ne l'a pas fait.

Comme nous l'avons fait avec SQL Server, il nous faut donc modifier la base générée. Nous ne montrons pas comment faire les modifications. Nous donnons simplement le script de création de la base :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
# SQL Manager Lite for MySQL 5.3.0.2
# ---------------------------------------
# Host     : localhost
# Port     : 3306
# Database : rdvmedecins-ef


/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;

SET FOREIGN_KEY_CHECKS=0;

USE `rdvmedecins-ef`;

#
# Structure for the `clients` table : 
#

CREATE TABLE `clients` (
  `ID` INTEGER(11) NOT NULL AUTO_INCREMENT,
  `NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
  `PRENOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
  `TITRE` VARCHAR(5) COLLATE utf8_general_ci NOT NULL,
  `VERSIONING` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY USING BTREE (`ID`) COMMENT ''
)ENGINE=InnoDB
AUTO_INCREMENT=96 AVG_ROW_LENGTH=4096 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
COMMENT=''
;

#
# Structure for the `medecins` table : 
#

CREATE TABLE `medecins` (
  `ID` INTEGER(11) NOT NULL AUTO_INCREMENT,
  `NOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
  `PRENOM` VARCHAR(30) COLLATE utf8_general_ci NOT NULL,
  `TITRE` VARCHAR(5) COLLATE utf8_general_ci NOT NULL,
  `VERSIONING` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY USING BTREE (`ID`) COMMENT ''
)ENGINE=InnoDB
AUTO_INCREMENT=56 AVG_ROW_LENGTH=4096 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
COMMENT=''
;

#
# Structure for the `creneaux` table : 
#

CREATE TABLE `creneaux` (
  `ID` INTEGER(11) NOT NULL AUTO_INCREMENT,
  `HDEBUT` INTEGER(11) NOT NULL,
  `MDEBUT` INTEGER(11) NOT NULL,
  `HFIN` INTEGER(11) NOT NULL,
  `MFIN` INTEGER(11) NOT NULL,
  `MEDECIN_ID` INTEGER(11) NOT NULL,
  `VERSIONING` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY USING BTREE (`ID`) COMMENT '',
   INDEX `MEDECIN_ID` USING BTREE (`MEDECIN_ID`) COMMENT '',
  CONSTRAINT `creneaux_ibfk_1` FOREIGN KEY (`MEDECIN_ID`) REFERENCES `medecins` (`ID`) ON DELETE CASCADE ON UPDATE NO ACTION
)ENGINE=InnoDB
AUTO_INCREMENT=472 AVG_ROW_LENGTH=455 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
COMMENT=''
;

#
# Structure for the `rvs` table : 
#

CREATE TABLE `rvs` (
  `ID` INTEGER(11) NOT NULL AUTO_INCREMENT,
  `JOUR` DATE NOT NULL,
  `CRENEAU_ID` INTEGER(11) NOT NULL,
  `CLIENT_ID` INTEGER(11) NOT NULL,
  `VERSIONING` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY USING BTREE (`ID`) COMMENT '',
  UNIQUE INDEX `CRENEAU_ID_JOUR` USING BTREE (`JOUR`, `CRENEAU_ID`) COMMENT '',
   INDEX `CRENEAU_ID` USING BTREE (`CRENEAU_ID`) COMMENT '',
   INDEX `CLIENT_ID` USING BTREE (`CLIENT_ID`) COMMENT '',
  CONSTRAINT `rvs_ibfk_2` FOREIGN KEY (`CLIENT_ID`) REFERENCES `clients` (`ID`) ON DELETE CASCADE ON UPDATE NO ACTION,
  CONSTRAINT `rvs_ibfk_1` FOREIGN KEY (`CRENEAU_ID`) REFERENCES `creneaux` (`ID`) ON DELETE CASCADE ON UPDATE NO ACTION
)ENGINE=InnoDB
AUTO_INCREMENT=28 AVG_ROW_LENGTH=16384 CHARACTER SET 'utf8' COLLATE 'utf8_general_ci'
COMMENT=''
;
  • lignes 22, 38, 54, 74 : les clés primaires ID des tables sont de type AUTO_INCREMENT donc générées par MySQL ;
  • lignes 26, 42, 60, 78 : la colonne VERSIONING est de type TIMESTAMP et est mise à jour lors d'un INSERT ou d'un UPDATE ;
  • ligne 63 : la clé étrangère de la table [creneaux] vers la table [medecins] avec la clause ON DELETE CASCADE ;
  • ligne 80 : la contrainte d'unicité de la table [rvs] ;
  • ligne 83 : la clé étrangère de la table [rvs] vers la table [creneaux] avec la clause ON DELETE CASCADE ;
  • ligne 84 : la clé étrangère de la table [rvs] vers la table [clients] avec la clause ON DELETE CASCADE ;

Le script de génération des tables de la base MySQL [rvmedecins-ef] a été placé dans le dossier [RdvMedecins / databases / mysql]. Le lecteur pourra le charger et l'exécuter pour créer ses tables.

Ceci fait, les différents programmes du projet peuvent être exécutés. Ils donnent les mêmes résultats qu'avec SQL Server sauf pour le programme [ModifyDetachedEntities] qui plante. Pour comprendre pourquoi, on peut regarder le résultat du programme [ModifyAtttachedEntities] :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
client1--avant
Client [,xx,xx,xx,]
client1--après
Client [86,xx,xx,xx,]
client2
Client [86,xx,xx,xx,11/10/2012 11:31:12]
client3
Client [86,xx,xx,yy,11/10/2012 11:31:12]
  • lignes 1-2 : un client avant sauvegarde du contexte ;
  • lignes 3-4 : le client après la sauvegarde. Il a une clé primaire mais pas de valeur pour son champ [Versioning] alors que SQL Server mettait à jour le champ [Timestamp] de l'entité.

Maintenant examinons le code du programme [ModifyDetachedEntities] qui plante :

[ModifyDetachedEntities]
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
using System;
...

namespace RdvMedecins_01
{
  class ModifyDetachedEntities
  {
    static void Main(string[] args)
    {
      Client client1;

      // on vide la base actuelle
      Erase();
      // on ajoute un client
      using (var context = new RdvMedecinsContext())
      {
        // création client
        client1 = new Client { Titre = "x", Nom = "x", Prenom = "x" };
        // ajout du client au contexte
        context.Clients.Add(client1);
        // on sauvegarde le contexte
        context.SaveChanges();
      }
      // affichage base
      Dump("1-----------------------------");
      // client1 n'est pas dans le contexte - on le modifie
      client1.Nom = "y";
      // modification entité hors contexte
      using (var context = new RdvMedecinsContext())
      {
        // ici, on a un nouveau contexte vide
        // on met client1 dans le contexte dans un état modifié
        context.Entry(client1).State = EntityState.Modified;
        // on sauvegarde le contexte
        context.SaveChanges();
      }
      ...
    }

    static void Erase()
    {
      ...
    }

    static void Dump(string str)
    {
      ...
    }
  }
}
  • ligne 20 : un client est sauvegardé. Il a alors sa clé primaire mais par sa version ;
  • ligne 33 : une modification est faite avec client1. Elle échoue car il n'a pas la version qui est en base.

On résoud le problème en intercalant le code suivant entre les lignes 25 et 26 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
      // on récupère client1 pour avoir sa version
      using (var context = new RdvMedecinsContext())
      {
        // client2 sera dans le contexte
        Client client2 = context.Clients.Find(client1.Id);
        // on fixe la version de client1 à celle de client2
        client1.Versioning = client2.Versioning;
}

Désormais, l'entité [client1] a la même version qu'en base et peut donc être utilisée pour une mise à jour de la ligne en base.

IV-C. Architecture multicouche s'appuyant sur EF 5

Nous revenons à notre étude de cas décrite au paragraphe , page .

Image non disponible

Nous allons commencer par construire la couche [DAO] d'accès aux données. Pour ce faire, nous créons le projet console VS 2012 [RdvMedecins-MySQL-02] [1] :

Image non disponible
  • en [2], les références [Common.Logging, EntityFramework, MySql.Data, MySql.Data.Entity, Spring.Core] sont ajoutées avec NuGet ;
  • en [3], le dossier [Models] est recopié du projet [RdvMedecins-MySQL-01] ;
Image non disponible
  • en [4], les dossiers [Dao, Exception, Tests] et le fichier [App.config] sont recopiés du projet [RdvMedecins-SqlServer-02] ;
  • en [5], le fichier [Program.cs] a été supprimé ;
  • en [6], le projet est configuré pour exécuter le programme de test de la couche [DAO].

Dans le fichier [App.config], on remplace les informations de la base SQL Server par celles de la base MySQL. On les trouve dans le fichier [App.config] du projet [RdvMedecins-MySQL-01] :

[App.config]
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
<!-- chaîne de connexion-->
  <connectionStrings>
    <add name="monContexte"
         connectionString="Server=localhost;Database=rdvmedecins-ef;Uid=root;Pwd=root;"
         providerName="MySql.Data.MySqlClient" />
  </connectionStrings>
  <!-- le factory provider -->
  <system.data>
    <DbProviderFactories>
      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL"
          type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.5.4.0, Culture=neutral, PublicKeyToken=C5687FC88969C44D"
        />
    </DbProviderFactories>
  </system.data>

Les objets gérés par Spring changent également. Actuellement on a :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
  <!-- configuration Spring -->
  <spring>
    <context>
      <resource uri="config://spring/objects" />
    </context>
    <objects xmlns="http://www.springframework.net">
      <object id="rdvmedecinsDao" type="RdvMedecins.Dao.Dao,RdvMedecins-SqlServer-02" />
    </objects>
</spring>

La ligne 7 référence l'assembly du projet [ RdvMedecins-SqlServer-02 ]. L'assembly est désormais [ RdvMedecins-MySQL-02 ].

Ceci fait, nous sommes prêts à exécuter le test de la couche [DAO]. Il faut auparavant prendre soin de remplir la base (programme [Fill] du projet [ RdvMedecins-MySQL-01 ]). Le programme de test réussit.

Nous créons la DLL du projet comme il a été fait pour le projet [ RdvMedecins-SqlServer-02 ] et nous rassemblons l'ensemble des DLL du projet dans un dossier [lib] créé dans [ RdvMedecins-MySQL-02 ]. Ce seront les références du projet web [ RdvMedecins-MySQL-03 ] qui va suivre.

Image non disponible

Nous sommes désormais prêts pour construire la couche [ASP.NET] de notre aplication :

Image non disponible

Nous allons partir du projet [RdvMedecins-SqlServer-03]. Nous dupliquons le dossier de ce projet dans [RdvMedecins-MySQL-03] [1] :

Image non disponible
  • en [2], avec VS 2012 Express pour le web, nous ouvrons la solution du dossier [RdvMedecins-MySQL-03] ;
  • en [3], nous changeons et le nom de la solution et le nom du projet ;
Image non disponible
  • en [4], les références actuellesdu projet ;
  • en [5], on les supprime ;
  • en [6], pour les remplacer par des références sur les DLL que nous venons de stocker dans un dossier [lib] du projet [RdvMedecins-MySQL-02].

Il ne nous reste plus qu'à modifier le fichier [Web.config] On remplace son contenu actuel par le contenu du fichier [App.config] du projet [RdvMedecins-MySQL-02]. Ceci fait, on exécute le projet web. Il marche.

IV-D. Conclusion

Récapitulons ce qui a été fait pour passer du SGBD SQL server au SGBD MySQL :

  • le champ qui servait à gérer la concurrence d'accès aux entités a été changé. Sa version SQL Server était :
 
Sélectionnez
1.
2.
3.
    [Column("TIMESTAMP")]
    [Timestamp]
    public byte[] Timestamp { get; set; }

Elle est devenue :

 
Sélectionnez
1.
2.
3.
    [ConcurrencyCheck]
    [Column("VERSIONING")]
    public DateTime? Versioning { get; set; }

avec MySQL ;

  • les annotations [Table] qui lient une entité à une table ont été changées ;
  • la chaîne de connexion à la base et le [DbProviderFactory] ont été modifiés dans les fichiers de configuration [App.config] et [Web.config] ;
  • après sauvegarde en base, une entité SQL Server avait à la fois sa clé primaire et son Timestamp . Avec MySQL, elle avait seulement sa clé primaire. Cela a amené la modification d'un code.

Au final, c'est assez peu de modifications mais il a quand même fallu revoir du code. Nous recommençons la même démarche pour trois autres SGBD :

  • Le SGBD Oracle Database Express Edition 11 g Release 2 ;

précédentsommairesuivant

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2012 Serge Tahé. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.