EntityFramework en mySQL in .NET

Ingediend door Dirk Hornstra op 29-apr-2018 20:08

Ik ben bezig met de API van Fitbit. Hier heb ik een losse blog-post over, deze blog gaat over de koppeling die ik met mySQL wil maken. Lokaal heb ik een lokaal .mdf-bestand, SQL Server Compact als ik me niet vergis. Maar als ik dit op mijn website ga zetten, heb ik dat daar volgens mij niet beschikbaar en wil ik dus met mijn mySQL-database connecten. En liefst via het EntityFramework zodat ik in mijn code netjes met Linq kan werken.

Ik heb een losse DatabaseHelper.cs static-class gemaakt die bepaalt of ik met SQL-server of met mySQL verbinding maak. Ik ben dus gaan onderzoeken hoe ik dit kan integreren in Visual Studio 2017.

Als ik een EntityModel maak, kan ik echter alleen voor SQL Server databases kiezen. Eerst eens gekeken in VS wat ik daar kan vinden. Via de menubalk Tools... Extensions and Updates ga ik daar op de Marketplace zoeken op mySQL. Ik vind daar: 
dotConnect MySQL ADO.NET Data Provider
Dit is de link om in je browser de gegevens te zien:
https://marketplace.visualstudio.com/items?itemName=DevartSoftware.dotConnectMySQLADONETDataProvider#overview
Ik zie daar "Trial" bij staan, dus vraag me af of ik dat wel wil. Omdat ik nu toch mijn browser open heb surf ik naar mysql.com.
Zij bieden toch ook wel wat aan? En ja hoor, gevonden, dus deze eerst maar installeren. 
Mocht dat niet bieden wat ik nodig heb, dan kan ik altijd het alternatief nog proberen;
https://dev.mysql.com/downloads/windows/visualstudio/
De download en installatie gaat snel, vervolgens Visual Studio 2017 starten.
Bij het toevoegen van een EF model kan ik nog niet kiezen voor een mySQL database, dus dat scherm weer sluiten.
Dan bij de Server Explorer een nieuw connectie toevoegen, dat lukt wel. Dus daar heb ik nu mijn mySQL DB staan.
Vervolgens een leeg Entity Framework aangemaakt. Bij de properties voor de DDL Generation Template kan ik nu kiezen voor SSDLToMySQL.tt.

Na nog wat doorlezen op mysql.com zie ik dat ik na dat mysql-for-visualstudio package ook nog de mysql-connector
moet downloaden/installeren:
https://dev.mysql.com/downloads/connector/net/

Even een dansje maken. Nadat ik Visual Studio opnieuw opgestart heb, is ook meteen mijn EntityModel gekoppeld
aan mijn mySQL database. Woop! Ik stop weer gauw met dansje omdat ik een foutmelding krijgt, Host 'ab80::2038::...' mag niet verbinden met deze database. Uhm, werken we ineens met IPv6? In phpmyadmin dan maar een user met deze host toevoegen, dan
werkt het (gelukkig) wel. 

Ik kan in het model nog niet updaten vanuit de database. Bij het project eerst maar naar de Nuget-packages en hier de MySql.Data.Entity toegevoegd. Ik zie hier ook een ztools.other.MySql.Data van Oracle met omschrijving
"ADO.NET driver for MySQL".

Als ik nu een model wil aanmaken op basis van een mySQL-DB sluit de pop-up zich af en heb ik
geen model....  Meer mensen hebben dit probleem:

https://stackoverflow.com/questions/45469056/mysql-and-mvc-entity-framework-in-vs-2017-not-working

Zowel de connector als mysql-for-visualstudio weer verwijderd.
Ik had de GA-download uitgevoerd, ditmaal dan de development-versie...
mysql-for-visualstudio-2.0.5.msi geïnstalleerd.
Hierna wederom de connector geïnstalleerd, voor volledige installatie gekozen. Hierna een nieuw project gemaakt.

Bij de nuget packages "EntityFramework" versie 6.2.0 van Microsoft geïnstalleerd.
Niet voldoende.
MySql.Data.EntityFramework, latest stable 8.0.11.
Deze weer met "uninstall" verwijderd. Deze zorgt namelijk dat na de "next" je pop-up weer verdwijnt.
Mysql.Data.Entity prerelease 7 gekozen.
Hier staat bij Entity Framework 6.0 supported.
Werkt nog niet, deze teruggezet naar 6.10.6, de "stable version".
Werkt nog niet....

