Cover

Inhalt

Titelei

Impressum

Inhalt

Vorwort

Über den Autor

1 Einleitung

1.1 Programmiersprache in diesem Buch

1.2 Fallbeispiele in diesem Buch

1.2.1 Entitäten

1.3 Anwendungsarten in diesem Buch

1.4 Hilfsroutinen zur Konsolenausgabe

1.5 Programmcodebeispiel zum Download

2 Was ist Entity Framework Core?

2.1 Was ist ein Objekt-Relationaler Mapper?

2.2 ORM in der .NET-Welt

2.3 Versionsgeschichte von Entity Framework Core

2.4 Unterstützte Betriebssysteme

2.5 Unterstützte .NET-Versionen

2.6 Unterstützte Visual Studio-Versionen

2.7 Unterstützte Datenbanken

2.8 Funktionsumfang von Entity Framework Core

2.9 Funktionen, die dauerhaft entfallen

2.10 Funktionen, die Microsoft bald nachrüsten will

2.11 Hohe Priorität, aber nicht kritisch

2.12 Neue Funktionen in Entity Framework Core

2.13 Einsatzszenarien

3 Installation von Entity Framework Core

3.1 Nuget-Pakete

3.2 Paketinstallation

3.3 Aktualisierung auf eine neue Version

4 Konzepte von Entity Framework Core

4.1 Vorgehensmodelle bei Entity Framework Core

4.2 Artefakte bei Entity Framework Core

5 Reverse Engineering bestehender Datenbanken

5.1 Reverse Engineering mit PowerShell-Befehlen

5.2 Codegenerierung

5.3 .NET Core-Tool

5.4 Schwächen des Reverse Engineering

6 Forward Engineering für neue Datenbanken

6.1 Regeln für die selbsterstellten Entitätsklassen

6.1.1 Properties

6.1.2 Datentypen

6.1.3 Beziehungen (Master-Detail)

6.1.4 Vererbung

6.1.5 Primärschlüssel

6.1.6 Beispiele

6.2 Regeln für die selbsterstellte Kontextklasse

6.2.1 Nuget-Pakete

6.2.2 Basisklasse

6.2.3 Konstruktor

6.2.4 Beispiel

6.2.5 Provider und Verbindungszeichenfolge

6.2.6 Eigene Verbindungen

6.2.7 Thread-Sicherheit

6.3 Regeln für die Datenbankschemagenerierung

6.4 Beispiel-Client

6.5 Anpassung per Fluent-API (OnModelCreating())

6.6 Das erzeugte Datenmodell

7 Anpassung des Datenbankschemas

7.1 Persistente versus transiente Klassen

7.2 Namen im Datenbankschema

7.3 Reihenfolge der Spalten in einer Tabelle

7.4 Spaltentypen/Datentypen

7.5 Pflichtfelder und optionale Felder

7.6 Feldlängen

7.7 Primärschlüssel

7.8 Beziehungen und Fremdschlüssel

7.9 Optionale Beziehungen und Pflichtbeziehungen

7.10 Uni- und Bidirektionale Beziehungen

7.11 1:1-Beziehungen

7.12 Indexe festlegen

7.13 Weitere Syntaxoptionen für das Fluent-API

7.13.1 Sequentielle Konfiguration

7.13.2 Strukturierung durch Statement Lambdas

7.13.3 Strukturierung durch Unterroutinen

7.13.4 Strukturierung durch Konfigurationsklassen

7.14 Massenkonfiguration mit dem Fluent-API

8 Datenbankschemamigrationen

8.1 Anlegen der Datenbank zur Laufzeit

8.2 Schemamigrationen zur Entwicklungszeit

8.3 Befehle für die Schemamigrationen

8.4 ef.exe

8.5 Add-Migration

8.6 Update-Database

8.7 Script-Migration

8.8 Weitere Migrationsschritte

8.9 Migrationsszenarien

8.10 Weitere Möglichkeiten

8.11 Schemamigrationen zur Laufzeit

9 Daten lesen mit LINQ

9.1 Kontextklasse

9.2 LINQ-Abfragen

9.3 Schrittweises Zusammensetzung von LINQ-Abfragen

9.4 Repository-Pattern

9.5 Einsatz von var

9.6 LINQ-Abfragen mit Paging

9.7 Projektionen

9.8 Abfrage nach Einzelobjekten

9.9 Laden anhand des Primärschlüssels mit Find()

9.10 LINQ im RAM statt in der Datenbank

9.11 Umgehung für das GroupBy-Problem

9.11.1 Mapping auf Nicht-Entitätstypen

9.11.2 Entitätsklasse für die Datenbanksicht anlegen

