DbContext-Datenbank | EF 5.0 | only-code

    • VB.NET

    Es gibt 1 Antwort in diesem Thema. Der letzte Beitrag () ist von Thias.

      DbContext-Datenbank | EF 5.0 | only-code

      @ErfinderDesRades: hat ein Tutorial dazu gemacht, wie man ohne Code eine Datenbank erzeugen kann - und das relativ schnell und einfach.
      Im Internet gibt es immer wieder Leute die behaupten, sie wüssten wie man mit Datenbanken richtig umgeht und diese zu nutzen hat - nein ErfinderDesRades gehört nicht dazu.
      Durch meine Spielereien mit dem MVC4-Framework für ASP.NET Anwendungen ist mir aufgefallen, dass man ohne jegliche Modelle Datenbanken erzeugen kann, ohne sich um den Code zu kümmern - das einzige was man braucht, ist etwas Logik und drei Dateien.

      Voraussetzungen, um dieses Tutorial durchzuarbeiten sind:
      • Wissen über Entity Framework (im Folgenden EF) /wie man dieses installiert
      • Grundlagen der relationalen Datenbankentwicklung
      • möglichst alle Grundlagen zur Programmierung draufhaben
      • .NET 4.5 kann auch mit niedrigerem funktionieren - nicht getestet
      • NuGet


      Nun, warum schreibe ich denn jetzt dieses Tutorial? Ganz einfach, ich möchte mein erlangtes "Wissen" auch der Außenwelt zur Verfügung stellen. Außerdem ist dieses Thema - EF - noch ein ziemlich Neues und nicht jeder hat sich bis jetzt damit auseinandergesetzt. Ich möchte außerdem eine grundsolide Struktur für mich bauen, da ich - praktisch jedenfalls - ein Gehirn wie eine Kartoffel habe, sodass ich hier immer wieder nachgucken kann und denke "jo, ist super, Copy&Pastest du das mal." -was jetzt nicht bedeuten soll, dass ich hier Copy&Paste-Code liefern werde, da hier auch einiges an Eigenarbeit gefordert wird.
      Worum geht es jetzt in diesem Tutorial genau? Wie ich bereits sagte, werde ich die Grundzüge des EF und der Arbeit damit zeigen. Außerdem natürlich auch die Vorteile und - sofern vorhanden - auch die Nachteile.

      Die Grundstruktur des Beispiels ist (wen wunderts)

      VB.NET-Quellcode

      1. Public Class DbEFSample
      2. End Class


      Für den Einstieg in das EF wird a) das EF und b) noch weitere Verweise benötigt.
      Das EF ist relativ einfach per install-package EntityFramework in der NuGet-Konsole installiert - analog dazu kann auch der Wizard von NuGet genutzt werden.
      Die weiteren Verweise sind System.ComponentModel.DataAnnotations, System.Data.Entity und System.Data.SqlServerCe - hierbei nutze ich den SqlServerCe als Proof-Of-Concept, es kann jeder andere SqlProvider genutzt werden (auch SQLite.NET, allerdings habe ich dies nicht getestet).



      Was diese einzelnen Bibliotheken aufsich haben, wird im Folgenden natürlich noch gezeigt. Außerdem ist dies auch erst der Anfang.
      Wenn nun die Verweise ordnungsgemäß installiert und eingerichtet sind, kann mit der Grundstruktur der Logik angefangen werden - ich nutze hier eine kleine Datenbank mit einer Auflistung von User <-> Beitrag <-> Kommentar (vergleichbar mit Reddit, o.Ä.).

      Dinge die berücksichtigt werden müssen, sind a) wie sollen die Datensätze miteinander verknüpft sein und b) wie sehen die Datensätze aus?
      Der erste Punkt ist schnell abgearbeitet: User:Beitrag 1:n. Fertig. Nur, das Design der Datensätze ist nicht ganz klar.
      Also erstmal ein geeignetes Modell aufbauen.

      Brainfuck-Quellcode

      1. +----User----+
      2. | ID |
      3. | Name |
      4. | Password |
      5. | EMail |
      6. | Beiträge |
      7. | Kommentare |
      8. +------------+
      9. +---Beitrag---+
      10. | ID |
      11. | Title |
      12. | Written |
      13. | Content |
      14. | Author |
      15. | Kommentare |
      16. +-------------+
      17. +--Kommentar--+
      18. | ID |
      19. | Written |
      20. | Content |
      21. | Beitrag |
      22. | Author |
      23. +-------------+

      Was nun gebraucht wird, ist eine vollständige Umsetzung in VB-Code. Ebenfalls, aufgrund des Modells oben, nicht weiter schwierig.
      Table und Key wird der Compiler vorerst anmeckern, das Problem: die Imports fehlen (sofern nicht schon vorher in der Verweis-Ansicht erledigt)

      VB.NET-Quellcode

      1. Imports System.ComponentModel.DataAnnotations
      2. Imports System.ComponentModel.DataAnnotations.Schema

      Für den User etwas eigenes:

      VB.NET-Quellcode

      1. <Table("User")> _
      2. Public Class UserModel
      3. ' Die ID soll der Key der Tabelle sein
      4. ' und immer neu ausgerechnet werden
      5. <Key()> _
      6. <DatabaseGeneratedAttribute( DatabaseGeneratedOption.Identity )> _
      7. Public Property Id As Integer
      8. Public Property Name As String ' der Name des Users
      9. Public Property Password As String ' Passwort, wofür auch immer
      10. Public Property EMail As String ' passt schon.

      Nun kommt aber etwas ganz komisches - die Eigenschaften, die sich auf andere Tabellen beziehen, müssen mit Overridable angegeben werden.
      Damit ergibt sich für den Rest der Klasse:

      VB.NET-Quellcode

      1. Public Overridable Property Entries As List(Of EntryModel)
      2. Public Overridable Property Comments As List(Of CommentModel)
      3. End Class

      Damit ist das User-Model schon fertig und muss nicht mehr bearbeitet werden - sollten dennoch Änderungen angewendet werden müssen: kein Problem! Einfach ändern und es funktioniert.

      Was nun gebraucht wird, sind die restlichen Models - Entry und Comment.
      Hier wird analog vorgegangen, wie beim User. - ich gebe hier nur eine C&P-Vorlage, da sich der Code nicht weiter unterscheidet.

      VB.NET-Quellcode

      1. <Table("Entries")> _
      2. Public Class EntryModel
      3. <Key()> _
      4. <DatabaseGeneratedAttribute( DatabaseGeneratedOption.Identity )> _
      5. Public Property Id As Integer
      6. Public Property Title As String
      7. Public Property Written As DateTime
      8. Public Property Content As String
      9. Public Overridable Property Author As UserModel
      10. Public Overridable Property Comments As List(Of CommentModel)
      11. End Class

      VB.NET-Quellcode

      1. <Table("Comments")> _
      2. Public Class CommentModel
      3. <Key()> _
      4. <DatabaseGeneratedAttribute( DatabaseGeneratedOption.Identity )> _
      5. Public Property Id As Integer
      6. Public Property Written As DateTime
      7. Public Property Content As String
      8. Public Overridable Property Entry As EntryModel
      9. Public Overridable Property Author As UserModel
      10. End Class


      Da nun alle Models korrekt eingebaut wurden, kann mit dem DbContext angefangen werden - ein, u.U., schwieriges Unterfangen.
      Zuerst wird das EntityFramework benötigt. Dies ist einfach via Install-Package EntityFramework in der NuGet-Konsole installiert.
      Nach diesem Schritt wird die App.config erweitert:

      XML-Quellcode

      1. <?xml version="1.0" encoding="utf-8"?>
      2. <configuration>
      3. <configSections>
      4. <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
      5. <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
      6. </configSections>
      7. <startup>
      8. <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
      9. </startup>
      10. <entityFramework>
      11. <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
      12. </entityFramework>
      13. </configuration>

      Hier werden direkt zwei Dinge sichtbar: das EntityFramework wird intern gesetzt und hat auch eine ConnectionFactory - später dazu mehr.

      Aber vorher doch nochmal die Verbindung zur Datenbank - nur wegen dieser wird das EntityFramework benötigt!
      Der Grundaufbau einer Verbindung zu einer Datenbank ist relativ einfach:

      VB.NET-Quellcode

      1. Public Class BlogContext
      2. Inherits DbContext
      3. End Class

      Etwas wird allerdings, laut Visual Studio, noch gebraucht - ein Verweis auf System.Data.Entity.
      Die Verbindung will allerdings gefüllt werden, a) mit Code und b) einem/mehreren Konstruktor/en.
      Ersteres kann einfach gemacht werden:

      VB.NET-Quellcode

      1. Public Property User As DbSet(Of UserModel)
      2. Public Property Entries As DbSet(Of EntryModel)
      3. Public Property Comments As DbSet(Of CommentModel)

      Ein DbSet ist also ein Datensatz von genau einem Model, hier User, Entry und Comment.

      Der DbContext wird allerdings nicht funktionieren, da eine entsprechende Verbindung fehlt.
      Daher wird noch ein Konstruktor benötigt. Hierzu sollte einmal der eigentliche DbContext angeschaut werden.
      Hier ist nun eindeutig ersichtlich, dass einige Konstruktoren implementiert werden müssen - allerdings nicht alle!
      In diesem Fall wird der New(string) Konstruktor implementiert.

      VB.NET-Quellcode

      1. Public Sub New (nameOrConnectionString As String)
      2. MyBase.New(nameOrConnectionString)
      3. End Sub

      Hierbei kann der Konstruktor mit einem Namen (s. App.config - ConnectionStrings) oder einem ConnectionString aufgerufen werden - sehr interessantes Feature.
      Ich werde beide Möglichkeiten beleuchten - wobei der Name eher uninteressant ist, daher ist dies auch als nächstes dran.

      Für den Namen gilt allgemein folgendes:

      XML-Quellcode

      1. <connectionStrings>
      2. <add name="Name" connectionString="irgendeinverbindungstext" providerName="irgendeinprovider" />
      3. </connectionStrings>

      Es wird also Sql Ce (Microsoft Sql Server Compact) verwendet - eine abgespeckte Variante vom Sql Server, die ohne Server-Instanz ausgeführt werden kann.
      Bei dem ConnectionString für Sql Ce muss allerdings aufgepasst werden, dass der Aufbau korrekt ist. Daraus ergibt sich, dass der ConnectionString wie folgt lautet:

      XML-Quellcode

      1. <connectionStrings>
      2. <clear />
      3. <add name="SomeConnection" connectionString="provider=System.Data.SqlServerCe.4.0;provider connection string=&quot;Data Source=Data.sdf&quot;" providerName="System.Data.EntityClient" />
      4. </connectionStrings>

      <clear /> wird verwendet, um mögliche andere Verbindungen zu löschen, diese sind uninteressant und brauchen nicht beachtet zu werden!
      Wie sichtbar ist, wird der SqlServerCe4 Provider als auch der EntityClient verwendet .. warum?
      Der EntityClient selber ist nur eine Schnittstelle zwischen dem SqlProvider und dem EntityClient - der SqlProvider ansich versteht keine "Linq" und muss deshalb durch einen anderen Provider irgendwie in eine nutzbare Sql-Query übersetzt werden. Dies tut der EntityClient.
      Anders ist es, wenn versucht wird, auf einem Sql Server zu arbeiten:

      XML-Quellcode

      1. <add name="DefaultConnection" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|DatenbankName.mdf;Initial Catalog=DatenbankName;Integrated Security=SSPI" providerName="System.Data.SqlClient" />

      Hier ist der Provider der SqlClient - spielt hier aber keine Rolle.

      Es wird einfacher, sobald nurnoch der ConnectionString selber genutzt werden soll. Zwar ist eine Manipulierung der App.config angesagt, aber das soll nicht stören.
      Die vorhin angesprochene defaultConnectionFactory muss etwas erweitert werden:

      XML-Quellcode

      1. <entityFramework>
      2. <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlCeConnectionFactory, EntityFramework">
      3. <parameters>
      4. <parameter value="System.Data.SqlServerCe.4.0" />
      5. </parameters>
      6. </defaultConnectionFactory>
      7. </entityFramework>

      Hier ist also auch der SqlServer, der in den ConnectionStrings schon zusehen war, direkt eingebunden.
      Was nun noch fehlt, ist der ConnectionString für den DbContext.
      Für diesen ist nicht soviel Wissen erforderlich, denn es gibt nicht viel mehr, was beachtet werden muss: "Data Source=" & System.IO.Path.Combine( Environment.CurrentDirectory, "Test.sdf" )
      Welche Möglichkeit genutzt wird, liegt nicht in meinem Ermessen. Beide Möglichkeiten habe ihre Stärken und Schwächen.

      Die Datenbank ist am Anfang, logischerweise, nicht initialisiert.
      Dafür gibt es aber, eine kleine Abhilfe:

      VB.NET-Quellcode

      1. Dim context As BlogContext = New BlogContext("Data Source=" & System.IO.Path.Combine(Environment.CurrentDirectory, "Blog.sdf"))
      2. context.Database.Initialize(False)


      Ab jetzt ist die Verbindung offen und kann für alles genutzt werden.
      Die Eigenschaften Configuration und Database sollten natürlich noch an die eigenen Gegebenheiten angepasst werden.

      Im Anhang ist ein Beispielprojekt was das Grundprinzip dahinter zeigt.
      Dieses Projekt benötigt VS 2012 (Express Desktop) und neuer.

      Vorschläge, Kritik (konstruktiv) gerne.
      Dateien
      • Tutorial01.zip

        (13,88 kB, 243 mal heruntergeladen, zuletzt: )
      • Tutorial01.1.zip

        (13,88 kB, 239 mal heruntergeladen, zuletzt: )

      Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „AliveDevil“ ()

      Mal eine Frage, wie bekommt man dies vernünftig lauffähig?

      In den Zip-Daten gibt es bei mir keine sdf-Datenbank-Dateien.

      Oder soll das Super-Schlaue-Entity-Framework diese Datenbanken selber erstellen?

      Leider wüsste ich nur nicht wie - bekomme wenn ich das Programm starte nur leere Panels mit den Überschriften Benutzer, Beiträge und Kommentare angezeigt.

      MVVM.Light von GalaSoft wird für dieses Projekt benötigt (habe ich dann auch installiert), aber leider wird dies im Tut auch nicht erwähnt - von daher bin ich aktuell etwas verwirrt!

      Edit: habe auch schon mal "context.Database.Initialize(False)" auf "context.Database.Initialize(True)" gesetzt - kein Unterschied!

      Edit die Zweite: Dank @ErfinderDesRades sein Beispiel-Projekt funktioniert auf anhieb! EntityFramework-CodeFirst-Sample

      Edit die Dritte: Ebenfalls funktioniert: WPF EF Code-First Datenbanksample

      Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „Thias“ ()