Laden...

EF Pocos modifizieren

Erstellt von darktower vor 10 Jahren Letzter Beitrag vor 10 Jahren 2.344 Views
D
darktower Themenstarter:in
34 Beiträge seit 2013
vor 10 Jahren
EF Pocos modifizieren

verwendetes Datenbanksystem: SQL Server 2012
EF 6.1
Code First with Database
Visual Studio 2013 Express
Winforms

Mein Projekt beinhaltet mehrere Master-Detail Tabellen. In den Mastertabellen (DataGridView) sollen die wichtigsten Daten readonly abgerufen werden. Einzig in der Detailansicht finden die Crud-Vorgänge statt.
Ich habe meine POCOs mit Code-First automatisch aus der Db generieren lassen und in eine eigene Solution ausgelagert. Auf diese POCOs greife praktisch alle Layer zu.

Als Beispiel für meine Problemstellung zeige ich hier einmal meine Poco-Klasse "Kontakte" aufs minimale reduziert:


[Table("Kontakt")]
    public partial class Kontakt
    {
        public Kontakt()
        {
        }

        public int id { get; set; }

        public string fullname 
        {
            get
            {
                return vorname + " \"" + spitzname + "\" " + nachname;
            }
        }

        [StringLength(40)]
        public string nachname { get; set; }

        [StringLength(40)]
        public string vorname { get; set; }

        [StringLength(40)]
        public string spitzname { get; set; }

        public int? ortId { get; set; }

        public bool aktiv { get; set; }

        public virtual Ort Ort { get; set; }
    }

Wenn ich jetzt eine Mastertabelle mit den Kontakten erstelle, möchte ich z.B. den Ort anzeigen. Hierfür würde ich der Klasse Kontakte folgende Property hinzufügen:


public virtual string ortsname 
        {
            get
            {
                return this.Ort.name;
            }
        }

Damit könnte ich im Datagridview nun ganz einfach den Ortsnamen anzeigen lassen.
Soweit sogut, nur kommt mir das nicht gerade als guter Stil vor. Wie könnte ich das Problem besser lösen?

Mein zweites Problem geht eher allgemein um das Thema wie ich Datenbankänderungen am besten aktualisieren kann ohne meine erweiterten POCOs jedes mal neu anpassen zu müssen.
Wie gehe ich da am besten vor?

Danke schon im Vorraus für eure Hilfe.

16.835 Beiträge seit 2008
vor 10 Jahren

POCOs kennen ihre wahre Verwendung nicht. Also auch keine Attribute wie [Table] oder [Key].
Besser wäre hier das Verwenden der FluentAPI für das Datenbankmapping.

Solche Erweiterungen gehören meiner Meinung nach in Extension Methods.
Änderst Du das Datenbankschema müssen auch die POCOs (oder das Mapping angepasst werden). Da führt bei einer relationalen DB recht wenig dran vorbei.

Noch besser wäre es dem Grid keine Entitäten zu geben, sondern spezifische DTOs.
Ein DTO hat genau die Properties, die spezifisch benötigt werden.
DTOs müssen bei Entity-Änderungen auch nicht angefasst werden, sondern nur der Mapper auf die Entitäten (oder Du nimmst Hilfen wie den AutoMapper).
Dadurch abstrahierst Du das ganze um eine weitere, nützliche Schicht.

Zum Code-Stil: Eigenschaften schreibt man in C# groß.
General Naming Conventions

D
darktower Themenstarter:in
34 Beiträge seit 2013
vor 10 Jahren

Ok, ich sehe schon, ich kann schon wieder einen Großteil meiner bisherigen Architektur über Bord werfen. 8o

Bisher habe ich mein Projekt so aufgebaut.
DAL<->BLL<->UI
Dann gibt es jeweils eine eigene Solution für "Contracts" (alle Schnittstellen) und "Entities" (die wie im Eingangspost beschriebenen automatisch generierten Enties)

Im DAL ist neben dem DBContext das UOF welches zusätzlich als generisches Repository für den BLL bereitgestellt ist.
Im BL gibt es eine BasisBLL-Klasse von der jede spezielle BL-Klasse erbt (um die Grundfunktionen bereitzustellen). Die speziellen BL-Klassen implementieren dann die nötigen zusätzlichen Methoden für die UI.
In der UI weise ich die Klassen aus der "Entitiy"-Solution zu und fülle diese aus den jeweiligen Methoden der BLL.

