Archiv der Kategorie: Entity Framework 6

Entity Framework 6 provider wechsel–MSSQL und PostGreSql 2.1.1.0


Vor einiger Zeit habe ich erläutert wie man das gleiche EF Model für eine MSSQL Datenbank und MySQL Datenbank verwenden kann. Das “gleiche” geht natürlich auch mit anderen SQL Datenbanken wie z.B. PostGreSql. Nur das die Verwendung für PostGreSql mit einigen Tücken auf sich aufmerksam machte bei der Umsetzung. Wie man eine normale PostGreSql Datenbank mit .NET EF anspricht habe ich bereits in meinem Artikel “Postgres Datenbank und Entity Framework mit npgsql” beschrieben. Im Aktuellen Artikel werde ich auf die Grundlagen der beiden bereits geschriebenen Artikel verweisen und hier nur noch auf die entstandenen Probleme eingehen, denn das Vorgehen ist hier durchaus ähnlich.

Wie bereits bekannt, dient als Grundlage eine MSSQL Datenbank die bereits in einem EDMX abgebildet ist. Wir müssen dann die passende SSDL für die zugehörige PostGreSql Datenbank ergänzen. Als erstes müssen wir also unsere Tabellen aus der MSSQL Datenbank auch in der PostGres Datenbank anlegen und hier aufpassen, das auch nur die kompatiblen Datentypen verwendet werden die beide SQL Systeme und .NET verstehen.

Ich habe bei der Datenbankkonvertierung auf die Trial Version von MS SQL to PostgreSQL gesetzt, da ich keine Datensätze zu migrieren hatte und die Fremdschlüsselbeziehungen per “Hand” nachgetragen habe, denn die Trial Version hat hier einige Einschränkungen. Außerdem wurde beim Konvertieren kein Autoinkrement konvertiert, was ebenfalls angepasst werden musste mit Hilfe des Datentyps “serial” and “bigserial” in PostGreSql.

Nachdem man zwei “gleiche” Datenbanken hat für MSSQL und PostGres kann man mit Hilfe des Tools “edmGen” die passenden Daten für die PostGres Datenbank erstellen, wozu auch unsere SSDL gehört, die wir für den Providerswitch zwischen MSSQL und PostGreSql benötigen. Damit edmGen ausgeführt werden kann müssen die entsprechenden npgsql Providerdaten im GAC registriert werden (die Dateien Selbst kann man sich inzwischen auf über NuGet ziehen). Für EF6 wird hier aber zusätzlich noch die “Npgsql.EntityFrameworkLegacy.dll” und die “Npgsql.EntityFramework.dll” im GAC benötigt, die zusätzlich zur “Npgsql.dll” registriert werden müssen, wie bereits in einem meiner älteren Artikel erläutert. Da es evtl. zu Kompatibilitätsproblemen führen kann zwischen EF6 und der “Npgsql.EntityFrameworkLegacy.dll”, da diese eigentlich für alle EF Version Kleiner 6.0 gedacht ist, habe ich mir hier ein Beispielprojekt angelegt in dem ich per NuGet die aktuelle Version der Legacy Variante kopiere und dann diese Datei im Hauptprojekt direkt verweise in dem ich EF6 verwende. Denn die Legacy Datei wird nur für das Erstellen der Modeldaten mit edmGen verwendet. Nachdem man mit edmGen die passenden Modeldaten für unsere PostGres Datenbank erstellt hat benötigen wir hier nur die SSDL. Die SSDL einfach im passenden Projekt wo z.B. unser EDMX ist ablegen und als Resource einbetten. Wie der passende Connectionstring auszusehen hat, habe ich bereits in einem meiner anderen Artikel beschrieben.

Die SSDL muss aber noch “angepasst” werden, denn aktuell würde hier die Fehlermeldung:

„Schema specified is not valid. Errors: error 2102: The version of EdmItemCollection must match the version of StoreItemCollection.“

