Hallo,
ich muß sehr große Dateien (>1 GB) nach beliebigen Kriterien sortieren, daher fallen interne Sortierverfahren wie Quicksort/Heapsort gleich weg.
Bei externen Sortierverfahren gibt es - wenn man der gängigen Literatur glauben darf - 2 Ansätze: Den externen Mergesort und den externen Distributionsort, die sich fast invers zueinander verhalten.
Zu beiden Verfahren gibt es dann wieder unterschiedliche Optimierungsstrategien, wie etwa [Knuth]
MergeSort -> Replacement Selection
DistSort -> Random Cycling
Meine Frage ist nun, ob ein Ansatz prinzipielle Vorteile gegenüber dem anderen bringt. Oder ist es egal, welchen von beiden ich implementiere? Einen direkten Vergleich beider konnte ich leider nicht finden.
Um aussagekräftige Zeiten zu erhalten, habe ich ein paar Testmethoden geschrieben um eine große Textdatei einzulesen.
Was mir dabei aufgefallen ist und mich leicht rätseln lässt, ist die schnelle Verarbeitung:
Ich habe eine 850MB große Textdatei, welche ich blockweise einlese und dann auf Zeilen aufsplitte. Als Alibifunktion merke ich mir ein bestimmtes Zeichen jeder Zeile, einfach um zu gewährleisten, dass jede Zeile einmal benutzt wurde.
Für diese Datei benötigt ein Durchlauf nun durchschnittlich 2,7 Sekunden. Ich lasse den Test 10 mal durchlaufen und schmeiße die Extremwerte weg um Initialisierungsspitzen zu vermeiden. Auch der Taskmanager mit Gelesene E/A-Bytes an, dass die Datei vollends gelesen wurde.
Einfache Rechnung 850 MB / 2,7 Sekunden = 314 MB/s.
Es handelt sich dabei um einen neuen Quad-Rechner mit SATA-Anschluß, aber sollte dies nicht über der sequentiellen Maximalleistung einer modernen (Nicht-SSD) Festplatte liegen?
Nicht dass mich das schnelle Lesen nicht freut, ich würde nur gerne verstehen, wie er zustande kommt 🙂
Was habe ich schon versucht?
Ich lese die Datei mit einem StreamReader ein, und speichere jede Zeile in einer ArrayListe
Hier könnte es schneller sein, die Datei in größeren Blöcken (Stichwort: FileStream.Read) einzulesen, statt immer zeilenweise vorzugehen .
Pro gelesenem Block kannst du dann mehrere Zeilen so wie von den anderen beschrieben extrahieren und gehashed speichern.
In dem Zusammenhang wäre es auch möglich die Datei asynchron einzulesen. Dies führt laut Literatur zu einer besseren Auslastung der Festplatte, aber ob dies für deinen Fall wirklich Sinn macht, ist fraglich.
(Vom RAM her sollte es kein Problem sein, da ich 3,5 Gigabyte zur Verfügung habe)
Das ist leider nicht pauschal auslegbar. In einem 32 Bit-OS stehen dir nur 2GB für dein Anwendungsprogramm zur Verfügung. Der virtuelle Adressraum beträgt zwar 4GB, aber 2GB davon sind quasi für das Betriebssystem reserviert. Allerdings gibt es noch einen Schalter um 3GB zu nutzen (Stichwörter: PAE, /3GB)
Erst mit einem 64 Bit-OS ist diese Beschränkung sehr weit nach oben verlegt worden.
Es gab vor längere Zeit (glaube Ende letzten Jahres) einen Bericht über einen managed Kernel von den nächsten Windows-Versionen. Die Entwickler/-Chefs von Windows weigern sich aber grundlegend von ihren C-Anwedungen und teils C++ abzutreten.
Meinst du Singularity?
Nur schade, dass nicht reines C#, sondern extra Cspec dafür eingeführt werden mußte. Daraus könnte man nun ableiten, dass C# an sich nicht ohne Erweiterung _für höheres _ausreicht.
Wie etwa hier in diesem Forumspost behauptet.
Besonders die Größe des .NET Frameworks wird häufig von den Kritikern ins Spiel gebracht.
Nur gut, dass Microsoft mit dem client profile dagegenhält 🙂
(Falls dieser Thread eher im Smalltalk-Bereich aufgehoben ist, bitte gleich verschieben)
Frage zum integrierten Unit-Testing in VS2008/NUnit:
Sind damit auch Performance-Tests möglich?
Also Möglichkeiten zur genauen Zeitmessung von Funktionen oder eine Auslastungskurve des Speichers.
Bislang habe ich in der Doku zu NUnit nur Möglichkeiten für die Verifikation von Methoden gefunden. Dies ist zwar der eigentliche Sinn von Unit-Tests, aber trotzdem wären Performancetests auch beizeiten wichtig und nützlich.
Wenn dies mit NUnit nicht möglich ist, gibt es vielleicht Alternativen dazu, die diese Art von Tests beherrschen?
Hallo,
ich schreibe abwechselnd an das Ende von Textdateien. Dazu positioniere ich den Schreibstrom per Seek-Methode am Ende der Datei.
StreamWriter fileOut = new StreamWriter(new FileStream(_name,
_mode, _access, _share), _encoding);
fileOut.BaseStream.Seek(0,SeekOrigin.End);
fileOut.WriteLine("test");
Je nach Dateikodierung öffne ich die Textdateien entweder per Standard- (Codepage: 1252) oder Unicode-Kodierung (Codepage: 1200).
Bei normaler Kodierung steht der Teststring in einer neuen Zeile am Ende der Textdatei.Bei Unicode-Kodierung steht vor dem String noch ein nicht druckbares Zeichen.
Dieses Verhalten ist nicht gewünscht und ich möchte - wenn es möglich ist - vermeiden, eine Sonderbehandlung für Unicode einzuführen.
Gibt es vielleicht eine gleichbleibende und darum empfehlenswertere Methode des StreamWriters um an das Dateiende zu schreiben?
Ich habe gelesen, dass es bei Unicode kein EOF-Tag gibt, wie bei Textdateien üblich. Hat dies damit etwas zu tun?
Also ich freue mich auf den bestimmt gut dokumentierten Programmcode, auch wenn mir persönlich eine Umsetzung mit WCF sympathischer gewesen wäre.
Da du Lager- und Artikelverwaltung als Beispiele angibst, wirst du bestimmt auch eine datenbankneutrale Datenhaltung zur Verfügung stellen.
Welche Methodik/Vorgehensweise wirst du dafür benutzen, weil bei dem Thema scheiden sich ja die Geister? 🙂
Hallo,
ich benutze die RijndaelManaged-Klasse um einen String zu verschlüsseln.
Nun habe ich die verschiedenen KeySizes der Instanz abgefragt und möglich sind dabei die Werte 128, 196 und 256
Nur komischerweise werden bei der Chiffrierung immer die gleichen verschlüsselten Texte erstellt, egal wie der Wert der KeySize ist.
algorithm.KeySize = keySize;
algorithm.Key = Key;
algorithm.IV = IV;
Die KeySize setze ich vor dem setzen des eigentlichen Schlüssels weil sonst unnötigerweise zweimal ein interner Schlüssel generiert werden würde.
Wenn du eine wirklich große Applikation hast und das Problem der verschiedenen Dienste wirklich gut lösen möchtest, würde ich das Lesen von
Dissecting a CSharp Application empfehlen.
In Kapitel 3 geht es um eine Property-Architektur.
In Verbindung mit Kapitel 5 kannst du einmal einen Service für zentrale Programmeinstellungen oder auch einen Service für immer wiederkehrende Dateioperationen erstellen.
Irgendwo im Programm hast du dann beispielsweise folgenden Code:
PropertyService prop = (PropertyService) ServiceManager.Services.GetService(typeof(PropertyService));
String con = prop.GetProperty("ApplicationConnectionString");
Naja, für richtig kurios halte ich den Fehler nicht.
Schließlich wird bei **OneWay **-Kommunikation nur in eine Richtung kommuniziert, da sollte die Verbindung nicht ewig offen gehalten werden.
Hier wird näher darauf eingegangen.
Darum würde wohl auch eine Erhöhung der Timeout-Zeit nicht helfen, vielleicht braucht die Operation auf einem alten Rechner 8x so lange 🙂
Wie wäre es, wenn dein Programm eine Timeout-Exception richtiggehend erwartet und dies dann halt nur zu einer Neuinstanzierung des Proxys führt.
Laut MSDN soll bei jedem WCF-Aufruf gewisse Exceptions immer erwartet werden, siehe hier
Ok, das Problem konnte ich lösen.
Aber nun stellt sich eine andere Frage bezüglich Errorhandling bei WCF:
Jeder Zugriff auf den Proxyclient muß von ziemlich vielen catch-Klauseln begleitet werden, was bei vielen Funktionen ja furchtbar unübersichtlich wird.
try
{
BusinessLogic.UserMgr.LogOn(username);
}
catch (TimeoutException timeProblem)
{
Console.WriteLine("The service operation timed out. " + timeProblem.Message);
BusinessLogic.AbortChannels();
}
catch (FaultException<NoUserAuthenticationFault> fault)
{
MessageBox.Show("Logon with user " + username + " failed");
BusinessLogic.AbortChannels();
}
catch (FaultException ex)
{
MessageBox.Show("SOAP-Error:" + ex.GetType().Name + " - " + ex.Message);
BusinessLogic.AbortChannels();
}
catch (CommunicationException commProblem)
{
MessageBox.Show("Communication problem: " + commProblem.Message + commProblem.StackTrace);
BusinessLogic.AbortChannels();
}
Wie geht ihr da vor? Für jede Clientfunktion eine Facade?
IDE: Visual Studio 2008 Beta 2
Hallo,
beim Ausführen von svcutil.exe, auch komplett ohne Parameter, kommt folgender Fehler:
C:\>svcutil
Unbehandelte Ausnahme: System.IO.FileLoadException: Die Datei oder Assembly svcu
til, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a oder eine
Abhängigkeit davon wurde nicht gefunden. Fehler bei der Strong Name-Überprüfung
. (Ausnahme von HRESULT: 0x8013141A)
Dateiname: svcutil, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d
50a3a ---> System.Security.SecurityException: Fehler bei der Strong Name-Überprü
fung. (Ausnahme von HRESULT: 0x8013141A)
Die Zone der Assembly, bei der ein Fehler aufgetreten ist:
MyComputer
Liegt dies am Betastatus, an mir oder an MS? 🙂
Bei einer WCF-Applikation, die per wsHTTPBinding kommuniziert, werden Exceptions und Faults nicht vom Server an den Client zurückgeliefert.
Stattdessen wird beim manuellen Werfen einer **FaultException **gemeldet, dass keine Fehlerbehandlungsroutine dafür vorhanden ist.
Ich benutze quasi eine 08/15-Konfigurationsdatei.
Laut einem Artikel könnte es daran liegen, dass beim wsHTTPBinding standardmäßig **OneWay **gesetzt ist. Also einseitige Kommunikation.
Aber muß ich nun zur sinnvollen Fehlerbehandlung gleich komplett auf Duplex oder Async-Kommunikation umsteigen?
Verwirrend finde ich auch, dass in den ganzen WCF-Beispielen die man so findet, davon eigentlich nie die Rede ist.
Ich versuche die Frage mal allgemeiner zu formulieren:
Ich habe auf dem Server nun eine temporäre Zertifikatsstelle und mit dieser ein gültiges Serverzertifikat erstellt. Dieses befindet sich auch im Zertifikatsspeicher des Servers.
Welche prinzipiellen Schritte sind nun auf dem Client auszuführen?
Für dein Szenario, wo verschiedene Anforderungen bestehen, ist WCF recht gut geeignet.
Größter Nachteil sind die Betriebssystem-Vorraussetzungen, solange dies kein Problem darstellt, spricht nichts gegen WCF.
Also alles ab XP, Server 2003 und natürlich Vista.
Bei WCF gibt es Sessions, die einen Client während einer längeren Sitzung identifizieren können.
Hallo,
ich möchte Zertifikate zur Authentifizierung einsetzen um Benutzer bei einer Serverapplikation zu erkennen. Diese Anwendung soll über das Internet funktionieren, wodurch leider die praktische Windows-Authentifizierung entfällt.
Ausgegangen bin ich von diesem Tutorial, welches wohl nur die Minimalanforderungen an eine zertifikatsbasierte Anwendung erfüllt, aber für den Einstieg sollte das genügen: UserNamePassword Validator
Um schon mal im vorhinein alle Probleme mit einer offiziellen Zertifizierungsstelle zu vermeiden wird beim Server der Wert
<authentication certificateValidationMode="None" />
gesetzt.
Mein erstes Verständnisproblem tritt nun schon auf, bei der Frage was beim Client zu tun ist.
Für den Zertifikatsteil wird im MSDN-Artikel folgendes Batch-Skript gelistet:
echo ************
echo Server cert setup starting
echo %SERVER_NAME%
echo ************
echo making server cert
echo ************
makecert.exe -sr LocalMachine -ss MY -a sha1 -n CN=%SERVER_NAME% -sky exchange -pe
certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r CurrentUser -s TrustedPeople
Der erste Befehl erzeugt augenscheinlich das Zertifikat selber und legt es in diesen ominösen PC-Speicher für Zertifikate.
Der zweite Befehl ist dagegen Voodoo. Laut Beschreibung
The following lines in the Setup.bat batch file copy the server certificate into the client trusted people store.
soll dies das Zertifikat am Client installieren. Aber der Client ist ein anderer PC, aber die Adresse des Clients wird nirgends angegeben.
Wenn ich den Server starte und mir mit dem svcutil.exe die WCF-Clientsettings generieren lasse, wird sogar ein Eintrag für das erstellte Zertifikat hinzugefügt:
<identity>
<certificate encodedValue="AwAAAAEAA etc etc" />
</identity>
Trotzdem kommt bei der ersten Verbindung Client->Server der in diesem Forum schon öfters erwähnte Fehler:
Fehler beim Aufbau der X509-Zertifikatkette (CN=pc_wo_server_laueft). Die Vertrauenskette des verwendeten Zertifikats kann nicht verifiziert werden. Ersetzen Sie das Zertifikat, oder ändern Sie den Zertifikatprüfungsmodus. Eine Zertifikatskette wurde zwar verarbeitet, endete jedoch mit einem Stammzertifikat, das beim Vertrauensanbieter nicht als vertrauenswürdig gilt.
Muß ich zuvor das Zertifikat beim Server exportieren und per Zertifikats-SnapIn irgendwie installieren? Davon steht jedenfalls im MSDN-Artikel nichts...
Ich habe genau das selbe Problem, obwohl ich wsHttpBinding statt netTCPBinding verwende.
Sogar wenn ich für die Prüffunktion
myServiceHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = UserNamePasswordValidator.None;
verwende, kommt es zu dieser Fehlermeldung.
In der eingebetteten Exception steht dann aber sogar der genaue Grund:
The service certificate is not provided. Specify a service certificate in ServiceCredentials.
Source: CreateServerX509TokenProvider()
Nur ich habe meines Wissens nirgends etwas eingestellt, was ein X.509-Zertifikat nötig machen würde.
Muß man dies vielleicht irgendwo explizit ausschalten oder kann man die ClientCredentials bei MessageClientCredentialType=UserName nur per Zertifikat mitgeben?
Sehr, sehr komplex die Authentifizierungsmöglichkeiten mit WCF.
Für den Fall, dass der derselbe Benutzer auf den beiden PCs unterschiedliche Passwörter hat. Dann lasse ich das Passwort am Client einlesen und würde es gerne zur automatischen Windowszertifizierung verwenden.
Nur wie setze ich die Windows-Credentials. Die Eigenschaft ClientCredentials in der Proxy-Klasse bietet ja jede Menge Möglichkeiten zum Setzen von Namen/Passwörtern.
Die offensichtlichste Variante hat zumindest bei mir nicht funktioniert, der Client wurde trotzdem abgewiesen.
Genauergesagt funktioniert es für den angemeldeten Benutzer, wenn dieser ein anderes Passwort besitzt. Möchte man sich jedoch mit einem Benutzer anmelden, der auf dem Klient-PC nicht läuft, dann ist dies nicht möglich.
proxyToServer.ClientCredentials.Windows.ClientCredential.UserName = dlg.UserName;
proxyToServer.ClientCredentials.Windows.ClientCredential.Password = dlg.Password ;
Ist dies dann überhaupt prinzipiell richtig oder muß ich dazu unter
Bindings -> MessageClientCredentialType von _Windows _auf UserName wechseln?
Achso.
In meiner bisherigen Architektur war der Zugriffsclient statisch im ganzen Clientprogramm vorhanden und daher habe ich immer versucht mit der bestehenden Instanz weiterzuarbeiten.
Funktioniert jedenfalls. Danke.
Hallo,
in einem WCF-Szenario kommuniziere ich ganz mit WCF. In der Applikation gibt es nun eine Login-Maske, wobei auch ein späteres Ausloggen möglich ist.
Ich schließe beim Logout den Kanal mit der Close()-Methode wonach sich der Kanal im Zustand Closed befindet.
Nur wie kann ich den Kanal dann wieder öffnen? Bei einem nachträglichen _Open() _kommt die Meldung, dass der Kanal verworfen wurde:
"Auf das verworfene Objekt kann nicht zugegriffen werden.\r\nObjektname: System.ServiceModel.ChannelFactory`1[IUserManager]."
Momentan muß ich die Anwendung immer komplett neu starten, was sicherlich nicht Sinn der Sache sein kann.
Dies ist ganz ähnlich, wenn ein Fehler im Kanal auftritt, wie etwa ein Kommunikationsfehler. Dann geht der Kanal ist den Zustand Faulted. Mit der Abort()-Methode bekomme ich ihn dann auch in den Closed-Zustand, aber halt nicht mehr wieder auf.
Ja der Wahnsinn 😁
Ich hab jetzt ganz nach deinem Tipp einen BindingContext ohne jegliche Sicherheit hinzugefügt und schon dauert die Anmeldephase vom Client keine ganze Sekunde mehr.
Vielen herzlichen Dank
Ich habe mal die Diagnosemethoden von WCF konfiguriert, aber ehrlichgesagt werde ich von den Darstellungen des _Service Trace Viewers _regelrecht erschlagen.
Ist poste trotzdem mal die Stelle, die wohl 12s benötigt, auch wenn ich nicht raussehe für was.
Der Sprung ist zu sehen von 15:17 + 10s auf 15:17 + 22 Sekunden.
Ganz normal per ServiceHost, wobei die Open()-Methode ganz simpel in einer Klasse gekapselt ist.
Der Aufruf erfolgt von einem Formular heraus.
Habe es gerade auch in einem Konsolenprojekt gehostet, aber zeigt keinen Unterschied in der Verbindungsdauer.
Hallo,
ich nutze WCF mit dem wsHttpBinding.
Der erste Verbindungsaufbau zwischen zwei PCs die im gleichen LAN sind, dauert nun etwa ab 10 Sekunden, im Durchschnitt etwa 14 Sekunden.
Pings zwischen beiden Rechern haben eine Laufzeit von <1 Millisekunden.
Nach dem ersten Aufbau der Verbindung wird jeder Zugriff dagegen zeitnah ausgeführt, was auch wunderbar ist.
Nur wieso dauert der Verbindungsaufbau derart lange, werden zu Beginn so viele Steuer- und Metadaten ausgetauscht?
Ich habe probeweise mal das netTCP-Binding zur Kommunikation bemüht, dort war aber das gleiche Verhalten zu beobachten. Die Verbindung brauchte immer noch mehr als 8 Sekunden zum Aufbau.
Im realen Einsatz ist diese Zeitspanne verkraftbar, aber zum Testen ist es wirklich sehr hindernd, wenn man ständig so lange warten muß.
Gibt es darum vielleicht eine Möglichkeit zumindest im Debugmodus eine schnellere Verbindung hinzubekommen?
Als Behavior habe ich noch die Parameter
httpGetEnable="True"
includeExceptionDetailinFaults="True"
für das Binding eingestellt, falls hier eine Optimierung möglich ist.
Ein Problem ergibt sich bei mir allerdings, wenn ich mehrere Dienste verwende.
Beim Client erzeuge ich mir mit folgenden Anweisungen in einer Batch-Datei die aktuellen Metadaten.
svcutil.exe /config:App.config /mergeConfig http://server:12345/Service1?wsdl
svcutil.exe /config:App.config /mergeConfig http://server:12345/Service2?wsdl
Bei einem Dienst (Service1) wurde die App.config immer richtig erweitert bei Benutzung des Merge-Flags. Nun werden alte <serviceModel>-Einträge anscheinend ignoriert und jedesmal neue Einträge hinzugefügt.
Ist zwar nicht weltbewegend, wollte ich deswegen hier nur erwähnt haben.
Habt ihr besondere Strategien entwickelt um die Client-Konfigurationen zu benutzen. Eventuell nicht in die allgemeine App.config mitaufnehmen sondern extra lagern?
Interessanter Ansatz, der zudem noch funktioniert 🙂
Vielen Dank!
Hallo,
ich lasse einen Dienst mit WCF (Framework 3.5) hosten.
myHost = new ServiceHost(typeof(UserService);
myHost.Open();
In der App.config stehen die nötigen Hosting-Details.
Nun möchte ich einen zweiten Service hosten lassen, welcher fachlich nichts mit dem anderen zu tun hat und darum nicht in eine gemeinsame Schnittstelle gepresst werden soll, z.B. ganz abstrus einen AutobahnService.
Wie ist hierbei vorzugehen?
Ich gehe mal davon aus, dass in der Konfigurationsdatei mehrere _<service></service> _Einträge problemlos funktionieren. Aber wie gehe ich programmtechnisch vor, weil doch die Klasse ServiceHost im Konstruktor nur einen Typen erwartet.
In der HQL-Syntax beginnen Ausdrücke oft gleich mit der FROM-Klausel: Beschreibung
Oh, das ist gut zu wissen.
Ich verwende, wie wohl viele hier, einen Werbeblocker. Daher hatte ich gar keine Kenntnis von der Existenz eines Banners.
In meinem Fall Adblock Plus des Firefox, bei dem meines Wissens dadurch kein "Banner view" beim Aufruf einer Seite gezählt wird.
Als Konsequenz werde ich mycsharp.de auf die Whitelist des Blockers setzen 🙂
Ohne dieses Forum wäre die .NET-Landschaft im deutschsprachigen Raum total verwaist.
Danke speziell an denjenigen, der die Traffickosten zu bezahlen hat 👍
Original von Rainbird
Wenn ich z.B. nur Kopfdaten eines bestimmten Interpreten haben will, lasse ich mir doch kein Objekt zurückgeben, welches den Interpreten und all seine Alben enthält.
So wie ich den Ansatz verstanden habe, sollte man es tatsächlich so machen. Ist zwar auf Anhieb ein wenig merkwürdig, aber im Laufe eines Geschäftsprozesses kommt es desöfteren vor dass im Laufe der Abarbeitung einzelne Informationen sequentiell geholt werden müssen.
Und nun anstatt mehrere Aufrufe, die nur auf die jeweilige Anfrage passen, wird gleich ein Gesamtpaket geschnürrt.
Die Details dieses Gesamtpakets obliegen ja dem Entwickler und können im einfachen Falle auch durchaus von schlanker Natur sein.
Original von Rainbird
Es kommt also auf die Anwendungsfälle der Anwendung an, ob zusammengefasste DTOs sinvoll sind, oder nicht. Ich würde das deshalb nicht pauschal festlegen, sondern von Fall zu Fall abwägen.
Eben 👍
Aber eine Mischung aus DTOs und Datenklassen, welche an den Client geliefert werden, würde mir irgendwie missfallen.
Original von Rainbird
Außerdem würde ich niemals alle Eigenschaften der einzelnen Hibernate-Objekte im "Sammel-DTO" nachmals definieren. Wäre nicht auch folgendes denkbar?Alles typsicher, aber trotzdem die Original-Typen beibehalten.
Dies würde ich mit Der Client muß nur die DTO-Klassen kennen was zum Vorteil hat, dass bei einer DB-Änderung die DTO-Klassen nicht zwangsweise mitgeändert werden müssen. versuchen zu rechtfertigen.
Wenn die DTO-klassen dagegen direkt mit den Datenklassen assoziiert sind, wäre dies nicht mehr der Fall. Aber dieser Punkt ist wirklich streitbar.
Die Vorteile von ES kann man zur genüge in der MSDN nachlesen.
Aber falls man sich nun tatsächlich für diese Technologie entscheidet, ist der zu erwartende Aufwand schwer einschätzbar.
Daher möchte ich fragen, wer wirklich schon handfeste Projekte mit ES/COM+ durchgeführt hat und welche Schwierigkeiten dabei aufgetreten sind.
Sind die immer wieder genannten Eigenschaften wie distributed transactions, object pooling, queued components oder loosely coupled events wirklich alle durchweg sinnvoll oder manche nur schnickes Beiwerk?
Wenn man die Forumssuche bemüht, könnte man fast geneigt sein zu glauben, dass Praxiserfahrung rar ist weil auch ohne ES Großprojekte realisierbar sind.
Zugegeben, die Fragestellung ist schwammig, aber als ES-Interessierter hat man es nicht leicht 😉
Original von Rainbird
Könntest Du aber nicht das User-Objekt (welches von der Hibernate-Engine befüllt wird) direkt als DTO verwenden? Oder erbt die Klasse user von irgendwelchen Hibernate-Basisklassen (Ich kenne mich mit Hibernate nicht so gut aus)?
Ursprünglich wurde das User-Objekt bis zur Fassade weitergereicht, die damit gearbeitet hat.
Aber in dem von dir aufgebrachten DTO-Pattern wird aus Performanzgründen empfohlen, seperate DTOs für Geschäftsprozesse anzulegen. Siehe hier.
Die Hibernate-Klassen entsprechen im Schaubild den Klassen _Album _und Artist.
Um nun nicht zuviele (kostenintensive) Methodenaufrufe zu vermeiden, baut ein Assembler fette Klassen zusammen, die eher zuviel denn zu wenig Informationen aus der DB enthalten.
Auch können sich so die Strukturen der DB-Tabellen ändern, was zu einer Änderung der Hibernate-bezogenen Klassen führt.
Die DTOs müssen dagegen nur angepasst werden, wenn sich die DB-Änderung auf die Anwendung durchschlägt.
Und da die Präsentationsschicht nur DTOs und keine keine grundlegenden Klassen kennt, ist eine Neuübersetzung der Clientanwendung nicht zwingend nötwendig.
Nachteil ist halt der Mehraufwand für die DTOs sowie die Assemblertätigkeit. Aber was tut man nicht alles für gute Architektur 🙂
Die im Text angesprochene Remote Facade wird durch die Klasse UserManager repräsentiert.
So, erstmal vielen Dank Rainbird für deine unschätzbar guten Erklärungen.
Ich habe nun diese Vorgehensweise mit serialisierbaren DTOs anhand des Beispielprojekts implementiert und finde die Schichtentrennung nun annehmbar, aber natürlich immer noch ausbaufähig.
Konkret wird bei einer Klientenanfrage nun erstmal durch eine Assembler-Klasse ein UserDTO mit sendenswerten Daten gefüllt. Auch innerhalb der Business-Schicht wird nur noch mit der DTO-Repräsentation gearbeitet, nicht mehr direkt mit dem NHibernate-Objekt.
Die UserDTO-Klasse sieht der User-Klasse von NHibernate zwar ziemlich ähnlich und bedeuted einen nicht unbeutenden Mehraufwand, aber so werden nur genau die Daten übers Netz geschickt, die wirklich auch gebraucht werden.
Der Client muß nur die DTO-Klassen kennen was zum Vorteil hat, dass bei einer DB-Änderung die DTO-Klassen nicht zwangsweise mitgeändert werden müssen.
Desto mehr Daten ich von haus aus in die DTO-Klassen packe, desto weniger Zugriffe auf die UserManager-Fassade sind nötig. Hier kann ich als Programmierer immer selber abwägen, ob mir nun ein schlankes Datenpaket oder weniger Netzwerkzugriffe wichtig sind.
Original von Rainbird
nHibernate in Ehren, aber hast Du Dir schon mal typisierte DataSets und ADO.NET angesehen?
Informationen zu typisierten DataSets habe ich mir im Vergleich zu anderen Datenhaltungstechnologien durchgelesen und halte das ebenso für einen guten Ansatz. Die Nachteile bei diesem Ansatz sind auch ziemlich auf NHibernate übertragbar
MSDN
* Deployment. The assembly containing the typed DataSet class must be deployed to all tiers that use the business entity.
* Support of Enterprise Services (COM+) callers. If a typed DataSet will be used by COM+ clients, the assembly containing the typed DataSet class must be given a strong name and must be registered on client computers. Typically, the assembly is installed in the global assembly cache. These steps will also be required for custom entity classes, as described later in this document.
* Extensibility issues. If the database schema is modified, the typed DataSet class might need to be regenerated to support the new schema. The regeneration process will not preserve any custom code that was implemented in the typed DataSet class. The assembly containing the typed DataSet class must be redeployed to all client applications.
* Instantiation. You cannot instantiate the type by using the new operator.
* Inheritance. Your typed dataset must inherit from DataSet, which precludes the use of any other base classes.
NHibernate verwende ich aber nicht nur wegen der objektorientierten Kapselung sondern genauso wegen der automatischen Unterstützung von DB-Systemen. Auch HQL (Hibernate Query Language) als Ersatz für natives SQL stellt ein großes Plus dar.
Ich benutze auch die ProtectSection-Funktionen, allerdings nicht mit einem RSAProvider, aber vielleicht existiert bei diesem Punkt kein Unterschied.
<configProtectedData>
<providers>
<add useMachineProtection="true" name="DPAPIProtection" type="System.Configuration.DpapiProtectedConfigurationProvider, System.Configuration, Version=2.0.0.0, Culture=neutral,PublicKeyToken=..." />
</providers>
</configProtectedData>
Wobei der Parameter useMachineProtection="true" angibt, ob die Speicherung benutzer- oder systemspezifisch erfolgen soll.
Stimmt, für mich gehörten die Klassen der NHibernate-Objekte eindeutig zur untersten Datenzugriffsschicht. Die Kapselung zwischen BLL und Klient war deswegen so, um eine möglichste lose Kopplung zu erhalten. Aber auch beim Hashtable-Ansatz muß der Klient die Bezeichnungen der User-Eigenschaften kennen, wodurch dann eigentlich nichts gewonnen ist.
Um mich mit dieser doch recht unterschiedlichen Sichtweise vertraut zu machen, habe ich folgenden interessanten Artikel zu den von dir eingeführten DTOs gefunden.
Er ist zwar eigentlich für J2EE ausgelegt, aber dort hat man ja die gleichen Probleme zu lösen, was das ganze übertragbar macht (außer Entsprechungen für Session oder Entity Beans zu finden ist voll doof g).
Use a Transfer Object to encapsulate the business data. A single method call is used to send and retrieve the Transfer Object. When the client requests the enterprise bean for the business data, the enterprise bean can construct the Transfer Object, populate it with its attribute values, and pass it by value to the client.
Hier wird wie du es beschrieben hast, das DTO an die Präsentationsschicht, sprich dem Klienten, übergeben und zwar als Wert und nicht als Referenz.
Kann ich mir dies in etwa so vorstellen:
In der Business-Schicht, was einem in Java einem EJB entspricht, wird zuerst ein User-Objekt mithilfe der Datenzugriffstechnologie (bei mir ein OR-Mapper) mit Werten aus der DB gefüllt.
Danach wird dieses Objekt geklont, weil ja laut Artikel keine Referenz sondern nur eine wertgleiche Kopie ausgetauscht werden soll.
Für änderungsfähige DTOs wird dann die Updatable Transfer Objects Strategy eingeführt, die recht überzeugend wirkt.
Instead of providing fine-grained set methods for each attribute, which results in network overhead, the BusinessObject can expose a coarse-grained setData() method that accepts a Transfer Object as an argument. The Transfer Object passed to this method holds the updated values from the client.
Um nochmal auf deine konkrete Vorgehensweise zurückzukommen: Wenn ich dich richtig verstehe, erzeugst du mit
// Proxy für den Zugriff auf die Benutzerverwaltung erzeugen
IUserManager manager=ServiceFactory<IUserManager>.GetProxy();
eine (möglicherweise entfernte, also beliebige Interprozesstechnik) Referenz auf die Managerinstanz welche in der BLL läuft. Oder wird erst verzögert beim GetAllUsers() der erste Remotezugriff nötig?
Da ich eigentlich nur in der Arbeit oder im Studium programmiere, ist es nichts mit Musik hören nach eigenem Willen.
Ansonsten eher in die Richtung, dass Texte schon nicht unwichtig sind
Laut: System of a Down, KoRn, Rammstein
Leise: Tocotronic, The Cooper Temple Clause, LostAlone
Alt aber unvergessen: The Doors, David Bowie
Hier ein Link zum entsprechenden MSDN-Artikel:
Bei mehreren Fragezeichen wird (ähnlich einem printf von C) die Reihenfolge der Parameter eingehalten.
Der OLE DB-.NET-Anbieter unterstützt keine benannten Parameter für die Übergabe von Parametern an eine SQL-Anweisung oder gespeicherte Prozedur, die von OleDbCommand aufgerufen wird, wenn CommandType auf Text festgelegt ist. In diesem Fall muss das Fragezeichen (?) als Platzhalter verwendet werden. Beispiel:
SELECT * FROM Customers WHERE CustomerID = ?
Funktioniert es denn mit der Fragezeichen-Variante?
Lass dir den String vor der Ausführung einmal ausgeben, vielleicht ist das String-Objekt ID leer und darum ergibt sich kein sinnvolles SQL-Kommando beim Zusammensetzen.
Die numerische Version der Formel für den Binomialkoeffizienten verwenden
Die Fragen sind mehr als berechtigt, bin eher neu in geschichteten Anwendungen und möchte nun Erfahrungen und best practices sammeln.
Der OptimisticUserManager war so gedacht, dass nur ganz bestimmte Operationen mit dem User-Objekt möglich sind. Im Beispielprojekt waren dies etwa:
public List<String> getUserNames()
public void DeleteAllGirls()
public void addNewUser(String name, String password, String email)
Dadurch braucht also die Präsentationsschicht keinen direkten Zugriff auf das User-Objekt. Für jedes spezielle Bedürfnis ist die Präsentationsschicht auf die Businessschicht angewiesen.
Da ich den Zugriff Präsentationsschicht -> Businessschicht möglichst einfach halten wollte, habe ich nur einfache/primitive Datentypen verwendet. So wie hier im Beispiel Strings oder List<String>.
Die Implementierung des User-Objekts kann damit intern gewechselt werden, ohne dass die Präsentationsschicht davon etwas mitbekommt. Das ist wohl zumindest eine Mindestanforderung an ein 3-stufiges Schichtenmodell.
Aber dein Einwand mit einer komplexeren Anfrage, also etwa alle Userobjekte mit allen zugehörigen Eigenschaften, bereitet mir Kopfzerbrechen 🤔
Hier besteht nun wirklich das Problem, wie dies vernünftig abstrahiert werden kann ohne zuviele unnötige Methodenaufrufe.
Eine Quick&Dirty-Lösung wäre eine so geartete Funktion
void getAllUsers(List<Hashtable>)
die für pro User eine Hashtable füllt und jede Hashtable in einer Liste anfügt. Die Präsentationsschicht könnte dann die gewünschten Werte auslesen, es genügte ihr die Kenntnis der Namen der Eigenschaften.
Aber ob das dem Sinn des Schichtenmodells entspricht vermag ich nicht zu beurteilen.
Wow, was für tolle Erklärungen.
Sowas gehört direkt in die MSDN aufgenommen, dann müßte man sich nicht immer alles selber zusammensuchen.
--Spekulation--
Vielleicht liegt der seltene Einsatz von ES darin begründet, dass Dienste die externe Konfigurationen benötigen, von vornherein unbeliebt sind.
Bei COM+ also konkret die Einstellungen über den Komponentendienst.
Wenn man bei MSMQ die Warteschlangen nicht auch im Programmcode erzeugen könnte, sondern nur durch die Management Console, dann wäre hier die Hemmschwelle wohl größer.
Fazit: Die MMC ist schuld :evil:
--Zurück zum Thema--
WCF klingt schon interessant, ist aber für mich ausgeschlossen, da .NET 2.0 vorgeschrieben ist. Und bis eine stabile Version von VS2008 weiter verbreitet ist, kann noch einige Zeit vergehen.
Ideal fände ich eine Art von Remoting, welches für instabile Netze ausgelegt ist. Vielleicht vollbringt ja WCF dieses Kunststück.
Rein privat und relativ neu:
Vielen Dank für die Korrektur.
Ich habe bislang noch nicht viel mit diesen Technologien gearbeitet, was genau der Grund für mein Interesse am Thema ist.
Das so eine Übersicht in die "Grob bis sehr grob" geht ist mir bewusst.
Aber in den meisten Postings, die ich zum "Verteilte Anwendungen" im Forum finden sich verallgemeinernde Aussagen der Marke "Remoting ist nicht optimal für das Internet geeignet".
Hab das Bild mal durch eine Version mit "Wertung" ausgetauscht
Ich hätte eher an eine Art Checkliste gedacht.
Im Forum stehen schon viele Informationen zu den einzelnen Techniken, aber eben halt sehr verstreut auf viele Diskussionsfäden, die ich ungeprüft übernehmen würde.
Da ich noch nicht fündig geworden bin, habe ich eine derartige Schnellübersicht mal selbstständig angefangen.
Aber sie ist zugegeben sehr diletantisch, weil mir beispielsweise bei MQs konkrete Erfahrungen fehlen. Wie ist bei dieser Technologie etwa die Übertragungsgeschwindigkeit im Vergleich zu Remoting/WebServices?
Aus diesem Grund suche ich eine fertige Liste, auch mit mehr Kategorien zur Einteilung.
Vor jedem verteiltem Softwareprojekt steht man ja erneut vor der Wahl der richtigen Netzwerktechnologie aus dem .NET-Framework.
Daher meine Frage, ob jemanden bereits ein Vergleichsartikel bzw. eine Vergleichstabelle bekannt ist. Also um sich schnell eine Übersicht über die jeweiligen Vor- und Nachteile zu verschaffen.
Konkret beziehe ich mich dabei auf Technologien wie Web Service, .NET Remoting, manuelle TCP-IP-Kommunikation, COM+ oder MessageQueues (hab ich gar eine wichtige Variante vergessen?).
Eigentlich habe ich die Datei schon als Binary im Ressourcendesigner definiert, aber das Problem liegt sicher in der von dir genannten Richtung.
Da ich jedenfalls keine Lösung finden konnte, habe ich die Einbindung mittels Resourcenmanager wieder entfernt und bette die Exe nun direkt ohne Umwege ein. Dann klappt die Deserialisierung wie erwartet ohne zusätzlichen "Overhead"
Ist ja auch nicht so schlimm 🙂