9.11.3 Einbinden der Entitätsklasse in die Kontextklasse

9.11.4 Verwendung der Pseudo-Entitätsklasse

9.11.5 Herausforderung: Migrationen

9.11.6 Gruppierungen mit Datenbanksichten

9.12 Kurzübersicht über die LINQ-Syntax

9.12.1 Einfache SELECT-Befehle (Alle Datensätze)

9.12.2 Bedingungen (where)

9.12.3 Bedingungen mit Mengen (in)

9.12.4 Sortierungen (orderby)

9.12.5 Paging (Skip() und Take())

9.12.6 Projektion

9.12.7 Aggregatfunktionen (Count(), Min(), Max(), Average(), Sum())

9.12.8 Gruppierungen (GroupBy)

9.12.9 Einzelobjekte (SingleOrDefault(), FirstOrDefault())

9.12.10 Verbundene Objekte (Include())

9.12.11 Inner Join (Join)

9.12.12 Cross Join (Kartesisches Produkt)

9.12.13 Join mit Gruppierung

9.12.14 Unter-Abfragen (Sub-Select)

10 Objektbeziehungen und Ladestrategien

10.1 Standardverhalten

10.2 Kein Lazy Loading

10.3 Eager Loading

10.4 Explizites Nachladen (Explicit Loading)

10.5 Preloading mit Relationship Fixup

10.6 Details zum Relationship Fixup

11 Einfügen, Löschen und Ändern

11.1 Speichern mit SaveChanges()

11.2 Änderungsverfolgung auch für Unterobjekte

11.3 Das Foreach-Problem

11.4 Objekte hinzufügen mit Add()

11.5 Verbundene Objekte anlegen

11.6 Verbundene Objekte ändern/Relationship Fixup

11.7 Widersprüchliche Beziehungen

11.8 Zusammenfassen von Befehlen (Batching)

11.9 Objekte löschen mit Remove()

11.10 Löschen mit einem Attrappen-Objekt

11.11 Massenlöschen

11.12 Datenbanktransaktionen

11.13 Change Tracker abfragen

11.13.1 Zustand eines Objekts

11.13.2 Liste aller geänderten Objekte

12 Datenänderungskonflikte (Concurrency)

12.1 Rückblick

12.2 Im Standard keine Konflikterkennung

12.3 Optimistisches Sperren/Konflikterkennung

12.4 Konflikterkennung für alle Eigenschaften

12.5 Konflikteinstellung per Konvention

12.6 Fallweise Konflikteinstellung

12.7 Zeitstempel (Timestamp)

12.8 Konflikte auflösen

12.9 Pessimistisches Sperren bei Entity Framework Core

13 Protokollierung (Logging)

13.1 Verwendung der Erweiterungsmethode Log()

13.2 Implementierung der Log()-Erweiterungsmethode

13.3 Protokollierungskategorien

14 Asynchrone Programmierung

14.1 Asynchrone Erweiterungsmethoden

14.2 ToListAsync()

14.3 SaveChangesAsync()

14.4 ForeachAsync()

15 Dynamische LINQ-Abfragen

15.1 Schrittweises Zusammensetzen von LINQ-Abfragen

15.2 Expression Trees

15.3 Dynamic LINQ

16 Daten lesen und ändern mit SQL, Stored Procedures und Table Valued Functions

16.1 Abfragen mit FromSql()

16.2 Zusammensetzbarkeit von LINQ und SQL

16.3 Globale Abfragefilter bei SQL-Abfragen (ab Version 2.0)

16.4 Stored Procedures und Table Valued Functions

16.5 Globale Abfragefilter bei Stored Procedures und Table Valued Functions

16.6 Nicht-Entitätsklassen als Ergebnismenge

16.7 SQL-DML-Befehle ohne Resultset

17 Weitere Tipps und Tricks zum Mapping

17.1 Shadow Properties

17.1.1 Automatische Shadow Properties

17.1.2 Festlegung eines Shadow Property

17.1.3 Ausgabe aller Shadow Properties einer Entitätsklasse

17.1.4 Lesen und Ändern eines Shadow Property

17.1.5 LINQ-Abfragen mit Shadow Properties

17.1.6 Praxisbeispiel: Automatisches Setzen des Shadow Property bei jedem Speichern

17.2 Tabellenaufteilung (Table Splitting)

17.3 Berechnete Spalten (Computed Columns)

17.3.1 Automatisches SELECT

17.3.2 Praxistipp: Spalten mit einer Berechnungsformel anlegen

17.3.3 Spalten mit einer Berechnungsformel nutzen

17.3.4 Spalten mit einer Berechnungsformel beim Reverse Engineering

