Laden...

Entities als Business Objects? In Verbindung mit WCF

Erstellt von zack0r vor 14 Jahren Letzter Beitrag vor 14 Jahren 3.380 Views
Z
zack0r Themenstarter:in
15 Beiträge seit 2006
vor 14 Jahren
Entities als Business Objects? In Verbindung mit WCF

verwendetes Datenbanksystem: MS SQL 2008 Express
Visual Studio 2008
.NET 3.5 SP1

Hi,
hab zu dem Thema schon gesucht aber nichts gefunden was meine Frage beantwortet hat.

Bin im moment dran ne Client-Server-Datenbank Anwendung zu schreiben.
Der Server greift auf die Datenbank zu (per DAL, über Entity Framework), regelt alles Geschäftliche und gibt die Daten über WCF an einen Client. (bzw. eigentlich holt sich natürlich der Client die Daten vom Server 😃 )

Ich hab auch soweit schon nen Testlauf laufen lassen und es hat funktioniert.
Jetzt ist meine Frage: (Enitites == Business Objects)

Ich hab z.B. ein entity article und im DAL ne Methode getArticles() die ein Object List<article> zurückgibt. Dieses wird dann über den WCF-Service angeboten und im Client kann ich es direkt an ein Grid binden.

Jetzt hab ich eben mal die Referenz vom WCFService auf den DAL entfernt, da ich den Business Layer eingebaut habe. Jetzt weiß natürlich der WCFService nicht mehr was ein Object article überhaupt ist.
Muss ich mein EntityModell also nochma in ne Extra KlasseBibliothek packen (anstatt DAL) und von jeder anderen Klasse n Verweis dazu machen?

Oder muss ich nochmal extra jede Tabelle als BusinessObject schreibt, was ich für ziemlich unsinnig halte.

Danke im Vorraus für Antworten 😃

gruß zack0r

3.728 Beiträge seit 2005
vor 14 Jahren
Verteilte Anwendungen

Hallo zack0r,

"Business Objects" ist ein Buzzword. Jeder versteht darunter entwas anders. Für manche Leute sind das einfache Strukturen für den Datentransfer zwischen den Komponenten. Andere sehen darin statuswahrende Objekte, die Geschäftslogik über ein Objektmodell zugänglich machen. Und so weiter. Was meinst Du damit?

