Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Entity Framework-Klassen und Darstellung in der View
Rioma
myCSharp.de - Member



Dabei seit:
Beiträge: 228

Themenstarter:

Entity Framework-Klassen und Darstellung in der View

beantworten | zitieren | melden

Hallo zusammen,

wie handhabt ihr die Entitäten bezüglich anzuzeigender Daten? Ich lese sehr oft, dass Entitäten nichts im ViewModel verloren haben, leider machen das Microsoft beispiele häufig so. Ich nehme an, dass ihr die Entitäten dann nochmal auf Models für die View mapped
(von mir aus: PersonEntität wird zu Person oder so ähnlich).

Falls ja:


1. Warum dieser Vorgang? nur weil ein paar Spalten aus der Datenbank nicht in der View gebraucht werden, oder hat das noch andere Gründe? (man könnte per Fluent Api ja auch einige Spalten außen vor lassen)

2. Wo findet dieser Vorgang statt? --> BusinessLayer?

3. Benutzt ihr Frameworks wie Automapper dafür?

4. Hat jemand vielleicht sogar ein Beispiel, wo man sich so etwas angucken und nachvollziehen kann? [/list]

Danke euch :)
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Rioma am .
private Nachricht | Beiträge des Benutzers
MorphieX
myCSharp.de - Member



Dabei seit:
Beiträge: 184
Herkunft: Rahden

beantworten | zitieren | melden

Darüber denke ich auch sehr oft nach...

So bin ich sonst immer vorgegangen:
Model (POCO-Klassen) erstellt und INotifyPropetyChanged implementiert
Wenn es berechnete Properties (z.B. Menge * Einzelpreis = Gesamtpreis) sind, die nicht zur Datenbank gehören, benutze ich das [NotMapped]-Attribute.
Dieses Model benutze ich dann mit dem EntityFramework als Entität
Bei mir sind die Views meist Fenster, dafür erstelle ich separate ViewModels. Diese ViewModels besitzen dann aber meist ein Objekt mit einer Instanz einer Entität, die ich jetzt ebenfalls an die View binden kann, oft auch eine ObservableCollection einer Entität.

Damit bin ich bis jetzt immer gut zurecht gekommen.
Man liest aber immer wieder, dass das "nicht richtig" oder "schlecht" sei...

Darum überlege ich mir zur Zeit dieses Konstrukt:

Die Entitäten (wieder POCO-Klassen) besitzen wirklich nur die Properties, die sie wirklich abspeichern wollen
Zu jeder Entität gibt es eine abstrakte Basis-ViewModel-Klasse, die alle Properties der Entität 1:1 nachbilden, zusätzlich aber noch INotifyPropertyChanged implementieren
Diese Basisklassen würde ich mir über eine T4-Vorlage automatisch erzeugen lassen...
Dann gibt es zu jeder Entität mindestens ein ViewModel, welches von der Basisklasse erbt, zusätzlich aber noch weitere Properties zur Verfügung stellt (z.B. Menge * Einzelpreis = Gesamtpreis)

Dann gibt es wieder für jedes Fenster ein ViewModel, welches mindestens eine Instanz der Entity-ViewModels besitzt. Also dasselbe wie oben, nur dass nicht direkt auf die Models gebunden wird, sondern noch die VM-Klasse dazwischen liegt.

Wäre das vielleicht ein "richtigerer" weg? ;-)
private Nachricht | Beiträge des Benutzers
Coffeebean
myCSharp.de - Team

Avatar #avatar-3295.gif


Dabei seit:
Beiträge: 2459
Herkunft: Deutschland/Schweiz

beantworten | zitieren | melden

Im Asp.Net-Bereich nimmt man oft DTOs dafür.

Das sind Objekte, die speziell nur das enthalten, was die View braucht. Dafür benutze ich eine Factory und mappe das Ganze. Vor dem return ein

....Select(x => _myFactory.Create(x));

und es werden DTOs zurückgegeben.