17.4 Standardwerte (Default Values)

17.4.1 Standardwerte beim Forward Engineering festlegen

17.4.2 Standardwerte verwenden

17.4.3 Praxistipp: Standardwerte schon beim Anlegen des Objekts vergeben

17.4.4 Standardwerte beim Reverse Engineering

17.5 Sequenzobjekte (Sequences)

17.5.1 Erstellen von Sequenzen beim Forward Engineering

17.5.2 Sequenzen im Einsatz

17.6 Alternative Schlüssel

17.6.1 Alternative Schlüssel definieren

17.6.2 Alternative Schlüssel im Einsatz

17.7 Kaskadierendes Löschen (Cascading Delete)

17.8 Abbildung von Datenbanksichten (Views)

17.8.1 Datenbanksicht anlegen

17.8.2 Entitätsklasse für die Datenbanksicht anlegen

17.8.3 Einbinden der Entitätsklasse in die Kontextklasse

17.8.4 Verwendung der Datenbanksicht

17.8.5 Herausforderung: Migrationen

18 Weitere Tipps und Tricks zu LINQ

18.1 Globale Abfragefilter (ab Version 2.0)

18.1.1 Filter definieren

18.1.2 Filter nutzen

18.1.3 Praxistipp: Filter ignorieren

18.2 Zukünftige Abfragen (Future Queries)

19 Leistungsoptimierung (Performance Tuning)

19.1 Vorgehensmodell zur Leistungsoptimierung bei Entity Framework Core

19.2 Best Practices für Ihre eigenen Leistungstests

19.3 Leistungsvergleich verschiedener Dattenzugriffstechniken in .NET

19.4 Objektzuweisung optimieren

19.5 Massenoperationen

19.5.1 Einzellöschen

19.5.2 Optimierung durch Batching

19.5.3 Löschen ohne Laden mit Pseudo-Objekten

19.5.4 Einsatz von klassischem SQL anstelle des Entity Framework Core-APIs

19.5.5 Lamdba-Ausdrücke für Massenlöschen mit EFPlus

19.5.6 Massenaktualisierung mit EFPlus

19.6 Leistungsoptimierung durch No-Tracking

19.6.1 No-Tracking aktivieren

19.6.2 No-Tracking fast immer möglich

19.6.3 No-Tracking im änderbaren Datagrid

19.6.4 QueryTrackingBehavior und AsTracking()

19.6.5 Best Practices

19.7 Auswahl der besten Ladestrategie

19.8 Zwischenspeicherung (Caching)

19.8.1 MemoryCache

19.8.2 Abstraktion CacheManager

19.9 Second-Level-Caching mit EFPlus

19.9.1 Einrichten des Second-Level-Cache

19.9.2 Verwenden des Second-Level-Cache

20 Softwarearchitektur mit Entity Framework Core

20.1 Monolithisches Modell

20.2 Entity Framework Core als Datenzugriffsschicht

20.3 Reine Geschäftslogik

20.4 Geschäftsobjekt- und ViewModel-Klassen

20.5 Verteilte Systeme

20.6 Fazit

21 Zusatzwerkzeuge

21.1 Entity Framework Core Power Tools

21.1.1 Funktionsüberblick

21.1.2 Reverse Engineering mit Entity Framework Core Power Tools

21.1.3 Diagramme mit Entity Framework Core Power Tools

21.2 LINQPad

21.2.1 Aufbau von LINQPad

21.2.2 Datenquellen einbinden

21.2.3 LINQ-Befehle ausführen

21.2.4 Speichern

21.2.5 Weitere LINQPad-Treiber

21.2.6 Interaktive Programmcodeeingabe

21.2.7 Fazit zu LINQPad

21.3 Entity Developer

21.3.1 Reverse Engineering mit Entity Developer

21.3.2 Forward Engineering mit Entity Developer

21.4 Entity Framework Profiler

21.4.1 Einbinden des Entity Framework Profilers

21.4.2 Befehle überwachen mit Entity Framework Profiler

21.4.3 Warnungen vor potenziellen Problemen

21.4.4 Analysefunktionen

21.4.5 Fazit zu Entity Framework Profiler

22 Zusatzkomponenten

22.1 Entity Framework Plus (EFPlus)

22.2 Second-Level-Caching mit EFSecondLevelCache.Core

22.3 Objekt-Objekt-Mapping und AutoMapper

22.3.1 Objekt-Objekt-Mapping per Reflection

22.3.2 AutoMapper

22.3.3 Beispiel

22.3.4 Abbildungen konfigurieren

22.3.5 Abbildung ausführen mit Map()

22.3.6 Abbildungskonventionen

22.3.7 Profilklassen