kommen, ich denke es ist noch ein Bug in den aktuellen npgsql 2.1.1.0 Version für EF6. Damit dieser Fehler nicht auftritt, muss in der SSDL für PostGres der gleiche xmlns Namespace eingetragen sein wie in der SSDL für MSSQL. Wenn dieser aktuell verglichen wird:

MSSQL im Schema Tag der SSDL: xmlns=“http://schemas.microsoft.com/ado/2009/11/edm/ssdl”

PostGreSQL im Schema Tag der SSDL: xmlns=“http://schemas.microsoft.com/ado/2009/02/edm/ssdl“

Hier muss zwingend der Wert von “/02/” auf “/11/” geändert werden, d.h. in der PostGres SSDL muss der gleiche xmlns Eintrag stehen wie in der MSSQL SSDL.

Jetzt muss nur noch die App.Config entsprechend angepasst werden auf npgsql und Entity Framework 6 und schon funktioniert das ganze wie bei MySql.

Hinweis: Ich hatte bei der Konvertierung der Datenbank noch Probleme mit einigen Datentypen die nicht “richtig” konvertiert wurden, dann erhält man z.B: die folgende Fehlermeldung:

testModel.msl(343,12) : error 2019: Member Mapping specified is not valid. The type ‚Edm.Double[Nullable=False,DefaultValue=]‘ of member ‚costs‘ in type ‚test.cost‘ is not compatible with ‚Npgsql.float4[Nullable=False,DefaultValue=]‘ of member costs‘ in type ‚test.Store.cost‘.

Hier muss nach einem passenden Datentyp gesucht werden der in beiden SQL Versionen “übereinstimmt”.

Entity Framwork 6 provider wechsel – MSSQL und MySQL


Oft muss eine Software sehr flexibel gestaltet werden, damit diese so vielen Kundenwünschen wie möglich entspricht. Hier muss z.B. auch die DB Schnittstelle eine entsprechende Flexibilität aufweisen, damit man nicht nur MS SQL Server unterstützt sondern z.B. auch MySQL.

Wenn man also eine Software erstellen möchte die auf unterschiedlichen Datenbanken laufen soll, sucht man sich am Besten ein passendes Framework wie das Entity Framework. Jetzt muss man noch darauf achten, das man in seinen Modeldaten nur Datentypen verwendet, die sowohl MS SQL und MySQL unterstützten. MySQL unterstützt z.B. kein “varchar(max)”, hier muss dann entsprechend abgewogen werden welcher Datentyp bzw. welche Länge wirklich benötigt wird.

Der Providerwechsel bzw. die Umsetzung, kann mit dem Entity Framework sehr einfach durchgeführt werden. Hier muss aber darauf hingewiesen werden, das der Release von EF6 erst vor kurzem war und es z.B. mit Visual Studio 2013 zu diversen Problemen kommen kann, wenn man ein passendes MySQL Script generieren möchte.

1. Installieren der Voraussetzungen

Als erstes benötigen wir einen aktuellen Connector für MySQL in der Version 6.8.1.0 oder höher, zu finden unter: http://dev.mysql.com/downloads/connector/net/

2. Erstellen einer Beispiel Konsolenanwendung

Legen Sie eine Konsolenanwendung für .NET 4 an und fügen Sie der die folgenden Verweise hinzu:

  • MySql.Data.dll (Version 6.8.1.0 oder höher)
  • MySql.Data.Entity.EF6.dll (Achtung aufpassen für .NET 4.0 und nicht 4.5 – für unser Beispiel)
  • EntityFramework.dll (Version 6+)
  • EntityFramework.SqlServer
  • System.Data.Entity
  • System.Configuration

Die MySql DLLs wurden vom Connector für MySql installiert und sollte man unter den Standard Assemblies finden.

3. Anlegen der passenden Datenbanken in MySQL und MS SQL

Der einfachste Weg um zwei “identische” Datenbanken für MySQL und MS SQL zu bekommen, ist es ein Model zu erstellen und dann entsprechend die SQL Skripte vom Visual Studio erstellen zu lassen.