Da du nicht geschrieben hast, in welchem Umfeld wir uns befinden komme ich jetzt einfach mal mit dem Asp.Net-Fall.

Asp.Net: Create Data Transfer Objects (DTOs)

Zu 1) Siehe Link
Zu 2) Je nach Umgebung. Baust du eine SPA kannst dus ein wenig anders machen als in einer MVVM-Umgebung. Wie so oft: Kommt darauf an.
Zu 3) Kann man machen, ja. Github - Automapper
Zu 4) Siehe Link.

Gruss

Coffeebean
private Nachricht | Beiträge des Benutzers
Rioma
myCSharp.de - Member



Dabei seit:
Beiträge: 228

Themenstarter:

beantworten | zitieren | melden

Danke erstmal für eure Antworten.

Umfeld wäre C# + WPF + MVVM + EF6 + MSSQL 2014

Ich denke aber dass sich das Asp.Net Beispiel recht gut übertragen lässt.

Mein vorgehen wäre dann wahrscheinlich wie schon im ersten Post angedeutet:

DAL gibt Entitäten zurück und im BL erzeuge ich mir daraus meine Objekte für die View und reiche diese entsprechend bis ins ViewModel.

Aber wo ist der große vorteil die Objekte von ein paar Properties zu befreien und wie oft kommt es vor, dass eine Entität Daten enthält, die die View nicht braucht?

Mein vorgehen war bisher EF6 CodeFirst und ich habe mir hier überlegt, was ich für Daten brauche. Rausfallen für die View, würde zum Beispiel die entsprechende ID, aber rechtfertigt die ID den Mehraufwand? Kann sich hier vielleicht jemand ein Praxis bezogenes Beispiel eines bestimmten Objektes (z.B. wie oben beschrieben PersonEntität wird zu Person --> was kann wegfallen/kommt hinzu) in diesem Umfeld aus den Fingern "saugen"?
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15827
Herkunft: BW

beantworten | zitieren | melden

Zitat von Rioma
Aber wo ist der große vorteil die Objekte von ein paar Properties zu befreien und wie oft kommt es vor, dass eine Entität Daten enthält, die die View nicht braucht?
In vermutlich 99% aller Fälle.
Es geht ja nicht nur darum, dass eine View weniger Eigenschaften braucht als ein Objekt hat - sondern meist umgekehrt:

In einer Benutzerregistrierung gibt es oft eine Länderauswahl - ergo eine Liste.
Das Datenbank-Objekt hat aber nur eine Eigenschaft; nämlich das Land, das er sich eben ausgesucht hat.

Hinzu kommt, dass Views meist völlig anders zusammengesetzt sind als Entitäten.

AutoMapper gehört aber nicht in den DAL.
STOP USING AUTOMAPPER IN YOUR DATA ACCESS CODE
Why mapping DTOs to Entities using AutoMapper and EntityFramework is horrible
private Nachricht | Beiträge des Benutzers
Rioma
myCSharp.de - Member



Dabei seit:
Beiträge: 228

Themenstarter:

beantworten | zitieren | melden

Alles klar, das ergibt auf jeden Fall Sinn. Automapper hätte ich auch nicht im DAL gehabt, sondern im BL. Ich würde im BL dann die Entitäten aus der Datenbank über den DAL bekommen und hier ein Mapping für die View vornehmen.

Allerdings scheint laut den Links Automapper nicht die beste Variante für EF zu sein. Bzw. erfordert es sehr viel "undurchschaubaren" Code. Darf ich Fragen wie du/ihr dies in einer WPF + MVVM + EF Anwendung realisiert?
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Rioma am .
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15827
Herkunft: BW

beantworten | zitieren | melden

Da wir komplett auf IQueryable setzen, mappen wir komplett alles mit eigenen Implementierungen, die eine Mischung Repository- und Adapter Pattern.

public interface IMapper<T1, T2> {}

public abstract class Mapper<T1, T2> : IMapper<T1, T2>
{
 // Gemeinsame Implementierungen, zB Collections hier.
// abstrakte Methode für konkrete Implementierung
}