22.3.8 Verbundene Objekte

22.3.9 Manuelle Abbildungen

22.3.10 Typkonvertierungen

22.3.11 Objektmengen

22.3.12 Vererbung

22.3.13 Generische Klassen

22.3.14 Zusatzaktionen vor und nach dem Mapping

22.3.15 Geschwindigkeit

22.3.16 Fazit zu AutoMapper

23 Praxislösungen

23.1 Entity Framework Core in einer ASP.NET Core-Anwendung

23.1.1 Das Fallbeispiel „MiracleList“

23.1.2 Architektur

23.1.3 Entitätsklassen

23.1.4 Entity Framework Core-Kontextklasse

23.1.5 Lebensdauer der Kontextklasse in ASP.NET Core-Anwendungen

23.1.6 Geschäftslogik

23.1.7 WebAPI

23.1.8 Verwendung von Entity Framework Core per Dependency Injection

23.1.9 Praxistipp: Kontextinstanzpooling (DbContext Pooling)

23.2 Entity Framework Core in einer Universal Windows Platform App

23.2.1 Das Fallbeispiel „MiracleList Light“

23.2.2 Architektur

23.2.3 Entitätsklassen

23.2.4 Entity Framework Core-Kontextklasse

23.2.5 Startcode

23.2.6 Erzeugte Datenbank

23.2.7 Datenzugriffscode

23.2.8 Benutzeroberfläche

23.3 Entity Framework Core in einer Xamarin-Cross-Platform-App

23.3.1 Das Fallbeispiel „MiracleList Light“

23.3.2 Architektur

23.3.3 Entitätsklassen

23.3.4 Entity Framework Core-Kontextklasse

23.3.5 Startcode

23.3.6 Erzeugte Datenbank

23.3.7 Datenzugriffscode

23.3.8 Benutzeroberfläche

23.4 N:M-Beziehungen zu sich selbst

24 Quellen im Internet

Holger Schwichtenberg

Effizienter Datenzugriff mit Entity Framework Core

Datenbankprogrammierung mit C# für .NET Framework, .NET Core und Xamarin

Der Autor:

Dr. Holger Schwichtenberg, Essen
www.IT-Visions.de

Alle in diesem Buch enthaltenen Informationen, Verfahren und Darstellungen wurden nach bestem Wissen zusammengestellt und mit Sorgfalt getestet. Dennoch sind Fehler nicht ganz auszuschließen. Aus diesem Grund sind die im vorliegenden Buch enthaltenen Informationen mit keiner Verpflichtung oder Garantie irgendeiner Art verbunden. Autor und Verlag übernehmen infolgedessen keine juristische Verantwortung und werden keine daraus folgende oder sonstige Haftung übernehmen, die auf irgendeine Art aus der Benutzung dieser Informationen – oder Teilen davon – entsteht.

Ebenso übernehmen Autor und Verlag keine Gewähr dafür, dass beschriebene Verfahren usw. frei von Schutzrechten Dritter sind. Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. in diesem Buch berechtigt deshalb auch ohne besondere Kennzeichnung nicht zu der Annahme, dass solche Namen im Sinne der Warenzeichen­ und Markenschutz­Gesetzgebung als frei zu betrachten wären und daher von jedermann benutzt werden dürften.

Bibliografische Information der Deutschen Nationalbibliothek: Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.

Dieses Werk ist urheberrechtlich geschützt. Alle Rechte, auch die der Übersetzung, des Nachdruckes und der Vervielfältigung des Buches, oder Teilen daraus, vorbehalten. Kein Teil des Werkes darf ohne schriftliche Genehmigung des Verlages in irgendeiner Form (Fotokopie, Mikrofilm oder ein anderes Verfahren) – auch nicht für Zwecke der Unterrichtsgestaltung – reproduziert oder unter Verwendung elektronischer Systeme verarbeitet, vervielfältigt oder verbreitet werden.

Lektorat: Sylvia Hasselbach
Copyediting: Matthias Bloch, Essen
Umschlagdesign: Marc Müller-Bremer, München, www.rebranding.de
Umschlagrealisation: Stephan Rönigk

Print-ISBN 978-3-446-44898-8
E-Book-ISBN 978-3-446-44978-7

Verwendete Schriften: SourceSansPro und SourceCodePro (Lizenz)
CSS-Version: 1.0

Font License Zurück zum Impressum

Copyright 2010, 2012, 2014 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. This Font Software is licensed under the SIL Open Font License, Version 1.1. This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL ----------------------------------------------------------- SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ----------------------------------------------------------- PREAMBLE The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. DEFINITIONS "Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. "Reserved Font Name" refers to any names specified as such after the copyright statement(s). "Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). "Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. "Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. PERMISSION & CONDITIONS Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: 1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. 2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. 3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. 5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. TERMINATION This license becomes null and void if any of the above conditions are not met. DISCLAIMER THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.