Dan de stappen op de stackoverflow-pagina volgen. 
MySQL.Data.Entity teruggezet naar 6.9.11.
MySQL.Data teruggezet naar 6.9.11.
EntityFramework teruggezet naar 6.1.3.

https://stackoverflow.com/questions/21206184/cant-use-a-mysql-connection-for-entity-framework-6
https://www.codeproject.com/Questions/1032004/MySql-EF-wizard-crashes-Does-anyone-know-a-fix
https://stackoverflow.com/questions/39110060/entity-frameworks-entity-data-wizard-crashes-when-connecting-to-mysql-database

Uiteindelijk heb ik er maar voor gekozen dat ik grafisch de boel niet kan koppelen aan de database. Dan maar "met het handje" de tabellen aanmaken. Ik heb een Entity-Model voor SQL Server (daar kun je wel de boel bijwerken) en een Entity-Model voor mySQL. 

Het model voor mySQL heb ik aangemaakt door te kiezen voor een nieuw EntityModel. 
Je kiest daarbij voor Empty EF Designer Model.
Let op dat je bij de eigenschap DDL Generation Template je deze instelt op SSDLToMySQL.tt.

Je maakt op het model (grafisch) een Entity en voegt de velden toe.
In het .edmx-bestand plaats je vervolgens in code de structuur van de échte mysql-tabel, deel <!-- SSDL content -->
Vervolgens klik je in het overzicht op Mapping Details. Je kunt nu de velden van de Entity koppelen aan de DB-velden.

Het .edmx-bestand/.tt-bestand bij het Entity-Model van SQL Server zorgt er wel voor dat er een class in C# gegenereerd wordt waar de .NET-code gebruik van maakt.

In de web.config is van belang dat je de zaken goed instelt:


<configuration>
....
 <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, Version=6.9.11.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d"></provider></providers>
  </entityFramework>
    <appSettings>
        <add key="use_mySQL" value="1"/>
    </appSettings>
  <connectionStrings>
    <add name="FitbitApiDataEntitiesSqlServer" connectionString="metadata=res://*/Models.FitbitApi.FitbitApiDataModel_SqlServer.csdl|res://*/Models.FitbitApi.FitbitApiDataModel_SqlServer.ssdl|res://*/Models.FitbitApi.FitbitApiDataModel_SqlServer.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=(LocalDB)\MSSQLLocalDB;attachdbfilename=|DataDirectory|\FitbitApiData.mdf;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" />
    <add name="FitbitApiDataEntitiesmySQL" connectionString="metadata=res://*/Models.FitbitApi.mySQL.FitbitApiDataModel_mySQL.csdl|res://*/Models.FitbitApi.mySQL.FitbitApiDataModel_mySQL.ssdl|res://*/Models.FitbitApi.mySQL.FitbitApiDataModel_mySQL.msl;provider=MySql.Data.MySqlClient;provider connection string=&quot;server=localhost;port=3306;database=database;uid=user;password=topsecret;sslmode=none&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>
  <system.data>
    <DbProviderFactories>
      <remove invariant="MySql.Data.MySqlClient" />
      <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.9.11.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
    </DbProviderFactories>
  </system.data>
</configuration>

In de global.asax moet je zorgen dat het EntityFramework voor mySQL goed wordt ingesteld:


        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            DatabaseHelper.SetEntityModelTypeSettings();
        }

Voor de volledigheid ook nog even mijn DatabaseHelper-class zodat je ziet hoe dit werkt;


 

using HealthPortal.Models.FitbitApi;
using HealthPortal.Models.FitbitApi.mySQL;
using MySql.Data.Entity;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Configuration;

namespace HealthPortal.Helpers
{
    public static class DatabaseHelper
    {
        private static bool _useMySQL = (WebConfigurationManager.AppSettings["use_mySQL"] == "1");

        public static void SetEntityModelTypeSettings()
        {
            if (_useMySQL)
            {
                DbConfiguration.SetConfiguration(new MySqlEFConfiguration());
            }
        }

        public static FitbitApiDataModel_dbConnector getEntityModelDatabase()
        {
            return new FitbitApiDataModel_dbConnector(_useMySQL ? "FitbitApiDataEntitiesmySQL" : "FitbitApiDataEntitiesSqlServer");
        }
    }
}