Entity Framework 6 / Code-First mit MySQL

Da die Jungs von Oracle mit dem MySQL/NET Connector auch schon so weit sind das in der beta des 6.8 Connectors das Entity Framework 6 unterstützt wird, wollte ich nun auch testen ob mein Projekt aus den vorangegangenen Blog-Posts auch mit MySQL funktioniert.

Ein paar Worte vorweg: EF6 und MySQL arbeiten Momentan noch nicht Perfekt zusammen – aber mit etwas Nacharbeit kann man beides zusammen gut benutzen.

Als erstes braucht man erst einmal den MySQL/NET Connector welchen man von der Mysql Seite (http://dev.mysql.com/downloads/connector/net/) beziehen kann.
Klickt man dort auf den Reiter „Development Releases“, kann man sich den „Connector/Net 6.8.1 beta“ herunter laden.
Diesen kann man entweder als MSI-Paket installieren – oder falls man wie ich bereits einen .net Connector installiert hat und der neue beta connector sich deswegen nicht installieren lassen will – kann man diesen auch als zip Datei herunter laden und über das gacutil-Tool im GAC selbst installieren.

Hier eine kleine Installations-Anleitung wie bei der Installation des ZIP-Paketes (mysql-connector-net-6.8.1-noinstall.zip) mit dem gacutil Tool vorzugehen ist:
Den Inhalt der Zip-Datei (Ordner v4.5) habe ich in F:\Visual_Studio_2013\Express\EF6CodeFirstMySQL\Libs\EF6_MySQL ausgepackt.
Dort befinden sich nun 4 Dateien von denen die beiden „mysql.data.dll“ und „mysql.data.entity.EF6.dll“ für uns wichtig sind.
Diese beiden DLL´s wollen wir nun mit dem gacutil Tool im GAC installieren.
Das gacutil Tool befindet sich bei mir im Verzeichnis „C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0 Tools\“ von wo aus wir es auch aufrufen.
Zur Installation einer DLL in den GAC muss man hier nur den Parmater „/i“ gefolgt mit dem Namen der DLL angeben.

Zuerst die mysql.data.dll:

C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0 Tools>gacutil.exe /i "F:\Visual_Studio_2013\Express\EF6CodeFirstMySQL\Libs\EF6_MySQL\mysql.data.dll"
Microsoft (R) .NET Global Assembly Cache Utility.  Version 4.0.30319.1
Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten.
 
Die Assembly wurde dem Cache erfolgreich hinzugefügt.

Und danach die mysql.data.entity.EF6.dll:

C:\Program Files\Microsoft SDKs\Windows\v7.0A\bin\NETFX 4.0 Tools>gacutil.exe /i "F:\Visual_Studio_2013\Express\EF6CodeFirstMySQL\Libs\EF6_MySQL\mysql.data.entity.EF6.dll"
Microsoft (R) .NET Global Assembly Cache Utility.  Version 4.0.30319.1
Copyright (c) Microsoft Corporation. Alle Rechte vorbehalten.
 
Die Assembly wurde dem Cache erfolgreich hinzugefügt.

Nachdem die beiden dll´s im GAC hinzugefügt wurden müssen wir diese noch als Referenz in unserem Projekt hinzufügen.
Hierzu nehme ich wieder das selbe Projekt welches wir das letzte mal von LocalDB auf SQL-Server Zugriff geändert haben.

Sind die beiden dll´s als Referenz hinzugefügt, brauchen wir noch einen Connectionstring für die App.config.
Hierfür habe ich mir wieder auf apphabor.com eine Datenbank (dieses mal Mysql) geholt.
Die Zugangsdaten hierfür sehen genauso kryptisch aus wie die vom SQL-Server:

server=ee7118bb-fc68-4288-a9db-a5677ddfe05.mysql.sequelizer.com
database=dbee7118bbfca9dba5677ddfe05
uid=nqmqksbevosquikme
pwd=ZGrKqQH6XJ5GGhqrauCkNjnXm8vWi8NfBYeEXCqpH6thX2vu4HF5gecXR5

Anmerkung: Auch diese Daten funktionieren nicht wirklich, da ich sie für diesen Post hier geändert habe.

Daraus ergibt sich folgender Connectionstring für die App.Config:

1
2
3
4
5
  <connectionStrings>
    <add name="Context"
	 providerName="MySql.Data.MySqlClient.EF6"
	 connectionString="Data Source=ee7118bb-fc68-4288-a9db-a5677ddfe05.mysql.sequelizer.com;Database=dbee7118bbfca9dba5677ddfe05;Uid=nqmqksbevosquikme;Pwd=ZGrKqQH6XJ5GGhqrauCkNjnXm8vWi8NfBYeEXCqpH6thX2vu4HF5gecXR5;"/>
  </connectionStrings>

Da wir nun nicht nur auf eine andere Datenbank zugreifen, sondern auf ein ganz anderes Datenbank-System (MS SQL-Server -> MySQL) müssen wir nun im „provider“ Abschnitt der App.config auch den MySQL-Provider angeben:

1
2
3
4
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
      <provider invariantName="MySql.Data.MySqlClient.EF6" type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6, Version=6.8.1.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
    </providers>

Der Provider vom SQL-Server darf hier ruhig stehen bleiben, da der Connectionstring vom MySQL auf den Provider mit dem invariantName=“MySql.Data.MySqlClient.EF6″ aus dem „provider“-Abschnitt der App.config zugreift.

Würden wir jetzt das „Update-Database“ Script laufen lassen um den Inhalt der bestehenden Migrations-Dateien in die Datenbank zu übertragen, würden wir folgenden Fehler erhalten:

The ADO.NET provider with invariant name 'MySql.Data.MySqlClient.EF6' is either not registered in the machine or application config file, or could not be loaded. See the inner exception for details.

Dies liegt daran, das für den MySQL Provider noch „DbProviderFactories“ Eintrag in dem Abschnitt „system.data“ der App.config fehlt:

1
2
3
4
5
  <system.data> 
    <DbProviderFactories> 
      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient.EF6" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory,MySql.Data, Version=6.8.1.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d"/> 
    </DbProviderFactories> 
  </system.data>

Damit sieht die komplette App.config wie folgt aus:

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
<?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=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
  <system.data> 
    <DbProviderFactories> 
      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient.EF6" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory,MySql.Data, Version=6.8.1.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d"/> 
    </DbProviderFactories> 
  </system.data>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
    </defaultConnectionFactory>
    <!-- <defaultConnectionFactory type="MySql.Data.MySqlClient.MySqlClientFactory,MySql.Data" />-->
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
      <provider invariantName="MySql.Data.MySqlClient.EF6" type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6, Version=6.8.1.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
    </providers>
  </entityFramework>
  <connectionStrings>
    <add name="Context"
	 providerName="MySql.Data.MySqlClient.EF6"
	 connectionString="Data Source=ee7118bb-fc68-4288-a9db-a5677ddfe05.mysql.sequelizer.com;Database=dbee7118bbfca9dba5677ddfe05;Uid=nqmqksbevosquikme;Pwd=ZGrKqQH6XJ5GGhqrauCkNjnXm8vWi8NfBYeEXCqpH6thX2vu4HF5gecXR5;"/>
  </connectionStrings>
</configuration>

Sollten wir jetzt veruschen das „Update-Database“ Script auszuführen, bekommen wir folgenden Fehler:

No MigrationSqlGenerator found for provider 'MySql.Data.MySqlClient.EF6'. Use the SetSqlGenerator method in the target migrations configuration class to register additional SQL generators.

Dieser Fehler sagt aus, das für den angegebenen Privider „MySql.Data.MySqlClient.EF6“ kein passender SQL-Generator gefunden werden konnte.
Der SQL-Generator wird dazu gebraucht um die LINQ Abfragen in den Spezifischen SQL-Dialekt der Datenbank (in diesem Fall MySQL) zu übersetzen.
Netterweise haben die Oracle-Jungs daran gedacht einen passenden SQL-Generator im MySQL-Provider mit einzubauen.
Um diesen auch zu benutzen müssen wir in der Datei „Configuration.cs“ welche sich im Migrations-Ordner befindet folgende Zeile im Konstruktor der Klasse mit einfügen:

1
SetSqlGenerator("MySql.Data.MySqlClient.EF6", new MySql.Data.Entity.MySqlMigrationSqlGenerator());

Damit sieht die Komplette Datei dann wie folgt aus:

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
namespace EF6CodeFirst.Migrations
{
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Migrations;
    using System.Linq;
 
    internal sealed class Configuration : DbMigrationsConfiguration<EF6CodeFirst.Context>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
            SetSqlGenerator("MySql.Data.MySqlClient.EF6", new MySql.Data.Entity.MySqlMigrationSqlGenerator());
        }
 
        protected override void Seed(EF6CodeFirst.Context context)
        {
            //  This method will be called after migrating to the latest version.
 
            //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
            //  to avoid creating duplicate seed data. E.g.
            //
            //    context.People.AddOrUpdate(
            //      p => p.FullName,
            //      new Person { FullName = "Andrew Peters" },
            //      new Person { FullName = "Brice Lambson" },
            //      new Person { FullName = "Rowan Miller" }
            //    );
            //
        }
    }
}