Vorwort

Liebe Leserinnen und Leser,

ich nutze Entity Framework in echten Softwareentwicklungsprojekten seit der allerersten Version, also seit der Version 1.0 von ADO.NET Entity Framework im Jahr 2008. Zuvor hatte ich einen selbstentwickelten Objekt-Relationalen Mapper in meinen Projekten verwendet. Entity Framework Core ist das Nachfolgeprodukt, das es seit 2016 gibt. Ich setzte seitdem auch (aber nicht ausschließlich) Entity Framework Core in der Praxis ein. Viele Projekte laufen noch mit dem klassischen Entity Framework.

Microsoft entwickelt Entity Framework Core inkrementell, d. h. die Versionen 1.x und 2.x stellen zunächst eine in vielen Punkten noch unvollständige Grundversion dar, die in den Folgeversionen dann komplettiert wird.

Dieses inkrementelle Konzept habe ich auch mit diesem Buch umgesetzt. Das Buch ist seit September 2016 in mehreren Versionen erschienen. Die vor Ihnen liegende Version dieses Buchs beschreibt nun alle Kernaspekte und viele Tipps und Tricks sowie Praxisszenarien zu Entity Framework Core. Ich plane, in Zukunft weitere Versionen dieses Buchs zu veröffentlichen, die die kommenden Versionen von Entity Framework Core beschreiben und auch weitere Tipps & Tricks sowie Praxisszenarien ergänzen.

Da Fachbücher leider heutzutage nicht nennenswert dazu beitragen können, den Lebensunterhalt meiner Familie zu bestreiten, ist dieses Projekt ein Hobby. Dementsprechend kann ich nicht garantieren, wann es Updates zu diesem Buch geben wird. Ich werde dann an diesem Buch arbeiten, wenn ich neben meinem Beruf als Softwarearchitekt, Berater und Dozent und meinen sportlichen Betätigungen noch etwas Zeit für das Fachbuchautorenhobby übrig habe.

Zudem möchte ich darauf hinweisen, dass ich natürlich keinen kostenfreien technischen Support zu den Inhalten dieses Buchs geben kann. Ich freue mich aber immer über konstruktives Feedback und Verbesserungsvorschläge. Bitte verwenden Sie dazu das Kontaktformular auf www.dotnet-doktor.de.

Wenn Sie technische Hilfe zu Entity Framework und Entity Framework Core oder anderen Themen rund um .NET, Visual Studio, Windows oder andere Microsoft-Produkte benötigen, stehe ich Ihnen im Rahmen meiner beruflichen Tätigkeit für die Firmen www.IT-Visions.de (Beratung, Schulung, Support) und 5Minds IT-Solutions GmbH & Co KG (Softwareentwicklung, siehe www.5minds.de) gerne zur Verfügung. Bitte wenden Sie sich für ein Angebot an das jeweilige Kundenteam.

Die Beispiele zu diesem Buch können Sie herunterladen auf der von mir ehrenamtlich betriebenen Leser-Website unter www.IT-Visions.de/Leser. Dort müssen Sie sich registrieren. Bei der Registrierung wird ein Losungswort abgefragt. Bitte geben Sie dort Ascension ein.

Herzliche Grüße aus Essen, dem Herzen der Metropole Ruhrgebiet

Holger Schwichtenberg

Über den Autor

Image

1 Einleitung
1.1 Programmiersprache in diesem Buch

Als Programmiersprache kommt in diesem Buch C# zum Einsatz, weil dies die bei weitem am häufigsten verwendete Programmiersprache in .NET ist. Der Autor dieses Buchs programmiert in einigen Kundenprojekten .NET-Anwendungen zwar auch in Visual Basic .NET, leider bietet dieses Buch jedoch nicht den Raum, alle Listings in beiden Sprachen wiederzugeben.

Eine Sprachkonvertierung zwischen C# und Visual Basic .NET ist im WWW kostenfrei verfügbar auf der Website http://converter.telerik.com.

1.2 Fallbeispiele in diesem Buch

Die meisten Beispielprogrammcodes in diesem Buch drehen sich um das Fallbeispiel der fiktiven Fluggesellschaft „World Wide Wings“, abgekürzt WWWings (siehe auch www.world-wide-wings.de).

Image

Bild 1.1 Logo der fiktiven Fluggesellschaft „World Wide Wings“

Image

HINWEIS: In einzeln Unterkapitel werden andere Fallbeispiele verwendet (z. B. die Aufgabenverwaltung „MiracleList“). Diese Fallbeispiele werden dann in den jeweiligen Kapiteln erläutert.