Das Entity Framework taugt momentan noch nicht für verteilte Anwendungen. Erst mit .NET 4.0 soll da was ändern (http://blogs.msdn.com/adonet/archive/2009/05/14/sneak-preview-n-tier-development-with-entity-framework-4-0.aspx).

Ein paar Gedanken zu dem Thema findest Du hier:
http://yellow-rainbird.de/blogs/rainbird/archive/2009/01/09/habe-sehnsucht-nach-dem-rowstate.aspx
Entity Framework auf Änderungen überprüfen

Ich möchte Dir deshalb sehr davon abraten, das Entity Framework einzusetzen, wenn Du eine verteilte Anwendung schreibst.

Z
zack0r Themenstarter:in
15 Beiträge seit 2006
vor 14 Jahren

Danke für die Antwort 😃

Hmm, jetzt hab ich mir gedacht arbeite dich doch mal in was neues modernes ein und jetzt erzählst du mir, dass die noch garnicht so weit sind... frechheit 😄

zum Glück meine erste Client-Server-Anwendung.

Das mit den Änderungen hab ich bis jetzt so gelöst:
Jeder User der connected kommt in ne Liste(typ: User) und bekommt ne Guid. Jeder User hat wieder ne Liste (typ: Tables) mit den Tables wo für jede Tabelle drin steht ob das Result Live ist.
D.h. im klartext der Client schickt per timer jede Sekunde ne Anfrage ob das Result Live ist. Wenn das false ist, dann wird aktualisiert.
Wenn jetz im DAL ne änderung vorgenommen wird wird für jeden User für die geänderte Tabelle die Eigenschaft Live auf false gesetzt.

Naja kA ob das so effizient ist?

Hmm und zu den änderungen (ich bezieh mich jetzt mal auf die Argumente von dir auf dem anderen Thread) hab ich bis jetzt das hier implementiert:

article ist ein Entity
Srv ist meine statische Server Klasse die z.B. die Userliste managed

Das ist z.B. der Code von dem ich oben gesprochen hab. Damit wird jedem User bei der nächsten Anfrage mitgeteilt das article geändert wurde. (noch etwas unbeholfen mit string...)


Srv.tableChanged("article");


        public void editArticle(article obj)
        {
            try
            {
                object original = null;

                EntityKey key = ctx.CreateEntityKey("article", obj);

                if (ctx.TryGetObjectByKey(key, out original))
                {
                    ctx.ApplyPropertyChanges(key.EntitySetName, obj);
                }
                else
                    throw new ObjectNotFoundException();

                ctx.SaveChanges();
                Srv.tableChanged("article");
                Srv.log("[DAL] " + obj.GetType().ToString() + " Edit: ID=" + obj.ArticleID);
            }
            catch (Exception e)
            {
                Srv.log("[DAL] Edit Exception: " + e.Message.ToString());
            }
        }

Noch zur Info:
das article object kommt direkt aus dem Grid des Clients und geht dann diesen weg

articleGrid.CurrentRow.ItemObject (oder so) => WCFProxy => WCFService => BLL (in meinem Fall articleManager) => DAL

Also die änderung erfolgt komplett ohne Detach & Attach.

Bestimmt alles Dirty Tricks :x

Gute Nacht

Achso.
Was ich etwas komisch finde.. kann es sein das Entities keine wirkliche Oberklasse haben? Weil ich z.B. kein List<EntityObject> erstellen kann wo ich dann meine spezifischen Entities einsetzen kann.

Weil so muss ich ja für jedes Entity/Tabelle extra ne add, edit, delete Methode schreiben....

3.728 Beiträge seit 2005
vor 14 Jahren
Serverlast

Von der Timer-Lösung solltest Du Abstand nehmen. Du erzeugst damit eine Grundlast, die Ressourcen verbraucht, auch wenn gerade eigentlich gar nichts passiert. Jetzt stell Dir vor, Du hättest 500 Clients. Dein Server wäre schon ein viertel ausgelastet, nur damit die Clients ständig wissen, ob sie noch aktuelle Daten haben. Das kann die Lösung nicht sein.

Dein Code ist auch von einer anderen Seite her nicht optimal. Was ist, wenn ich einen Artikel innerhalb einer größeren Transaktion ändere (die auch andere Tabellen betrifft)? Wenn diese Transaktion fehlschlägt, welchen Status haben dann die Clients?

Ich entdecke noch etwas Unschönes! Du kannst immer nur einen einzelnen Artikel ändern. Wenn ich nun eine neue Warengruppe aus dem elektronischen BMECat-Katalog eines Lieferanten mit 3000 neuen Artikeln aufnehmen will, muss ich bei Deiner Lösung 3000 Netzwerk-Zugriffe machen. Das heißt 3000 Transaktionen auf der DB und 3000 x den Kommunikationsoverhead. Wenn das System dann bei Artikel Nr. 2356 einen Fehler hat und abbricht, ist die ganze DB inkonsistent. Hinzu kommt, dass ich dabei Kaffee trinken gehen kann, bis er mein Sortiment-Update gemacht hat.

Und da fällt mir noch was auf. Dein Service-Code ist statuswahrend. Für jede Sitzung musst Du einen DataContext halten. Da wird Deinem Server bereits nach wenigen Sitzungen die Puste ausgehen. Die Mittelschicht solltest Du statuslos entwicklen.

Mir scheint, Du versuchst Deine verteilte Anwendung zu programmieren, als wäre sie ein 1-Tier-OOP-Anwendung. Dieser Fehler wird oft gemacht, wenn man beginnt n-Tier-Anwendungen zu schreiben. Hier findest Du ein komplettes und, von der Architektur her auch, praxiserprobtes n-Tier-Beispiel: .NET Applikationsserver
Da geht es zufällig auch um Artikel. Vielleicht kann Dir dieses Beispiel als Anregung dienen. Fragen und Kritik dazu sind in folgendem Thread gerne willkommen: Fragen, Diskussion, Kritik zu Projekt ".NET Applikationsserver"

P.S. Lass die Entity Framework Geschichte in Verbindung mit n-Tier bleiben, sonst programmierst Du Dir ´nen Wolf (Auf .NET 4.0 wirst Du ja nicht warten wollen, oder?).

Z
zack0r Themenstarter:in
15 Beiträge seit 2006
vor 14 Jahren

Hi,
hab ich ganz vergessen zu erwähnen. Es ging hierbei in erster Linie um ein Testproject um WCF, EF und n-Tier besser kennen zulernen (mit C# hab ich eigentlich auch noch nicht gearbeitet). Also ich wollte versuchen einen möglichst effizienten und praktischen Grundablauf zu schaffen bevor die Anwendung weiterentwickelt wird.
Daher auch das mit der Tabelle article. Das ist im Moment die einzige und wird es auch bleiben bis das Grundgerüst steht.

Hintergrund ist, ich habe die Anwendung schonmal geschrieben aber als Fat Client in Delphi... Leider hat sich nach etwas praktischem Einsatz gezeigt das der Fat Client viel zu langsam ist.

Deshalb ein Neuanfang.

Deshalb bin ich auch über Kritik sehr Dankbar, damit ich nicht die selben Fehler nochmal mache 😃

Also: Timer. Dachte ich mir schon das das nicht optimal ist. Wie macht man dass denn richtig? Ich kann ja im WCF keinen broadcast an alle Clients senden, dass sich was geändert hat. Also irgendwie muss mein Client ja abfragen. Ich dachte mir bevor jede Sekunde die komplette Tabelle übertragen wird, lieber nur ne Abfrage und nur die Tabelle übertragen wenn sich wirklich was geändert hat.

Änderung: Soweit ich mich erinnere war das unter Delphi damit gelöst worden, dass der entsprechende Eintrag der gerade editiert wird gelockt wird. Damit er nicht ein zweites mal parallel geändert werden kann.

Mehere Einträge auf einmal ändern brauchte ich noch nie. aber könnte man ja dann durch ne Stored Procedure durchführen denk ich mal. Oder?

Ich weiß nicht so recht was du mit Statuswahrend meinst.
Meine DAL-Klasse ist nicht static, sondern wird bei jedem zugriff vom BLL instanziert. Im contructor wird dann der context aufgebaut und im destructor auf null gesetzt.

Sollte ich DAL static machen? Immer wenn ich anfange Klassen static zu setzen dann entspricht das so garnicht meiner Vorstellung von OOP.

Mir scheint, Du versuchst Deine verteilte Anwendung zu programmieren, als wäre sie ein 1-Tier-OOP-Anwendung. Dieser Fehler wird oft gemacht, wenn man beginnt n-Tier-Anwendungen zu schreiben. Hier findest Du ein komplettes und, von der Architektur her auch, praxiserprobtes n-Tier-Beispiel: .NET Applikationsserver

Ja, das stimmt. Hab ich auch schon überall gelesen. Das Problem ist, dass jeder was anderen darunter versteht und es meiner meinung nach im Internet keine Optimalen Anleitungen gibt (zumindest hab ich noch keine gefunden).

Daher danke für den Server, werde ich mir auf jeden fall morgen mal genau ansehen.

Gute Nacht

X
1.177 Beiträge seit 2006
vor 14 Jahren

huhu,

Dachte ich mir schon das das nicht optimal ist. Wie macht man dass denn richtig? Ich kann ja im WCF keinen broadcast an alle Clients senden, dass sich was geändert hat. [..] der entsprechende Eintrag der gerade editiert wird gelockt

Über dies wurde hier schonmal ausführlich geredet, leider weis ich den Thread nicht mehr. Prinzipiell: Der Client (User) will etwas ändern, geht dann in Mittag und der Datensatz ist gelocked. Willst du jetzt
a) alle User die was dran ändern wollen Informieren dass Herr Meier in Mittag ist (Seit 30 min. in Bearbeitung) - (Erziehungsmasnahme)
b) Herrn Meier wenn er wiederkommt sagen, dass er nochmal die Änderungen tippen muss, weil der Datensatz zwischendurch geändert wurde.
c) evtl. eine "Neu/Alt" Ansicht zur Verfügung stellen, damit änderungen von Herrn Meier und Herrn Müller passen.