public interface ICurrencyMapper : IMapper<BackendModels.Currency, EntityModels.Currency> {}

public class CurrencyMapper : Mapper<BackendModels.Currency, EntityModels.Currency>, ICurrencyMapper 
{
 // konkrete Implementierungen
}

public interface IArticleMapper : IMapper<BackendModels.Article, EntityModels.Article> {}

public class ArticleMapper : Mapper<BackendModels.Article, EntityModels.Article>, IArticleMapper 
{
 // konkrete Implementierungen
 public ArticleMapper (IArticleRepository rep, ICurrencyMapper currencyMapper()
{
} 

}
private Nachricht | Beiträge des Benutzers
t0ms3n
myCSharp.de - Member



Dabei seit:
Beiträge: 319

beantworten | zitieren | melden

Der Artikel ist zwar etwas älter, aber dazu folgende Frage. (Kann gerne abgeleitet werden, sofern sinnvoll).

Nehmen wir folgendes Repository...


    public interface IRepository<TEntity>
    {
        TEntity FindById(int id);
        ....       
    }

Möchte ich nun irgendwo nur bestimmte Daten dieser Entität nutzen, kann ich diese entsprechend mappen. Dies ändert aber nichts an der Abfrage die gegen die Datenbank läuft.

Abt sagte, dass sie vollständig auf IQueryable setzen. Folglich sähe das Repository so aus ?:


    public interface IRepository<TEntity>
    {
        IQueryable<TEntity> FindById(int id);
        ....       
    }

Ist es nun der sauberere Weg, dies so zu tun und z.B. im BL entsprechend die gewünschte Projection vorzunehmen?

Oder nutzt ihr, da Tools wie AutoMapper (mittlerweile) ja auch mit IQueryable entsprechend umgehen können diese im DAL, sodass ein Repository z.B. so aussieht und kein IQueryable verfügbar gemacht wird:


    public interface IRepository<TEntity>
    {
        TModel FindById<TModel>(int id);
        ....       
    }
private Nachricht | Beiträge des Benutzers
Coffeebean
myCSharp.de - Team

Avatar #avatar-3295.gif


Dabei seit:
Beiträge: 2459
Herkunft: Deutschland/Schweiz

beantworten | zitieren | melden

Hallo t0ms3n,

wenn du mit IQueryable arbeitest würde ich bei ASP.NET mit OData, den Query-Options und eben IQueryable auch bei einer Single-Entity arbeiten. Somit kannst du dir erstmal mit mit einer Where() klausel mit der ID die eine Entity als Queryable holen und dann die SingleResult.Create(); Methode benutzen. OData Queryoptions werden dann darauf angewandt und wenn du dir das Query ggn die Datenbank anschaust siehst du, dass nur deine Felder abgerufen werden.

 [HttpGet]
        [EnableQuery]
        [ODataRoute("Houses({id})")]
        public IHttpActionResult GetSingleHouse([FromODataUri] int id)
        {
            IQueryable<HouseEntity> house = _houseRepository.GetAll().Where(x => x.Id == id);

            if (!house.Any())
            {
                return NotFound();
            }

            return Ok(SingleResult.Create(house));
}

Aufruf zum Beispiel: http://localhost:3153/odata/Houses(1)?$select=Street, City

SingleResult.Create<T> Method

Gruss

Coffeebean
private Nachricht | Beiträge des Benutzers
t0ms3n
myCSharp.de - Member



Dabei seit:
Beiträge: 319

beantworten | zitieren | melden

Hmmm, die Verwendung von OData ist an der Stelle doch aber nur bedingt relevant. Die eigentliche Frage ist ja, wann und wo ich eine Entität des DALs in eine entsprechend DTO/ViewModel wandle?

Der OData Fall spräche allerdings für das zweite Beispiel, also das mein Repository immer IQueryable liefert und (um bei ASP.NET zu bleiben) der Controller die Projektion durchführt, welche selbst ja auch wieder ein IQueryable ist.
private Nachricht | Beiträge des Benutzers