1.2.1 Entitäten

Im Anwendungsfall „World Wide Wings“ geht es um folgende Entitäten:

  • Flüge zwischen zwei Orten, bei denen die Orte bewusst nicht als eigene Entität modelliert wurden, sondern Zeichenketten sind (dies vereinfacht das Verständnis vieler Beispiele)

  • Passagiere, die auf Flügen fliegen

  • Mitarbeiter der Fluggesellschaft, die wiederum Vorgesetzte haben, die auch Mitarbeiter sind

  • Piloten als eine Spezialisierung von Mitarbeitern

  • Personen als Sammlung der gemeinsamen Eigenschaften für alle Menschen in diesem Beispiel. Personen gibt es aber nicht eigenständig, sondern nur in den Ausprägungen/Spezialisierungen Passagier, Mitarbeiter und Pilot. Im objektorientierten Sinne ist Person also eine abstrakte Basisklasse, die keine Instanzen besitzen kann, sondern nur der Vererbung dient.

Es gibt zwei Datenmodelle:

  • Das etwas einfachere Modell #1 ist das Ergebnis klassischen relationalen Datenbankdesigns mit Normalisierung. Das Objektmodell daraus entsteht per Reverse Engineering.

  • Modell #2 ist das Ergebnis des Forward Engineering mit Entity Framework Core aus einem Objektmodell. Zusätzlich gibt es hier weitere Entitäten (Persondetail, Flugzeugtyp und Flugzeugtypdetail), um weitere Modellierungsaspekte aufzeigen zu können.

In Modell #1 gibt es eine jeweils eigene Tabelle für Personen (auch wenn es keine eigenständigen Personen gibt), Mitarbeiter, Piloten und Passagiere. Diese Aufteilung entspricht den Klassen im Objektmodell.

In Modell #2 gibt es lediglich die Tabellen Passagiere und Mitarbeiter für diese vier Entitäten. Entity Framework Core ist derzeit etwas eingeschränkt und unterstützt das Konzept Table per Type (also eine eigenständige Tabelle für jede Klasse) nicht. Daher umfasst die Tabelle Passagiere auch alle Eigenschaften von Person. Die Tabelle Mitarbeiter umfasst neben den Personeneigenschaften die Eigenschaften der Entitäten Mitarbeiter und Pilot. In der Tabelle wird per Diskriminatorspalte unterschieden zwischen Datensätzen, die ein Mitarbeiter sind, und solchen, die ein Pilot sind. Entity Framework Core mischt hier die Konzepte Table per Concrete Type (TPC) und Table per Hierarchy (TPH). Einen dezidierten Einfluss auf diese Abbildung hat man in Entity Framework Core 1.x/2.0 noch nicht. Das klassische Entity Framework bietet hier mehr Optionen.

Abhängigkeitsarten sind hier:

  • Ein Flug muss einen Piloten besitzen. Der Copilot ist optional.

  • Ein Flug kann optional einen Flugzeutyp zugeordnet haben.

  • Jede Person und damit auch jeder Pilot und Passagier muss ein Persondetail-Objekt besitzen.

In diesem Buch kommen beide Datenmodelle vor, teilweise auch in modifizierter Form, um bestimmte Szenarien (z. B. Datenbankschemamigrationen) aufzuzeigen.

Bitte beachten Sie, dass die Objektmodelle, die in diesem Buch zu den Datenmodellen erstellt werden, nicht das Idealbild eines Objektmodells darstellen können, denn Entity Framework Core unterstützt einige Mapping-Möglichkeiten wie z. B. das N:M-Mapping noch nicht. Das Objektmodell zum einfachen Datenmodell ist das automatisch von Entity Framework Core aus der Datenbank generierte Objektmodell (Reverse Engineering); es ist bewusst nicht verändert worden.

Image

Bild 1.2 World Wide Wings-Datenmodell in der einfacheren Version

Image

Bild 1.3 Objektmodell zum World Wide Wings-Datenmodell in der einfacheren Version

Image

Bild 1.4 World Wide Wings-Datenmodell in der komplexeren Version

Image

Bild 1.5 Objektmodell zum World Wide Wings-Datenmodell in der komplexeren Version

1.3 Anwendungsarten in diesem Buch