Eine weitere sinnvolle Möglichkeit (ausser "Last Change Wins" - aber nur für "Wir können nix dafür Anwendungen") fällt mir nicht ein. Leider muss man sich hier zwischen Programmieraufwand und Usability entscheiden.

Ich weiß nicht so recht was du mit Statuswahrend meinst.
[..] Sollte ich DAL static machen?

Nein! static != Status-Verbleibend. satic ist nur Programmiertechnisch zu sehen. Mit "Status" ist in DE gemeint, dass man sich den Status des Users merkt. Also z.B. "Müller bearbeitet Datensatz 763 - Alte Daten: RE780384, 6249.47 €| Neue Daten: ..."

Das kann sehr schnell und gründlich ins Auge gehen. Dein Sever braucht sehr schnell viele Ressourcen (Speicher, Rechenzeit)

Wollte noch zu DL und BL sagen: Datalayer zum besorgen/bearbeiten der Daten. Der BL zum testen der Integrität.

Ein Beispiel: Login mit Username und PWD: guckt aus als ob nur ein DL reicht (abfrage zur DB). Dann sagt ein "Manager" - "ja, aber Paswörter müssen mindesten 6 Zeichen lang sein". Das kannst Du nicht mehr im DL abfangen. Wenn jetzt ein BL da wäre (der vorher nix gemacht hat) dann könnte man da die entsprechende Funktion ändern. Der DL und das Frontend laufen weiter wie gewohnt.