Das Erstellen eines MySQL Skriptes über das Visual Studio ist aktuell aber noch sehr “fehleranfällig” und ist mir nur in einer bestimmten Konstellation gelungen. Dazu habe ich auf einer Virtuellen Maschine nur Visual Studio 2012 und dotConnect für MySQL installiert. Dann habe ich das entsprechende Datenmodell erstellt und darauf geachtet, das ich nur Datentypen verwende die auch von beiden DB Systemen unterstützt werden. Da ich Visual Studio 2012 verwendet habe, handelte es sich hier auch um ein EF5 Model! – dies sollte aber nur zum erstellen der SQL Skripte verwendet werden.

Wenn man dann in den Eigenschaften der EDMX Datei die DDL Genration Template auf “Devart SSDLToMySql.tt” stellt und dann im Model auf “Generate Database from Model” klickt wird einem das passende MySQL Script erstellt.image

Mit dem Originaltemplate “SSDLToSQL10.tt” erhält man natürlich das passende MS SQL Script.

Ich denke wenn die einzelnen Anbieter noch etwas Zeit bekommen, ist es bestimmt bald möglich alles direkt mit EF6 in V2013 auszuführen. Dies war mir aber mit den aktuellen Treiberversionenversionen nicht möglich.

4. Passende MySql SSDL extrahieren

Damit wir später auch entsprechende Abfragen auf der MySql Datenbank ausführen können, benötigen wir noch die passende SSDL von der MySql Datenbank. Denn wenn wir später zwischen MySql und MS Sql Datenbank wechseln wollen, benötigen wir eine SSDL für die MySql Datenbank und eine SSDL für die MS SQL Datenbank. In meinem Beispiel extrahieren wir die SSDL für die MySql Datenbank und verwenden die Standard SSDL aus dem EDMX Model für die MS SQL Datenbank.

Die SSDL ist in der EDMX Model Datei “verpackt”. Als erstes benötigen wir eine EDMX Datei bei der wir das Model aus der MySql Datenbank erstellt haben. Dann öffnet man die EDMX Datei mit einem einfachen SQL Editor über das Visual Studio

image

Und wir extrahieren den Abschnitt nach “<!– SSDL content –>” von “<Schema …>” bis zum “</Schema>” Ende und erstellen eine neue Datei z.B. EFModel.MySql.ssdl, hier fügen wir als erste Zeile folgendes ein

<?xml version="1.0" encoding="utf-8"?>

Der Rest wird mit unseren XML Daten aus dem EDMX gefüllt, das ganze könnte dann z.B. folgendermaßen aussehen

<?xml version="1.0" encoding="utf-8"?>
<Schema Namespace="testDbModel.Store" 
        Alias="Self" 
        Provider="MySql.Data.MySqlClient" 
        ProviderManifestToken="5.6" 
        xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator" 
        xmlns="http://schemas.microsoft.com/ado/2009/11/edm/ssdl">
  <EntityContainer Name="testDbModelStoreContainer">
    <EntitySet Name="person" EntityType="testDbModel.Store.person" store:Type="Tables" Schema="testdb" />
  </EntityContainer>
  <EntityType Name="person">
    <Key>
      <PropertyRef Name="Name" />
    </Key>
    <Property Name="Name" Type="char" Nullable="false" MaxLength="10" />
    <Property Name="Vorname" Type="char" MaxLength="10" />
    <Property Name="Age" Type="int" />
    <Property Name="Geburtsdatum" Type="datetime" />
    <Property Name="IsMale" Type="bit" />
  </EntityType>
</Schema>

Hier ist wichtig, das als Provider “MySql.Data.MySqlClient” eingetragen ist. Sollte dies nicht der Fall sein, dann wurde das Model aus der falschen DB erstellt. Die Datei legen wir mit im EF Projekt im Selben Pfad wie die EDMX ab und stellen unter den Eigenschaften der Datei ein, das diese mit ins Ausgabeverzeichnis kopiert werden soll.

image

Auf die SSDL selbst greifen wir erst wieder im Connectionstring zu.