In diesem Buch erfolgen Bildschirmausgaben meist an der textbasierten Konsole in Konsolenanwendungen, denn dies ermöglicht die Fokussierung auf den Datenbankzugriff. Beim Einsatz von grafischen Benutzeroberflächen wie WPF, Windows Forms, ASP.NET Webforms oder ASP.NET MVC ist die Darstellung durch Datenbindung entkoppelt, das heißt man würde immer ein zweites Listing brauchen, um zu verstehen, dass die Datenzugriffe überhaupt liefern. Eingaben des Benutzers werden in den Konsolenbeispielen durch Variablen zu Beginn des Programmcodes simuliert.

Der Autor dieses Buchs führt seit vielen Jahren Schulungen und Beratungseinsätze im Bereich Datenzugriff durch und hat dabei die Erfahrung gemacht, dass Konsolenausgaben das didaktisch beste Instrument sind, da die Listings sonst sehr umfangreich und damit schlechter zu verstehen sind.

Natürlich ist die Konsolenausgabe in 99 % der Fälle der Softwareentwicklung nicht die gängige Praxis. Grafische Benutzeroberflächen sind Inhalt anderer Bücher, und die Datenbindung hat in der Regel keinen Einfluss auf die Form des Datenzugriffs. Dort, wo der Datenzugriff doch relevant ist, wird dieses Buch auch Datenbindungsbeispiele zeigen.

1.4 Hilfsroutinen zur Konsolenausgabe

Für die Bildschirmausgabe an der Konsole wird an mehreren Stellen nicht nur Console.WriteLine() verwendet, sondern auch Hilfsroutinen kommen zur Anwendung, die farbige Bildschirmausgaben erzeugen. Diese Hilfsroutinen in der Klasse CUI aus der ITV_DemoUtil.dll sind hier zum besseren Verständnis abgedruckt:

Listing 1.1 Klasse CUI mit Hilfsroutinen für die Bildschirmausgabe an der Konsole