In n-Tier-Anwendungen würde ich den BL nie in den Client packen. Überprüfungen (im BL) immer eine Ebene höher, damit man am Client manipulieren kann was man will und es passiert nix.

😃

Xynratron

Herr, schmeiss Hirn vom Himmel - Autsch!

Die Erfahrung zeigt immer wieder, dass viele Probleme sich in Luft auslösen, wenn man sich den nötigen Abstand bzw. Schlaf gönnt.

3.728 Beiträge seit 2005
vor 14 Jahren
Geschäftslogik

In n-Tier-Anwendungen würde ich den BL nie in den Client packen. Überprüfungen (im BL) immer eine Ebene höher, damit man am Client manipulieren kann was man will und es passiert nix.

Das stimmt zwar generell, ist aber manchmal auch nicht ganz das Wahre. Insbesondere dann, wenn beim Client Wert auf Komfort gelegt wird. Wenn ich für jede kleine Prüfung (also z.B. nach Eingabevalidierung einer Textbox) einen Netzwerkzugriff brauche, könnte das problematisch werden. Wenn ich z.B. gerade ein Angebot erfasse, möchte ich, dass nach Eingabe von Menge und Einzelpreis, in einer Position, sofort der Gesamtpreis und sofort die Gesamtsumme des Angebots angezeigt werden. Da möchte ich aber um Himmelswillen keinen Netzwerkzugriff dafür haben. Es handelt sich ja auch noch gar nicht um endgültige Daten, die gespeichert werden sollen.

Ein bischen Geschäftslogik muss deshalb auch immer im Client verfügbar sein. Allerdings sollte das lediglich Triviale Geschätslogik sein (z.B. Feldlängen prüfen oder Summen zusammenrechnen), aber keine komplexe Geschäftslogik (direkte Datenbankzugriffe, Buchungen oder sonstige Transaktionen).

Die Prüfungen und Summenberechnungen müssen deshalb nicht gleich im Formularcode stehen, sondern z.B. im Code eines Typisierten DataSets. Das DataSet kann selbständig die Feldlängen prüfen und andere triviale Dinge prüfen. Ebenso kann man Ausdrücke hinterlegen und eigene Berechnungsfunktionen einbauen. Triviale Geschäftslogik, die in einem DataSet eingebaut ist, kann übrigens der Client und der Server nutzen. Die Geschäftslogik ist also nur ein Mal vorhanden.

Statt eines DataSets könnte man das natürlich auch mit eigenen Datenklassen machen, aber der Aufwand wäre und ein vielfaches höher und der Nutzen bestimmt nicht größer.

Wichtig ist, dass der Server die letzte Bastion ist, was Prüfungen und Berechnungen angeht. Die serverseitigen Geschäftsoperationen müssen ihre Daten immer überprüfen, da es vierschiedene Clients geben kann und möglicherweise nicht alle dieser Clients Teilprüfungen- bzw. Berechnungen vorher macht. Ein Web-Client könnte z.B. auf die Summenberechnung verzichten und die Summen erst nach Klick auf den Submit-Button anzeigen. Die Geschäftslogik auf dem Server ist trotzdem die Selbe und garantiert, dass der Geschäftsprozess korrekt abgewickelt wird und die Daten konsistent und richtig sind.

365 Beiträge seit 2004
vor 14 Jahren

Also: Timer. Dachte ich mir schon das das nicht optimal ist. Wie macht man dass denn richtig?

Hi zack0r,

ich habe Rainbird gestern so ziemlich die gleiche Frage gestellt. Der Thread hier sollte dir also weiterhelfen.

Client/Server Anwendung - Datenänderungen den Clients mitteilem

Gruß

Christoph

Z
zack0r Themenstarter:in
15 Beiträge seit 2006
vor 14 Jahren

Hi,
super danke für die Antworten.
Ich werd jetz erstma das Beispiel studieren und mir weiter gedanken machen.
Ich kenne mich mit Remoting nicht aus, ist aber etwa so wie der Vorgänger von WCF oder?
Ich sehe nur das du anscheinend auch lokal auf dem Server darüber kommunizierst (mit dem Security und dem Locking Service) was hat das für Vorteile, bis auf, dass man die Services auf verschiedene Maschinen aufteilen könnte?
Wieso nicht einfach eingebundene Assemblys?

Beim studieren ist mit noch was aufgefallen, kann es sein, dass deine Singleton-Objekte nicht Threadsicher sind (sagt jemand der eben erfahren hat was singleton ist 😃 )?

