|
» myCSharp.de Diskussionsforum |
|
|
|
Autor
 |
|
Rioma
myCSharp.de-Mitglied
Dabei seit: 31.10.2013
Beiträge: 228
Entwicklungsumgebung: Visual Studio 2015 Pro
|
|
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=1]Danke euch :)
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Rioma am 03.06.2015 15:02.
|
|
03.06.2015 15:01
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
MorphieX
myCSharp.de-Mitglied
Dabei seit: 06.02.2012
Beiträge: 184
Entwicklungsumgebung: VS 2015 Community Herkunft: Rahden
|
|
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? ;-)
|
|
03.06.2015 15:49
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
|
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
C#-Code: |
....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
|
|
03.06.2015 15:58
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Abt
myCSharp.de-Team
Dabei seit: 20.07.2008
Beiträge: 14.461
Herkunft: BW
|
|
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
|
|
03.06.2015 18:19
|
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Rioma
myCSharp.de-Mitglied
Dabei seit: 31.10.2013
Beiträge: 228
Entwicklungsumgebung: Visual Studio 2015 Pro
Themenstarter
|
|
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 03.06.2015 20:21.
|
|
03.06.2015 20:19
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Abt
myCSharp.de-Team
Dabei seit: 20.07.2008
Beiträge: 14.461
Herkunft: BW
|
|
Da wir komplett auf IQueryable setzen, mappen wir komplett alles mit eigenen Implementierungen, die eine Mischung Repository- und Adapter Pattern.
C#-Code: |
public interface IMapper<T1, T2> {}
public abstract class Mapper<T1, T2> : IMapper<T1, T2>
{
}
public interface ICurrencyMapper : IMapper<BackendModels.Currency, EntityModels.Currency> {}
public class CurrencyMapper : Mapper<BackendModels.Currency, EntityModels.Currency>, ICurrencyMapper
{
}
public interface IArticleMapper : IMapper<BackendModels.Article, EntityModels.Article> {}
public class ArticleMapper : Mapper<BackendModels.Article, EntityModels.Article>, IArticleMapper
{
public ArticleMapper (IArticleRepository rep, ICurrencyMapper currencyMapper()
{
}
}
|
|
|
03.06.2015 20:30
|
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
Zwischen diesen beiden Beiträgen liegt mehr als ein Jahr. |
t0ms3n
myCSharp.de-Mitglied
Dabei seit: 13.01.2013
Beiträge: 314
|
|
Der Artikel ist zwar etwas älter, aber dazu folgende Frage. (Kann gerne abgeleitet werden, sofern sinnvoll).
Nehmen wir folgendes Repository...
C#-Code: |
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 ?:
C#-Code: |
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:
C#-Code: |
public interface IRepository<TEntity>
{
TModel FindById<TModel>(int id);
....
}
|
|
|
17.06.2016 10:31
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
|
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.
C#-Code: |
[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
|
|
17.06.2016 10:40
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
t0ms3n
myCSharp.de-Mitglied
Dabei seit: 13.01.2013
Beiträge: 314
|
|
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.
|
|
17.06.2016 11:19
|
E-Mail |
Beiträge des Benutzers |
zu Buddylist hinzufügen
|
|
|
|