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
oData und die 3-Schichten-Architektur
david777
myCSharp.de - Member



Dabei seit:
Beiträge: 37

Themenstarter:

oData und die 3-Schichten-Architektur

beantworten | zitieren | melden

Hallo Leute,

Ich mache einen neuen Thread auf, weil ich über das Thema jetzt schon eine Menge gelesen habe, jedoch alle Lösungsvorschläge im Netz ihre Nachteile zu haben scheinen.
Angenommen ich möchte eine einfache SW schreiben, die Autos verwalten soll.
Auf dem DAL habe ich dann z.B. eine Klasse "CarEntity", im BL eine Klasse "Car" und in der UI eine Klasse "CarViewModel".

Bei der UI kommt jetzt ein oData-Query an:

cars?$filter=name eq 'audi'

In meinem oData Controller kann ich das Query folgendermaßen auffangen:

public IHttpActionResult GetHouses(ODataQueryOptions<DataAccessLayer.Car> qo)
{


}

Im Objekt "qo" ist jetzt mein Query enthalten. Doch schon hier gibt es das erste Problem:

1. Weil das Query am Ende auf DataAccessLayer.Car -Objekte losgelassen werden soll habe ich keine andere Wahl als den Typ hier mit anzugeben. Mein UI-Layer soll aber die Datentypen aus dem DAL nicht kennen dürfen.

Um dieses Problem zu lösen müsste ich das ankommende Query erst einmal so umwandeln, dass es auf BusinessLayer.Car - Objekte zugreift.
Im BL müsste ich dann eine weitere Umwandlung vornehmen, die auf DAL-Objekte zugreift.

Diese Umwandlung zu schreiben ist aber ein riesiger Aufwand. Ist das also wirklich die richtige Herangehensweise?
Wenn ich nach oData + 3-Layer-Architecture suche finde ich ausschließlich Beispiele, wo es den Entwicklern egal ist und sie direkt auf den DAL zugreifen, wie ich es auch bisher gemacht habe. (z.B. OData FAQ)

Aber mir wurde hier immer wieder genau davon abgeraten...
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von david777 am .
private Nachricht | Beiträge des Benutzers
t0ms3n
myCSharp.de - Member



Dabei seit:
Beiträge: 314

beantworten | zitieren | melden

Ich habe darauf keine Antwort für dich, da ich mir weiterhin die gleiche Frage stelle. Eben auch nicht nach dem ob es generell möglich ist, sondern wie es für kleinere Anwendungen mit vertretbarem Aufwand möglich ist.

Ich habe im Rahmen von [Review] Best Practices/Architektur anhand von Bespielprojekt (WebApi + OData + EF + SPA (Aurelia)) auch mit AutoMapper etc. experimentiert und finde, dass dieser viele Punkte ganz gut lösen kann.

(auch wenn Abt immer wieder erwähnt, dass man diesen nicht nutzen soll und auch entsprechende Links dazu liefert. Ich fand allerdings nicht, dass diese noch vollständig zutreffen, da AutoMapper mittlerweile auch eine Projektions-Extension mitbringt.)

Um allerdings die harte Bindung an deinen DAL zu verlieren, kannst du die Entities auf jeden Fall in eine eigene DLL auslagern.
private Nachricht | Beiträge des Benutzers
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 9.977

beantworten | zitieren | melden

Ausser bei einer SW die OData selber als Aufgabe hat, sehe ich keinen einzigen Grund warum die UI in OData mit darunter liegenden Schichten reden sollte.

Die UI hat beim BL gefälligst ein DataService.GetCarById(id) abzufragen.
private Nachricht | Beiträge des Benutzers
david777
myCSharp.de - Member



Dabei seit:
Beiträge: 37

Themenstarter:

beantworten | zitieren | melden

Aber dann gehen mir doch alle Möglichkeiten, die oData liefert verloren. Da kann ich auch gleich das Protokoll vergessen und wieder mit normalem REST arbeiten.
Welchen Sinn haben oData-Queries in der URL, wenn ich dann im BL doch alles neu schreiben muss?
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15.928

beantworten | zitieren | melden

OData ist keine Eierlegende Wollmilchsau.
Es hat Vor- wie Nachteile.