Unter Wiki istn Beispiel für Singleton:
Hier unter dem Abschnitt Lazy Creation ist es so wie dus implementiert hast als nicht Threadsicher gekennzeichnet.

Hier ist dann wohl ne bessere Version...

365 Beiträge seit 2004
vor 14 Jahren

Hi zack0r,

ich bin zwar auch nicht so der Held, aber ich antworte trotzdem mal drauf. Haut mir auf die Finger, wenn ich hier falsch liege.

Die Singleton Objekte sind nicht threadsicher, was sie allerdings im vorliegenden Beispiel auch nicht sein müssen, da sie nicht von mehreren Threads abgerufen werden. Etwas threadsicher zu machen geht auch immer mit einem gewissen Performanceverlust einher (Locking Mechanismus). Etwas also threadsicher zu machen, wenn es nicht benötigt wird, wäre also keine gute Wahl.

Gruß

Christoph.

Z
zack0r Themenstarter:in
15 Beiträge seit 2006
vor 14 Jahren

Hi 🤔,
das hier steht in Wiki.

Falls es keine konkreten Einwände gibt, dass das Singleton-Objekt auch schon etwas früher instanziiert werden darf, ist der unter #Eager Creation beschriebene Code einfacher und schneller und daher in C# vorzuziehen.

und zwar zu diesem Code:

  using System ;
 
  sealed class Singleton
  {
    private Singleton() {  /* ...hier optional Initialisierungscode... */ }
    public static readonly Singleton Instance = new Singleton() ;
  }
 
  // Zugriff über
  Singleton s = Singleton.Instance ;

Die Quelle geht auf MSDN:
Microsoft

irgendwo stand da auch noch das der Compiler den Code ein wenig effizienter machen kann wenn man es sealed setzt.

Aber eigentlich wollte ich das nur so im Rande bemerken, da sowas sicher nichts bemerkbar ändert, trotzdem gut zu wissen 🙂

zack0r

3.728 Beiträge seit 2005
vor 14 Jahren
Singleton

@zack0r:

Vom SecurityService darf es nur eine Instanz geben, da dieser die Sitzungen der angemeldeten Clients verwaltet. Wenn es plötzlich mehrere Sitzungslisten gäbe, würde das nicht funktionieren, deshalb Singleton. Der von mir implementierte SecurityService ist threadsicher. Eine Doppelsperre ist nicht nötig, da die Instanz nicht von Remoting durch möglicherweise mehrere Anfrage-Threads erzeugt wird, sondern vom Host-Hauptthread. Und zwar bevor irgendwelche Clients überhaupt Verbindungen herstellen können. Deine Sorge ist deshalb unbegründet.

Ich kenne mich mit Remoting nicht aus, ist aber etwa so wie der Vorgänger von WCF oder?

Kann man so sagen. Remoting gibt es seit .NET 1.0. In .NET 2.0 wurde Remoting stark erweitert: Security ohne IIS, IPC-Kanal für schnelle Interprozesskommunikation. Seit .NET 3.0 gibt es WCF. Das ist auf den Ersten Blick zwar übermächtig toll, auf den zweiten Blick aber aufwändig und für viele Aufgaben einfach zu komplex. Wenn ich nicht gerade übers Internet mit einem Java-Server über SOAP reden muss, bringt WCF nicht wirklich einen Vorteil. Für Einsteiger ist WCF definitiv wesentlich schwerer zu erlernen, als Remoting.

Ich sehe nur das du anscheinend auch lokal auf dem Server darüber kommunizierst (mit dem Security und dem Locking Service) was hat das für Vorteile, bis auf, dass man die Services auf verschiedene Maschinen aufteilen könnte?
Wieso nicht einfach eingebundene Assemblys?

Auf den Sicherheitsdienst und den Sperrendienst wird von Clients übers Netzwerk zugegeriffen. Beide Dienste sind als Singleton implementiert, da sie zentrale Listen verwalten (Sitzungsliste bzw. Sperrenliste). Mein Beispiel ist komponentenorientiert aufgebaut. Ich brate keine Extrawurst beim Zugriff, nur weil die Assembly zufällig lokal vorhanden ist. Der Sicherheitsdienst ist deshalb auch von ContextBoundObject statt von MarshalByRefObject abgeleitet. Das beduetet, dass Du gar nicht lokal aufrufen kannst. ContextBoundObjects werden immer über einen Proxy angesprochen. Das macht es sehr schwer, den Sicherheitsdienst zu manipulieren. Du kannst z.B. nicht mittels Reflection auf irgendwelche private-Member zugreifen oder ähnliches.