using System; using System.Runtime.InteropServices; using System.Web; using ITVisions.UI; using System.Diagnostics; namespace ITVisions { /// <summary> /// Hilfsroutinen für Konsolen-UIs /// (C) Dr. Holger Schwichtenberg 2002-2017 /// </summary> public static class CUI { public static bool IsDebug = false; public static bool IsVerbose = false; #region Ausgaben unter bestimmten Bedingungen /// <summary> /// Ausgabe an Console, Trace und Datei - nur wenn Anwendung im DEBUG-Modus /// </summary> public static void PrintDebug(object s) { PrintDebug(s, System.Console.ForegroundColor); } /// <summary> /// Ausgabe an Console, Trace und Datei - nur wenn Anwendung im VERBOSE-Modus /// </summary> public static void PrintVerbose(object s) { PrintVerbose(s, System.Console.ForegroundColor); } #endregion #region Ausgaben mit vordefinierten Farben public static void MainHeadline(string s) { Print(s, ConsoleColor.Black, ConsoleColor.Yellow); } public static void Headline(string s) { Print(s, ConsoleColor.Yellow); } public static void HeaderFooter(string s) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine(s); Console.ForegroundColor = ConsoleColor.Gray; } public static void PrintSuccess(object s) { Print(s, ConsoleColor.Green); } public static void PrintDebugSuccess(object s) { PrintDebug(s, ConsoleColor.Green); } public static void PrintVerboseSuccess(object s) { PrintVerbose(s, ConsoleColor.Green); } public static void PrintWarning(object s) { Print(s, ConsoleColor.Cyan); } public static void PrintDebugWarning(object s) { PrintDebug(s, ConsoleColor.Cyan); } public static void PrintVerboseWarning(object s) { PrintVerbose(s, ConsoleColor.Cyan); } public static void PrintError(object s) { Print(s, ConsoleColor.White, ConsoleColor.Red); } public static void PrintDebugError(object s) { PrintDebug(s, ConsoleColor.White, ConsoleColor.Red); } public static void PrintVerboseError(object s) { Print(s, ConsoleColor.White, ConsoleColor.Red); } public static void Print(object s) { PrintInternal(s, null); } #endregion #region Ausgaben mit wählbarer Farbe /// <summary> /// Ausgabe an Console, Trace und Datei - nur wenn Anwendung im DEBUG Modus /// </summary> public static void Print(object s, ConsoleColor farbe, ConsoleColor? hintergrundfarbe = null) { PrintInternal(s, farbe, hintergrundfarbe); } public static void PrintDebug(object s, ConsoleColor farbe, ConsoleColor? hintergrundfarbe = null) { if (IsDebug IsVerbose) PrintDebugOrVerbose(s, farbe, hintergrundfarbe); } public static void PrintVerbose(object s, ConsoleColor farbe) { if (!IsVerbose) return; PrintDebugOrVerbose(s, farbe); } #endregion #region Spezielle Ausgabe mit Zusatzdaten /// <summary> /// Ausgabe mit Thread-ID /// </summary> public static void PrintWithThreadID(string s, ConsoleColor c = ConsoleColor.White) { var ausgabe = String.Format("Thread #{0:00} {1:}: {2}", System.Threading.Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToLongTimeString(), s); CUI.Print(ausgabe, c); } /// <summary> /// Ausgabe mit Uhrzeit /// </summary> public static void PrintWithTime(object s, ConsoleColor c = ConsoleColor.White) { CUI.Print(DateTime.Now.Second + "." + DateTime.Now.Millisecond + ":" + s); } private static long count; /// <summary> /// Ausgabe mit fortlaufendem Zähler /// </summary> private static void PrintWithCounter(object s, ConsoleColor farbe, ConsoleColor? hintergrundfarbe = null) { count += 1; s = String.Format("{0:0000}: {1}", count, s); CUI.Print(s, farbe, hintergrundfarbe); } #endregion #region interne Hilfsroutinen private static void PrintDebugOrVerbose(object s, ConsoleColor farbe, ConsoleColor? hintergrundfarbe = null) { count += 1; s = String.Format("{0:0000}: {1}", count, s); Print(s, farbe, hintergrundfarbe); Debug.WriteLine(s); Trace.WriteLine(s); Trace.Flush(); } /// <summary> /// Ausgabe an Console, Trace und Datei /// </summary> /// <param name="s"></param> //[DebuggerStepThrough()] private static void PrintInternal(object s, ConsoleColor? farbe = null, ConsoleColor? hintergrundfarbe = null) { if (s == null) return; if (HttpContext.Current != null) { try { //Achtung: Keine Textausgaben in Ausgabestrom von ASP.NET Webservices und WCF-Diensten und WebAPI schreiben! if (farbe != null) { HttpContext.Current.Response.Write("<span style='color:" + farbe.Value.DrawingColor().Name + "'>"); } if (!HttpContext.Current.Request.Url.ToString().ToLower().Contains(".asmx") && !HttpContext.Current.Request.Url.ToString().ToLower().Contains(".svc") && !HttpContext.Current.Request.Url.ToString().ToLower().Contains("/api/")) HttpContext.Current.Response.Write(s.ToString() + "<br>"); if (farbe != null) { HttpContext.Current.Response.Write(""); } } catch (Exception) { } } else { object x = 1; lock (x) { ConsoleColor alteFarbe = Console.ForegroundColor; ConsoleColor alteHFarbe = Console.BackgroundColor; if (farbe != null) Console.ForegroundColor = farbe.Value; if (hintergrundfarbe != null) Console.BackgroundColor = hintergrundfarbe.Value; //if (farbe.ToString().Contains("Dark")) Console.BackgroundColor = ConsoleColor.White; //else Console.BackgroundColor = ConsoleColor.Black; Console.WriteLine(s); Console.ForegroundColor = alteFarbe; Console.BackgroundColor = alteHFarbe; } } } #endregion #region Position des Konsolenfensters setzen [DllImport("kernel32.dll", ExactSpelling = true)] private static extern IntPtr GetConsoleWindow(); private static IntPtr MyConsole = GetConsoleWindow(); [DllImport("user32.dll", EntryPoint = "SetWindowPos")] public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags); // Setze Position des Konsolenfensters ohne Größe public static void SetConsolePos(int xpos, int ypos) { const int SWP_NOSIZE = 0x0001; SetWindowPos(MyConsole, 0, xpos, ypos, 0, 0, SWP_NOSIZE); } // Setze Position des Konsolenfensters mit Größe public static void SetConsolePos(int xpos, int ypos, int w, int h) { SetWindowPos(MyConsole, 0, xpos, ypos, w, h, 0); } #endregion } }

1.5 Programmcodebeispiel zum Download

Die Beispiele zu diesem Buch können Sie als Visual Studio-Projekte herunterladen auf der Leser-Website unter www.IT-Visions.de/Leser. Dort müssen Sie sich einmalig registrieren. Bei der Registrierung wird ein Losungswort abgefragt, das Sie als Käufer dieses Buchs ausweist. Bitte geben Sie dort Ascension ein. Durch die Registrierung erhalten Sie ein persönliches Kennwort per E-Mail zugesendet, das Sie dann für die Anmeldung nutzen können.

Bitte beachten Sie, dass nicht jede einzelne Zeile Programmcode, die Sie in diesem Buch finden, in den herunterladbaren Projekten enthalten sein kann. Die Projekte bilden funktionierende Lösungen. In diesem Buch warden auch alternative Lösungen für Einzelfälle diskutiert, die nicht unbedingt zu einer Gesamtlösung passen.

Image

2 Was ist Entity Framework Core?