OData selbst gibt Dir nur einen Rahmen, wie Du Nachrichten austauschst - mit gewissen Standardfeldern.
Es gibt hier auch andere Basen, wie zB. Siren, oder Json-LD oder HAL..... alle haben einen Fokus und eben die Vor- und Nachteile.

Wie ichs im anderen Thread schon gesagt habe:
OData sollte nicht direkt mit den Entitäten arbeiten. OData arbeitet gegendie Business-Schicht und diese stellt Expression Trees zur Verfügung.
Genau so kannst Du die Vorteile von OData wie Filter und Selects auch weitergeben und trotzdem das 3-Schichten-Modell verfolgen.

Du musst sie nicht neu schreiben. Auch das hab ich Dir bereits gesagt (und zwar hier: oData Queries auf Datenbank oder lokale Objekte).

Die Beispiele machen das nicht, weil das oft erschlägt. Das ist auch nicht das Ziel solcher Beispiele.
Entitäten haben i.d.R. Felder, die Du gar nicht nach Außen mitgeben willst oder darfst oder kannst.
Das ist übrigens auch mein Feedback in [Review] Best Practices/Architektur anhand von Bespielprojekt (WebApi + OData + EF + SPA (Aurelia))
private Nachricht | Beiträge des Benutzers
david777
myCSharp.de - Member



Dabei seit:
Beiträge: 37

Themenstarter:

beantworten | zitieren | melden

Zitat von Abt
OData sollte nicht direkt mit den Entitäten arbeiten. OData arbeitet gegendie Business-Schicht und diese stellt Expression Trees zur Verfügung.
Genau so kannst Du die Vorteile von OData wie Filter und Selects auch weitergeben und trotzdem das 3-Schichten-Modell verfolgen.

Genau das ist der Punkt, an dem ich jetzt schon so lange recherchiere. Ich weiß, was ein Expression Tree ist, doch ich habe keine Ahnung, wie ich dieses Wissen jetzt verwenden soll.

1. Wie bekomme ich meinen oData-Request auf einen Expression Tree "gemappt"?
2. Wie komme ich überhaupt an den Expression Tree meiner oData Abfrage?

Ich habe die anderen Beiträge schon gelesen und versucht herauszufinden, was du meinst.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15.928

beantworten | zitieren | melden

Sorry, grad nur am Handy und daher anständiges Tippen suboptimal; aber such mal nach ODataUriParser.
Oder: Du machst das EntitySet von Odata nicht auf Entitäten, sondern auf BusinessModelle.
Die Schicht zwischen Business Modell und OData implementiert dann eben diesen Expression Tree oder einen Mapper, der IQueryable<Model, Entity> unterstützt.
private Nachricht | Beiträge des Benutzers
david777
myCSharp.de - Member



Dabei seit:
Beiträge: 37

Themenstarter:

beantworten | zitieren | melden

Ok - der Begriff ist vermutlich auch für t0ms3n interessant:
Zitat von t0ms3n
Hallo zusammen,
- Im OData Bereich ist es mir weiterhin rätselhaft, wie es möglich ist, dort nicht die Entitäten der Datenbank zu nutzen und trotzdem die OData Funktionalitäten zu erhalten
.

Denn das hieße ja, dass ich mit dem UriParser erst einmal alle Informationen aus der URI bekomme.

Im BL baue ich dann anhand dieser Informationen einen Expression Tree, der am Ende auf das IQueryable des EF losgelassen wird. Bin ich da auf dem richtigen Weg?
Klingt dennoch nach viel Aufwand...
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von david777 am .
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15.928

beantworten | zitieren | melden

Zitat von david777
Klingt dennoch nach viel Aufwand...

Keine Frage; das ist es. Habe auch nie was anderes behauptet :-)
private Nachricht | Beiträge des Benutzers
t0ms3n
myCSharp.de - Member



Dabei seit:
Beiträge: 314

beantworten | zitieren | melden

Ihr stürzt mich in das Land der Tränen :D
private Nachricht | Beiträge des Benutzers
david777
myCSharp.de - Member



Dabei seit:
Beiträge: 37

Themenstarter:

beantworten | zitieren | melden

Dann ist es für kleine Anwendungen overkill es so zu machen, richtig?
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15.928

beantworten | zitieren | melden

