Laden...

Entity Framework-Klassen und Darstellung in der View

Erstellt von Rioma vor 8 Jahren Letzter Beitrag vor 7 Jahren 2.500 Views
R
Rioma Themenstarter:in
228 Beiträge seit 2013
vor 8 Jahren
Entity Framework-Klassen und Darstellung in der View

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 😃

M
184 Beiträge seit 2012
vor 8 Jahren

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? 😉

2.207 Beiträge seit 2011
vor 8 Jahren

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

R
Rioma Themenstarter:in
228 Beiträge seit 2013
vor 8 Jahren

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"?

16.807 Beiträge seit 2008
vor 8 Jahren

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

R
Rioma Themenstarter:in
228 Beiträge seit 2013
vor 8 Jahren

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?

16.807 Beiträge seit 2008
vor 8 Jahren

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()
{
} 

}
T
314 Beiträge seit 2013
vor 7 Jahren

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);
        ....       
    }

2.207 Beiträge seit 2011
vor 7 Jahren

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

T
314 Beiträge seit 2013
vor 7 Jahren

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.