Nun können wir noch einmal versuchen das „Update-Database“ Script auszuführen.
Wenn jetzt alles Ok ist, dann wurden die Tabellen angelegt und ihr habt einfach nur Glück 😉

Bei allen anderen wird genauso wie bei mir nun folgender Fehler in der Ausgabe erscheinen:

PM> Update-Database
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Applying explicit migrations: [201311301721422_v1].
Applying explicit migration: 201311301721422_v1.
MySql.Data.MySqlClient.MySqlException (0x80004005): Specified key was too long; max key length is 767 bytes

Na immerhin wurde die Tabelle „personens“ angelegt – die Tabelle „__migrationhistory“ aber leider nicht 🙁

Was soll uns der Fehler „Specified key was too long; max key length is 767 bytes“ nun sagen?
Hauptsächlich soll uns der Fehler einfach nur sagen das Oracle da noch mal am MySQL Connector/NET noch mal nach arbeiten muss.
Das Problem ist das auf meinem verwendeten MySQL Server standardmäßig die Datenbanken in UTF-8 angelegt werden.
Demzufolge werden die Tabellen auch in UTF-8 angelegt.
Soweit ich dies nun verstanden habe kann ein INNODB-Key nur 767 bytes lang sein, was in diesem Fall für die „__migrationhistory“ Tabelle als UTF-8 nicht ausreicht.
Das einzige was mit hierzu einfällt ist die Kollation der Datenbank von UTF-8 auf z.B. „Latin1_german_ci“ zu stellen.
In diesem Fall werden die Tabellen ebenfalls mit diesem Zeichensatz angelegt, welcher weniger Speicherplatz braucht als der Doppel-Bytige UTF-8 Zeichensatz.
Bevor wir nun das „Update-Database“ Script erneut ausführen nachdem wir den Zeichensatz der Datenbank geändert haben, müsssen wir erst die bestehende Tabelle „personens“ löschen, da wir sonst den Fehler „Table ‚personens‘ already exists“ bekommen.
Nachdem die Tabelle gelöscht ist und der Zeichensatz der Datenbank stimmt, können wir das „Update-Database“-Script erneut aufrufen und bekommen folgende Ausgabe:

PM> Update-Database
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Applying explicit migrations: [201311301721422_v1].
Applying explicit migration: 201311301721422_v1.
Running Seed method.

SO wollten wir das sehen! 🙂
Schaut man sich nun die Datenbank mit einem MySQL-Client seiner Wahl an, sieht man das die beiden Tabellen „personens“ und „__migrationhistory“ erfolgreich angelegt wurden.

Damit wäre bewiesen das man das Entity Framework 6 mit dem Code First Ansatz auch „schon“ mit MySQL benutzen kann.
Dies funktioniert noch nicht komplett ohne Code-Änderung, die Änderungen die dafür gemacht werden müssen, waren allerdings Minimal.
Nacharbeiten gibt es noch auf der Seite von Oracle um den MySQL Connector/NET besser im Zusammenspiel mit UTF-8 Datenbanken zu bringen.
Für eine Beta Version denke ich, ist die Funktionalität aber bereits ausreichend.

Ich hoffe ich konnte dem einen oder anderen mit diesem Post etwas Helfen (gerade in Bezug auf die Fehlermeldungen des Entity Frameworks bei der Verwendung mit dem MySQL Provider).

Vielen Dank für die Aufmerksamkeit,
Sven

One Response to Entity Framework 6 / Code-First mit MySQL

  1.  

    Sehr schönes HowTo! Hab’s grad mit der Version 6.9.7 des MySql Providers. Das Problem mit der Collation besteht weiterhin – allerdings hab ich herausgefunden, dass es nicht nötig ist, die Collation für die ganze DB zu ändern. Es genügt, die __MigrationHistory-Tabelle nach diesem Muster anzulegen:

    CREATE TABLE `test`.`__MigrationHistory` (
    `MigrationId` NVARCHAR(150) NOT NULL COMMENT “,
    `ContextKey` NVARCHAR(300) NULL COMMENT “,
    `Model` LONGBLOB NULL COMMENT “,
    `ProductVersion` NVARCHAR(32) NULL COMMENT “,
    PRIMARY KEY (`MigrationId`) COMMENT “)
    ENGINE = InnoDB
    DEFAULT CHARACTER SET = latin1
    COLLATE = latin1_german1_ci;

leave your comment


*

Unterstütze den Frickelblog!