Zitat von t0ms3n
Ihr stürzt mich in das Land der Tränen :D

Warum?
Zitat von david777
Dann ist es für kleine Anwendungen overkill es so zu machen, richtig?
Das kann man pauschal nicht sagen. Das muss man abschätzen und evaluieren.

Wenn die API klein ist aber tausende von Clients (die man oft nicht kennt) zugreifen, wird sich OData aufgrund der Vorteile i.d.R. lohnen.

Aber es gibt auch andere Hypermedia-Ansätzen wir Siren oder Json-LD, die evtl auf das ein oder andere besser passen als OData.
private Nachricht | Beiträge des Benutzers
david777
myCSharp.de - Member



Dabei seit:
Beiträge: 37

Themenstarter:

beantworten | zitieren | melden

Zitat von david777
Denn das hieße ja, dass ich mit dem UriParser erst einmal alle Informationen aus der URI bekomme.

Im BL baue ich dann anhand dieser Informationen einen Expression Tree, der am Ende auf das IQueryable des EF losgelassen wird. Bin ich da auf dem richtigen Weg?

Also stimmte diese Vermutung. So geht es?
private Nachricht | Beiträge des Benutzers
LaTino
myCSharp.de - Experte

Avatar #avatar-4122.png


Dabei seit:
Beiträge: 3.003
Herkunft: Thüringen

beantworten | zitieren | melden

Ja. Im Prinzip ist das aber auch genau, was man "von Hand" machen würde, nur abstrahiert.

LaTino
"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)
private Nachricht | Beiträge des Benutzers
david777
myCSharp.de - Member



Dabei seit:
Beiträge: 37

Themenstarter:

beantworten | zitieren | melden

Es geht ohne Expression Trees!
Ich habe noch etwas recherchiert und jetzt folgendes herausgefunden:

1. Im Controller Queryable deaktivieren
2. Dort ODataQueryOptions vom Typ "ViewModel" der Presentationsschicht holen
3. Die Queryoptions können an den BL gegeben werden und dort in ODataQueryOptions vom Typ des BL (bzw. DAL) umgewandelt werden:

z.B. geht das so:
//getEdmModel() liefert das EdmModel der entsprechenden Schicht

ODataQueryOptions dqo = new ODataQueryOptions(new ODataQueryContext(GetEdmModel(), typeof(DataAccessLayer.House), qo.Context.Path), qo.Request);


Somit kann ich mir den aufwändigen Weg über Expression Trees sparen und verletze nirgendwo die 3 Schichten. Ich hoffe niemand hat jetzt wieder ein super Gegenargument und sagt mir, dass dieser Weg der total falsche ist :-D
private Nachricht | Beiträge des Benutzers
t0ms3n
myCSharp.de - Member



Dabei seit:
Beiträge: 314

beantworten | zitieren | melden

Du hast ein EdmModel für die unterschiedlichen Schichten? Das klingt für mich nun allerdings zu mindestens merkwürdig :).
private Nachricht | Beiträge des Benutzers
david777
myCSharp.de - Member



Dabei seit:
Beiträge: 37

Themenstarter:

beantworten | zitieren | melden

Naja - nicht wirklich. Auf der obersten Ebene nur noch um den URL String zu bekommen. Versuche gerade das zu löschen. Im BL keins mehr und nur noch ganz unten im DAL.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15.928

beantworten | zitieren | melden

Ich versteh auch nicht ganz, wie das funktionieren soll.
Aber vielleicht, sofern das am Ende klappt, könntest Du das mal an einem Beispiel illustrieren.

Immer interessant, wenn es irgendwie effizienter geht.
private Nachricht | Beiträge des Benutzers
david777
myCSharp.de - Member



Dabei seit:
Beiträge: 37

Themenstarter:

beantworten | zitieren | melden

Wenn ich eine fertige Lösung habe, die super funktioniert, poste ich es hier auf jeden Fall rein.
private Nachricht | Beiträge des Benutzers
mfe
myCSharp.de - Member



Dabei seit:
Beiträge: 177

beantworten | zitieren | melden

@david777 Na wie siehts aus ? ;)

Ein umgesetztes Beispiel dazu würde mich auch interessieren.
private Nachricht | Beiträge des Benutzers