Jetzt kommen die DTO ins Spiel. Ich habe schon öfters von diesen gehört, diese aber bisher immer als eine Art POCOs abgetan. Was ist jetzt eigentlich der genaue Unterschied zwischen POCOs und DTO?
Meine erste naive Vorstellung: Die POCOs werden automatisch vom EF erstellt und bei jeder DB-Änderung automatisch angepasst->darum sollte es keinen eigenen Code enthalten. WOBEI...da die Entitäten (POCOs) ja partial generiert werden, könnte ich ja doch Erweiterung vornehmen ohne dass diese später wieder überschrieben werden.
Die DTO sehe ich als komplett vom EF entkapselt. Hier kann ich meine Properties und Methoden nach eigenen Vorstellungen erstellen.
Aber wie bekomme ich jetzt die Daten in die DTO? Und sollten die DTO in einem eigenen Solution statt den Entitäten sein um vom UI und BLL abgerufen werden zu können?
Sorry, dass ich mir teilweise selbst wiederspreche und Begriffe wie z.B. Entititäten und POCOs mische. Aber mir fehlt derzeit ein wenig der Plan wie ich das alles organisieren kann.

Mir ist bewusst, dass ich mich selbst über DTO und FluentAPI informieren und einlesen muss. Aber ein paar grundlegende Erklärungen um das Grundverständniss zu erhalten würden mir den Einstieg schon erleichtern. Bis vor wenigen Tagen hatte ich ja auch noch keine Ahnung was ein UnitOFWork oder RepositoyPattern ist. Dann diesem Forum wurde ich auf die richtige Bahn gelenkt. Danke nochmals dafür.

Für informative Links zum einlesen wäre ich natürlich auch sehr dankbar.

16.835 Beiträge seit 2008
vor 10 Jahren

POCO = Plain old C# Object.

Heisst nichts anderes: Du hast bereits irgendeine Klasse und verwendest diese für andere Dinge, zB als Datenbank-Entität.
Daher beinhalten POCO-Klassen keinerlei Datenbankinformationen. Die Klasse soll nicht wissen, dass sie eine Entität ist.

DTO = Data Transfer Object
Nichts anderes als eine Hilfsklasse, die kleinerlei Logik hat. Sie hat nur die Eigenschaften, die man für eine gewisse Aktion braucht.
Ähnlich wie ViewModels/ViewItems.

Sprich Du hast eine Userklasse mit 372 Properties.
Für die Anzeige eines User willst Du aber nur 3 Properties. Also mappst Du die relevanten Eigenschaften von der Entität in das DTO.
So lädst Du keine unnötigen Daten und gibst nur die Informationen raus, die derzeit gebraucht werden.
Merkst Du plötzlich, dass das Schema der Entität und 372 Eigenschaften doof ist und wendest Normalisierung an musst Du nur die Entität anfassen, und nicht die DTOs. Die View muss nicht geändert werden, nur der DAL.

Ähnlich macht mans auch bei Schnittstellen / APIs.
Google Maps API gibt auch nur gewisse Eigenschaften raus und nicht das ganze Datenbankobjekt.

F
10.010 Beiträge seit 2004
vor 10 Jahren

Damit könnte ich im Datagridview nun ganz einfach den Ortsnamen anzeigen lassen.

Das sagt doch schon alles, du willst also in das VIEWMODEL zusätzliche Properties einbauen.

D
darktower Themenstarter:in
34 Beiträge seit 2013
vor 10 Jahren

Ok, dann fasse ich einmal meine bisherigen Eindrücke zusammen:

Wir unterschieden zwischen 3 verschiedenen Arten von "Datenstrukturen":

  1. Datenbankmodell
    Hier ist alles in einzelnen Tabellen relational abhängig gespeichert. Perfekt zum Daten verwalten, aber ein Albtraum für den Entwickler um damit zu arbeiten.

2.POCOS bzw. Entities (Ist das jetzt das Gleiche?)
Hier erstellt der Mapper (in meinem Fall EF) die eizelnen Entities automatisch. Jede Tabelle der Datenbank entspricht nun einer Entitität.
Vorteil:
Alle Tabellen mitsamten ihren Relationen sind nun in den Entities gespeichert.
Alle CRUD Befehle verwaltet der DbContext und kömmert sich um die direkte Kommunikation mit der Datenbank.
Nachteil:
Da die Entities automatisch generiert werden und bei jeder Datenbankänderung überschrieben werden (einmal den Partial-Teil ausgenommen) ist man beim eigenen erweitern eingeschränkt.
Die GUI bekommt durch die Entities immer Zugriff auf das komplette DB-Schema.
Es werden viele Eigenschaften angeboten, welche in der GUI entweder anders oder gar nicht gebraucht werden. (z.B. Ids)