5. Anpassen der App.config (Connectionstring erstellen und Provider hinzufügen)

Als nächstes müssen wir die App.config anpassen, denn für EF6 gibt es hier weitere Einstellungen die vorgenommen werden müssen. Dazu gehört es die passenden Provider für MS SQL und MySQL in der App.config einzutragen, die Angabe des Providers wird ab EF6 benötigt.

<entityFramework>
  <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
  <providers>
    <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    <provider invariantName="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlProviderServices, mysql.data.entity.EF6" />
  </providers>
</entityFramework>

Auch die jeweils passenden Connectionstrings für die MySQL und MS SQL Datenbank fügen wir der App.config hinzu. Hier ist der entsprechende “provider” angepasst, der zugehörige “provider connection string” und die SSDL für MySql angepasst.

  <connectionStrings>
    <add name="MsSQLConn" connectionString="metadata=res://*/EFModel.csdl|res://*/EFModel.ssdl|res://*/EFModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=172.168.0.1\SQL2008R2;initial catalog=testDb;persist security info=True;user id=sa;password=meinPasswort;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
    <add name="MySQLConn" connectionString="metadata=EFModel.MySql.ssdl|res://*/EFModel.csdl|res://*/EFModel.msl;provider=MySql.Data.MySqlClient;provider connection string=&quot;server=localhost;user id=root;password=meinPasswort;persistsecurityinfo=True;database=testDb&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>

Im Connectionstring für MySql muss die passende SSDL für “EFModel.MySql.ssdl” eingetragen werden, hier ist es wichtig das diese ohne “res:/*//…” eingetragen wird. Die Datei wird nur vom EF gefunden, wenn diese auch auf “Copy to Output Directory” gestellt wurde, denn sonst kommt es beim Ausführen von MySql Anweisungen z.B. zu folgendem Fehler:

“System.Data.MetadataException: The specified metadata path is not valid. A valid path must be either an existing directory, an existing file with extension ‚.csdl‘, ‚.ssdl‘, or ‚.msl‘, or a URI that identifies an embedded resource. …”

Wenn man keine extra SSDL z.B. für MySql einbindet, dann kommt es bei SQL Abfragen zu folgendem Fehler:

„Unable to cast object of type ‚MySql.Data.MySqlClient.MySqlConnection‘ to type ‚System.Data.SqlClient.SqlConnection‘.“

UPDATE: Es ist auch möglich die MySql.ssdl Datei mit als Resource in die DDL einzubinden. Wichtig ist, dass dann unter Metadata der Namespace in dem sich die ssdl befindet mit vor den Namen der ssdl geschrieben wird. D.h. wenn die DLL den Namespace “efdata” hat und die ssdl den Namen “EFModel.MySql.ssdl” dann sieht der Metadata Eintrag für die MySql.ssdl folgendermaßen aus: “res://*/efdata.EFModel.MySql.ssdl|res://*/edoModel.csdl…”. Sollte dies nicht funktionieren, dann muss die DDL in der die ssdl als Resource eingebettet wurde mit z.B. ILSpy angeschaut werden und dort sieht man dann auch den genauen Namen der eingebetteten ssdl Resource.

6. Verwenden der Connectionstrings

Ich habe mein EF Model entsprechend erweitert, das ich den Connectionstring direkt übergeben kann. Daher kann ich den passenden Connectionstring direkt aus der App.config verwenden um z.B: eine neue Person der MS SQL Datenbank hinzuzufügen.

string connectionString = ConfigurationManager.ConnectionStrings["MsSQLConn"].ConnectionString;
testDbEntities entities = new testDbEntities(connectionString);
entities.Person.Add(new Person() { Age = 23, Geburtsdatum = DateTime.Now, IsMale = true, Name = "Test", Vorname = "Blubb"});
entities.SaveChanges();

Das einzige was angepasst werden muss, wenn man die Daten der MySQL Datenbank hinzufügen möchte ist, das “MsSQLConn” in ein “MySQLConn” zu ändern, um den MySQL Connectionstring zu ermitteln.