3.DTOs
Hier werden die Entities in DTOs gemappt.
Da die DTOs nun komplett unabhängig von der Datenbankstruktur sind, sind auch kéine Änderungen dieser automatisch notwendig wenn sich etwas in der DB ändert.
Man kann die DTOs ganz nach den eigenen Vorstellungen designen, mit Vererbung, Komposition, Interfaces usw.
Es werden nur die Properties für das GUI angeboten, welche auch wirklich benötigt werden. Die GUI weiss nun überhaupt nichts mehr über die eigentliche Datenbankstruktur, diese kann gänzlich anders aufgebaut sein.

Wo und wie passieren jetzt diese Mappings?
In meinem DAL wird die DB in Entities gemappt. Bisher habe ich die Entities in eine eigene Solution für alle 3 Layer zugreifbar ausgelagert. Wenn ich jetzt auc DTOs habe, würde ich die Entities im DAL lassen und den DTOs einen eigenen Layer geben. Diesen Layer können dann BLL und GUI ansprechen. Der BLL bezieht seine Daten durch ein generisches Repository/UnitOfWork mittels DbSets (was ja im Endeffekt wieder Entititäten sind) und erstellt (mappt) die nötigen DTOs aus den DbSets. Wie dieses Mapping funktioniert, ist mir aber noch unklar.

In der BLL könnte dann ein einfacher Aufruf so aussehen (Pseudocode):

public List<KontaktDTO> GetAllKontakts()
{
   List<Kontakt> EntitiyList =  uow.GetAll<Kontakt>();
   // hier passiert das Mapping von Kontakt auf KontaktDTO
  return List<KontaktDTO>;
}

Wie würde so ein einfaches Mapping aussehen?

Bitte bessert mich aus, wo ich Bullshit rede und klärt mich auf warum ich falsch liege. Es hat bei mir einfach noch nicht richtig klick gemacht. Es ist klar, dass für euch viele Sachen selbstverständlich sind, ihr arbeitet ja auch schon eine Weile in dem Geschäft (nehme ich einmal bei Usern welche mehrere Tausend post haben automatisch an). Ich kann noch nicht auf diesen Erfahrungsschatz zurückgreifen. Für mich sind bei jedem neuen Projekt schätzungsweise zwei Drittel neue Problemstellungen (was ja nicht nur negativ ist, da ich ja jedes neue Projekte auch mit einem immer geschulteren und kritischeren Auge sehe und meine Anforderungen selbst höher setze). Aber da kann es halt auch schnell einmal vorkommen, dass man vor lauter Bäumen den Wald nicht mehr sieht. Zu viel Informationen auf einmal, welche noch nicht als Routine abgespeichert sind. Da hilft das vorbildhafteste Informieren nichts und man sitzt Tage an einem einzigen kleinen Problem, wo ein erfahrener Entwickler in ein paar Munuten Klarheit bringen könnte.

16.835 Beiträge seit 2008
vor 10 Jahren

Vorweg: es gibt keine pauschale Antwort über eine optimale Architektur. Ob und wohin man DTOs auslagert spielt absolut keine Rolle für die Anwendung.

https://www.google.de/#q=c%23+entity+to+dto sollte vieles erklären und mit Beispielen zeigen.

D
500 Beiträge seit 2007
vor 10 Jahren

Hi darktower,

zu 2:

Schaut Dir mal an, was Ms dazu sagt.

Working with POCO Entities

Zu Deiner abschliessenden Frage, wie so ein Mapping aussehen wuerde:

Generate Dtos

bzw. gibt es sogar eine VS Extension

EntitiesToDtos

Gruss,
DaMoe

D
darktower Themenstarter:in
34 Beiträge seit 2013
vor 10 Jahren

Ich hab mir das ganze nocheinmal durchgedacht und bin zu dem Schluss gekommen das mir die Entititäten mit einwenig Workaround reichen. Durch partial kann ich eigentlich ser vieles auf meine Bedürfnisse anpassen. Und die eigenen partialen Klassen werden bei einem Db-Update auch nicht automatisch überschrieben. Da hält sich der Änderungsaufwand im Rahmen.

Das "EntitesToDtos" schaut vielversprechend aus, aber leider funktioniert das mit VS 2013 noch nicht.

Aber auf jeden Fall danke für eure Hilfe. Werde die Dtos bei meinem nächsten Projekt berücksichtigen.