Laden...
Avatar #avatar-2834.jpg
Rainbird myCSharp.de - Experte
Fachinformatiker (AE) Mauer Dabei seit 28.05.2005 3.728 Beiträge
Benutzerbeschreibung
Ich mag Tee lieber als Kaffee.

Forenbeiträge von Rainbird Ingesamt 3.728 Beiträge

03.07.2009 - 06:57 Uhr

Um wie viele Datensätze handelt es sich denn überhaupt? Wenn es nicht tausende von Datensätzen sind, würde ich immer alles komplett abrufen. Eine Inkrementelle Synchronisierung ist wesentlich aufwändiger zu entwickeln und auch viel fehleranfälliger. Du solltest überlegen, ob dieser große Aufwand am Ende einen betriebswirtschaftlich verargumentierbaren Mehrwert bringt (Ich vermute nicht).

Ich wollte eigentlich nur verschiedene Möglichkeiten aufzeigen, was man machen kann.

Das mit der Löschliste versteh ich aber nicht. Der Server versucht nicht zu wissen, welchen Datenbestand jeder einzelne Client gerade hat, um die passenden Delta-Daten liefern zu können. Die Synchronisierung würde immer vom Client aus gehen. Ein Client sendet dazu eine Liste der IDs + Zeitstempel, die er momentan geladen hat und entsprechende Filterkriterien. Der Server prüft die Liste gegen den aktuellen Datenbestand und erzeugt eine Liste mit den Deltadaten. In dieser Delta-Liste hat jeder Eintrag einen Enum-Wert, welcher die Änderung beschreibt (New,Geändert, Gelöscht) und enthält natürlich die aktuellen Daten. Datensätze, die sich nicht verändert haben, tauchen auf der Delta-Liste nicht auf. Der Client kann, sobald der die Delta-Liste vom Server zurückbekommen hat, diese mit seinem lokalen Datenbestand synchronisieren.

Wozu also irgendwelche getrennten Löschlisten pflegen?

Aber wie gesagt, lohnt sich der Aufwand für so eine Delta-Verarbeitung nur, wenn es sehr viele Datensätze sind, die bei einem Komplettabruf vom Server geladen werden müssten.

02.07.2009 - 23:43 Uhr

Die DLL soll ja nicht nur für VB6 sein sondern auch normal für C#.

Das solltest Du nicht machen. Für die COM-Unterstützung immer separate Wrapper schreiben, sonst kaufst Du Dir alle Beschränkungen von COM automatisch in Deine C#-Architektur mit ein.

02.07.2009 - 07:16 Uhr

Weil ich nicht Glaube, dass ich es Schaffe das fenster von z.B. VNC in mein MDI reinzupacken oder?

Warum nicht? Schau: http://www.pinvoke.net/default.aspx/user32/SetParent.html

Du machst ein leeres Formular, in welches Du mittels SetParent das VNC-Hander reinkaperst. Dann passt Du mittels MoveWindow (http://www.pinvoke.net/default.aspx/user32/MoveWindow.html) die Größe des VNC-Fensters an die Größe des Host-MDI-Child-Fensters an. Falls VNC eine fixe Größe hat, baust Du eben Scrolling ein.

Du brauchst nur den Window-Handle des VNC-Fensters. Wenn Du VNC von Deinem Programm aus mit System.Diagnostics.Process startest, bekommst über die MainWindowHandle-Eigenschaft des Process Objekts den gewünschten Handle. Den Handle Deines Windows.Forms.Form-Fensters bekommst Du über this.Handle.

So könnte das aussehen: http://i40.tinypic.com/r9e8n9.jpg

In wenigen Minuten wirst Du so Erfolge sehen!

02.07.2009 - 07:07 Uhr

Die Fehlermeldung deutet aber darauf hin, dass der TCP-Port 21402 bereits durch eine andere Anwendung belegt ist. Du kannst das durch Eingabe von netstat an der Konsole überprüfen. Probier aber mal eine andere Portnummer aus.

Du hast am Server vergessen, die Sicherheit auszuschalten. Bei RegisterChannel musst Du dazu im zweiten Parameter (secure) den Wert false übergeben. Damit würde die Anwendung als nächstes auf die Nase fallen.

02.07.2009 - 01:58 Uhr

Damit ich Dir helfen kann, brauche ich die Remoting-Konfiguration des Servers. Wie sieht die aus?

02.07.2009 - 01:52 Uhr

Es wäre Fatal wenn ein Schüler der sich auf einem Schulcomputer anmeldet den Lehrer jederzeit kicken könnte.

Mag sein. Trotzdem muss der Schüler jederzeit sehen, ob jemand anderes seine Sitzung gerade beobachtet bzw. so zugeschaltet ist, dass das möglich wäre. Wenn der Lehrer nicht möchte, dass die Schüler irgendwas rumfummeln, während er etwas erklärt, sollte einfach die Systeme per Fernzugriff sperren können.

Du hast mir die Frage nach der Motivation aber nicht beantwortet? Lehrer-Steuert-Schüler-PCs-Fern-Software gibt es bereits in verschiedenen Variationen. Warum neu schreiben?

02.07.2009 - 01:44 Uhr

Hallo Frisch12,

Einfach nur in dessen Session einklinken und unter umständen maus und Tastatureingaben zum entfernten rechner senden sodass diese umgesetzt werden.

So einfach ist das aber nicht.

Du könntest sowas als Windows-Dienst implementieren, der auf allen Rechnern installiert wird, die ferngesteuert werden können sollen (Wenn ein Active Directory zur Verfügung steht, kannst Du das Installer-MSI-Paket auch per Gruppenrichtlinie verteilen, dann wird der Dienst automatisch auch auf allen neuen Computern installiert, die zur Domäne hinzugefügt werden).

Über Remoting kannst Du Dich dann auf den Dienst eines bestimmten Rechners verbinden und dort z.B. die Maus übernehmen. Das wiederum wird den Einsatz diverser Win32-API-Aufrufe nötig machen. Infos dazu findest Du hier: http://www.pinvoke.net/

Stichworte für eine weitere Recherche wären: Window-Messages, wndproc, Hooks, SendMessage, LogonUser

Wichtig ist bei einer solchen Anwendung, dass der Benutzer sofort sieht (z.B. über ein NotifyIcon) dass sich jemand in seine Sitzung eingeklinkt hat und sieht was er gerade auf dem Bildschirm hat und seine Maus und Tastatur fernsteuern kann. Der Benutzer muss unbedingt die Möglichkeit haben, die Übernahme seiner Sitzung abzubrechen. Alles andere halte ich für Datenschutzrechtlich nicht verantwortbar. Was gibt es denn für Gründe, eine andere Windows-Sitzung fernzusteuern:*Ich bin Admin und arbeite an einem PC der weit entfernt steht *Ich mache Support und helfe jemand anderes ein Problem zu lösen *Ich arbeite z.B. währnd eines Online-Meetings gemeinsam an einem Dokument *Ich möchte jemanden Bespitzeln

Für all diese Zwecke gibt es bereits fertige Anwendungen (selbst fürs Bespitzeln). Was ist Deine Motivation, noch eine weitere hinzuzufügen, die vermutlich die bestehenden in Leistung und Sicherheit nicht übertreffen wird?

02.07.2009 - 01:29 Uhr

Ich will allerdings direkt eine Verbindung zwischen zwei Clients herstellen, da so auch offline eine Verbindung möglich ist und mir außerdem kein Server zur Verfügung steht.

Das ist ein Widerspruch! Wenn ein Rechner offline ist, hat er keine Netzwerkverbindung und kann dann garnicht übers Netzwerk kommunizieren. Der Begriff Server und Client meint die Rollen der einzelnen Systeme bei der Netzwerkkommunikation. Häufig wird bestimmte Funktionalität auf einem zentralen Computer zur Verfügung gestellt. Dieser Computer wird dann als Server bezeichnet (auch wenn er als Betriebssystem Windows XP Home installiert hat). Es gibt auch verteilte Anwendungen, bei denen alle Kommunikationsteilnehmer weitgehend gleichberechtigt sind. Man spricht dann von Peer-to-Peer, statt von Client-Server.

Warum willst Du überhaupt direkt mit Sockets arbeiten. Das ist in den allermeisten Fällen absoluter Unsinn. Das .NET Framework bietet verschiedene fertige Kommunikations-Implementierungen an, die viel einfacher zu nutzen sind. Das wären:*.NET Remoting *WCF *ASP.NET Webdienste *Microsoft Message Queuing *Enterprise Services

Wahrscheinlich kannst Du Dein Problem mit einer dieser Technologien viel besser lösen, als alles mit Sockets zu Fuß zu entwickeln.

Was willst Du denn genau für eine Anwendung schreiben?
Was soll am Ende dabei herauskommen?

02.07.2009 - 01:07 Uhr

Hallo Frisch12,

Du darfst den Kanal nicht bei jedem Knopfdruck registrieren. Der Kanal wird bei Programmstart oder bei der Ersten Verwendung nur EIN Mal registriert.

Hier findest Du eine funktionierende Remoting-Implementierung, die Dir nützlich sein könnte: Remoting-Helfer

02.07.2009 - 00:49 Uhr

Hab auch schon die dlls die benötigt werden mit ins VB6-Projektverzeichniss gepackt.
Kann es daran liegen, dass das Objekt selber noch andere Objekte intern braucht und auch noch eine andere DLL?

Die abhängigen DLLs müssen entweder im GAC registriert sein, oder im selben Verzeichnis liegen wie die registrerte COM-Interop-aktivierte Assembly. Wenn die Assembly über COM-Interop in den Prozess der VB6-Anwendung geladen wird, sucht die CLR im Verzeichnis der Assembly nach Abhängigkeiten (und natürlich im GAC).

Probier mal was anderes. Erstelle ein neues Visual Studio Projekt und baue eine HalloWelt-COM-Wrapper-Assembly. Da kannst Du das Problem leichter isolieren.

Wie sieht Dein Konstruktor aus? COM unterstützt keine parametrisierten Konstruktoren. Die Objekterstellung funktioniert nicht, wenn Deine Klasse keinen Standardkonstruktor hat.

Wird Deine COM-Wrapper-Assembly mit Ihren Klassen und deren Methoden im VB6-Objektkatalog korrekt angezeigt?

Welche Datentypen nehmen die Methoden der OscServer-Klasse als Parameter entgegen, bzw. welche Datentypen geben sie zurück? Sind alle diese Datentypen ComVisible? Können all diese Datentypen automatisch in entsprechende COM-Datentypen umgewandelt werden, oder ist möglicherweise der manuelle Eingriff in den COM-Marshalling-Vorgang nötig (das ist häufig bei Collections und Arrays der Fall)? Solche Datentyp-Probleme können bereits das Erzeugen einer Instanz der Klasse verhindern. Deshalb ganz wichtig: Was zeigt der VB6-Objektkatalog an?

In dem Zusammenhang könnte folgender Beitrag weiterhelfen:
[gelöst] C# zu VBA

01.07.2009 - 18:55 Uhr

// FEHLER: myOscServer ist ein Interface und kann deshalb nicht erzeugt werden
Set objTest = CreateObject("Bespoke.Common.Osc.myOscServer")

// KORREKTUR: Die ProgId zur Erstellung verwenden "Bespoke.Common.Osc.OscServer"
Set objTest=CreateObject("Bespoke.Common.Osc.OscServer")

Wenn es dann immer noch nicht geht, solltest Du die Assembly mal manuell mit dem Konsolentool REGASM.EXE registrieren. Da bekommst Du möglicherweise Fehler angezeigt, die beim registrieren auftreten. So wendest Du das Tool an:


REGASM.EXE DeineAssembly.dll /codebase /tlb:DeineAssembly.tlb /verbose

Weitere Hilfe zu diesem Tool findest Du unter: Assembly Registration-Tool (Regasm.exe)

Noch eine Frage: Ist Deine Assembly signiert? Du solltest Dein Assembly signieren (Stichwort Strong Name). Stelle außerdem sicher, dass Deine Assembly den CAS-Berechtigungssatz Fulltrust erhält.

01.07.2009 - 08:25 Uhr

Da fällt auf, dass Deine Klasse keinen Guid hat. Auch die Klasse sollte ein Guid-Attribut mit eindeutigem Schlüssel bekommen.

Hast Du auch in den Projekteigenschaften das Häkchen bei "Für COM-Interop registrieren" gesetzt?

Ansonsten könnten es noch Namnsprobleme sein. Um dem vorzubeugen, solltest Du sowohl Interface als auch Klasse ein ProgId-Attribut geben, und dort den COM-Namen (ProgID) eintragen.

30.06.2009 - 21:31 Uhr

Versuchs mal mit Dispatch.

Ändere das Attribut ClassInterface Deiner COM-Wrapper-Klasse folgendermaßen ab:

[ClassInterface(ClassInterfaceType.AutoDispatch)]

Versuche dann, das Objekt so zu erzeugen:


Dim objTest as Bespoke_Common_Osc.OscServer 
Set objTest=CreateObject("Bespoke.Common.Osc.OscServer")

30.06.2009 - 20:04 Uhr

Was heißt geht nicht? Kommt eine Ausnahme? Wenn ja, welche?

Dir ist klar, dass das Objekt nur kurz erzeugt und danach sofort wieder zerstört wird?

Was sollte denn beim Erzeugen des COM-Objekts passieren?

30.06.2009 - 06:04 Uhr

Für Objekterzeugung ist in VB6 das Set-Schlüsselwort nötig:


Dim objTest As Bespoke_Common_Osc.OscServer
Set objTest = New OscServer

29.06.2009 - 17:51 Uhr

Die Assembly mydll.dll muss trotzdem per REGASM.EXE registriert werden, damit Dein Moniker funktioniert. Ein Moniker ist nur eine Art Factory, entbindet aber nicht vom Zwang der COM-Registrierung. Lediglich mit SxS kannst Du mit COM-Komponenten ohne Registrierung arbeiten. Da wird das, was normalerweise beim registrieren in die Registry eingetragen wird, in einer XML-manifest-Datei gespeichert (Ähnlich wie manifest-Dateien für .NET-Assemblies).

Leider ignoeriet Access diese SxS-Manifeste einfach und lädt die Komponente nicht, wenn sie nicht, nach alter Väter Sitte, in der Registry eingetragen ist.

29.06.2009 - 08:10 Uhr

Hallo Frisch12,

wenn es sich um Windows-PCs handelt, kannst Du RDP (besser bekannt als Remote Desktop oder Terminal Services) verwenden. Das erfüllt einen hohen Sicherheitsstandard, ist in jedem Windows (ab XP) standardmäßig eingebaut und kann über eine COM-Schnittstelle auch von C# aus anprogrammiert werden. Um den Remote Desktop Viewer zu starten, kloickst Du auf Start -> Ausführen und gibst dann ein "mstsc". Um eine Verbindung herstellen, musst Du nur den DNS-Namen oder die IP-Adresse des Zielrechners kennen. Remote Desktop muss auf dem Zielrechner natürlich aktiviert sein (Systemsteuerung -> System -> Registerkarte Remote).

RDP ist wesentlich schneller als VNC, funktioniert aber nur unter Windows (Es gibt zwar Viewer für Linux, aber der zu steuernde PC muss ein Windows-PC sein).

28.06.2009 - 21:31 Uhr

Wenn Du VS.NET Express benutzt, dann ist da auch kein ReportViewer, der ist erst in der Std.

Das stimmt so nicht ganz. Das ganze Report-System gibt es für Express zum nachinstallieren als Add-On: http://www.microsoft.com/downloads/details.aspx?FamilyID=B67B9445-C206-4FF7-8716-A8129370FA1D&displaylang=de

28.06.2009 - 11:56 Uhr

Hallo zusammen,

ich würde über Abstraktionsebenen nicht ganz so feingranular nachdenken. Ob die Schleife mit for oder foreach ausgeführt wird, finde ich da eher nebensächlich.

Im Falle des ASP.NET-Handlers würde ich überlegen, welche Aufgaben direkt auf der Abstraktionsebene des Handlers liegen, und welche ich an eine andere Abstraktionsebene delegieren muss. Spontan würde ich sagen, dass sowohl die IP-Adressprüfung als auch die Validierung und der Workflow-Start in separate Methoden gehören, welche dann von Handler aufgerufen werden. Ein ASP.NET-Handler ist mit einem Click-Event eines Windows.Forms-Buttons vergleichbar. Der sollte sowieso keinen Logikcode enthalten, sondern diesen nur aufrufen. Und das schon aufgrund der Möglichkeit zur Wiederverwendbarkeit. Wenn ich die Funktion zum Speichern direkt in der Click-Ereignisprozedur schreibe, habe ich ein Problem, wenn ich speichern will, ohne den Button zu drücken. Genauso gut könnte ich auch außerhalb vom ASP.NET-Handler die IP-Adresse überprüfen wollen.

Wenn ich mir das Ganze Szenario so ansehe, finde ich dass ein ASP.NET-Handler kein gutes Beispiel ist, um über Abstraktionsebenen nachzudenken. Die Entscheidungen zur Methodenbildung fallen in diesem Beispiel nicht wegen der Abstraktion, sondern wegen der Trennung von Verantwortlichkeiten. Es gibt drei klar abgrenzte Aufgaben, die nicht voneinander abhängen. Daraus ergeben sich drei Methoden, die für jeweils unterschiedliche Dinge verantwortlich sind.

Das OSI-Referenzmodell beschreibt z.B. wie Netzwerkkommunikation abstrahiert wird. Jede Schicht in diesem Modell abstrahiert Netzwerkkommunikation. Man kann nur Code der grundsätzlich das selbe tut in Abstraktionsebenen aufteilen.

27.06.2009 - 21:39 Uhr

Hallo sKILLeSS,

zuerst einmal solltest Du das "== true" weglassen, das ist unnötig. Da Du von Page_Load sprichst, gehe ich davon aus, dass es sich um eine ASP.NET-Anwendung handelt. Da ist Windows-Authentifizierung nicht standardmäßig aktiv. Das musst Du entsprechend im IIS und in der Web.config einstellen. Außerdem läuft eine ASP.NET-Seite standardmäßig immer unter dem Benutzerknoto des IIS-Anwendungspools der Webseite (z.B. Konto NETZWERKDIENST oder ein speuzielles Domänen-Konto). Damit die ASP.NET-Seite die Identität des Aufrufers vom Browser übernimmt, musst Du in der Web.config Impersonation aktivieren. Für ASP.NET gibt es aber auch noch andere und möglicherweise bessere Alternativen für die Rechteprüfung: Sicherheit für ASP.NET-Webanwendungen

27.06.2009 - 18:46 Uhr

Mit dem .NET Framework 4.0 soll es etwas besser werden im Entity Framework. Da ist dann von Selftracking Entities die Rede. Aber ganz ehrlich: Das ist DataSet-Funktionalität durch Hintertürchen über hässliche draufgepfofte Workarounds, die eigentlich nicht zum Konzept passen. Was ist an DataSets/DataTables angeblich falsch? Warum wollen so viele Leute auf biegen und brechen relationale Daten in herkömmliche Objekte packen?

Wenn die Java-Jungs das machen, versteh ich das, die haben keine komfortable Datenzugriffs-API wie ADO.NET (JDBC erinnert eher an Classic-ADO). Aber im .NET Framework besteht dafür eigentlich keine Notwendigkeit. Genausowenig, wie Java-Portierungen fürs Logging in .NET zu verwenden (wir haben in System.Diagnostics exzellentes Logging), oder in C# mit dem Spring-Framework zu hantieren.

27.06.2009 - 11:42 Uhr

Hallo ErfinderDesRades,

das ist so, weil es einfach normale "dumme" Objekte sind. Schau mal hier: das ist so, weil POCOS (plain old C# objects) einfach grundsätzlich nur wissen, was ihn ihren Feldvariablen steht.

http://yellow-rainbird.de/blogs/rainbird/archive/2009/01/09/habe-sehnsucht-nach-dem-rowstate.aspx

Das Problem ist, dass man den Objekten die fehlende Funktionalität wieder beibringen müsste. Dann wäre man aber implizit wieder beim "fetten" DataSet. Die Frage ist also, ob OR-Mapper überhaupt der richtige Weg sind? Ich meine mittlerweile nein. Warum nicht einfach typisierte DataSets verwenden. Die können das alles schon. Man muss nicht alles neue annehmen, nur weil es neu ist und überall angepriesen wird. Wenn es niemand einsetzt, verschwindet es wieder vom Markt. Ganz von allein.

27.06.2009 - 11:30 Uhr

Hallo Kovu,

Du kannst den Internet Explorer über seine COM-API fernsteuern und dich so durch die Seite bewegen, automatisch Knöpfe drücken, Formularfelder ausfüllen, usw..

Hier steht, wie es geht: http://www.webtropy.com/articles/InternetExplorer.asp?Internet%20explorer

27.06.2009 - 11:22 Uhr

Ich wollte niemanden eine Entscheidung aufdrängen. Wenn jemand Latex gerne verwendet und damit gute Ergebnisse erzielt, ist das natürlich super. Es gibt ja auch Leute, die statt dem bunten Visual Studio nur im Notepad und mit Konsolentools C#-Anwendungen entwickeln. Wenn jemand dafür sinnvolle Argumente hat, warum nicht.

26.06.2009 - 22:04 Uhr

Ich bin nicht sicher, ob man ein DataSet einfach so versenden kann oder ob man dafür einen DataContract und KnownType braucht?

Ein DataSet kann man ohne weiteres versenden, da es Serializable ist.

Das Ding wird noch von dem Visual Studio Development Server gehostet.

Kann der WCF hosten?? Das ist aber wenn, dann nur zu Testzwecken geeignet. Aber dann bezieht er die Konfiguration aus der Web.config buw. App.config. Kannst bitte die Web.config/App.config vom Client und vom Server posten?

Auf welchen Port ist Dein ASP.NET Testserver eingestellt?

26.06.2009 - 17:08 Uhr

Egal wie Du es anstellst, wenn sich die Schnittstelle ändert, musst Du COM-Komponenten neu registrieren. Also immer dann, wenn sich etwas ändert.

Was meinst Du mit ".NET Factory die fertige COM-Objekte erstellt"?

26.06.2009 - 17:06 Uhr

Außer dass es als elitär gesehen wird, wenn man Latex benutzt, hat es keinen Vorteil. Wenn man in Word seine Arbeit zuerst komplett in die Gliederungsansicht schreibt und die Formatierung erst am Ende macht, hat man selbst bei sehr großen Dokumenten keine Probleme. Genauso wie die meisten Leute lieber mit Notepad arbeiten, als den Unix-vi-Editor zu verwenden, ist Latex für Leute, die nicht erst ein Studium zur Bedienung der Software absolvieren wollen, nicht unbedingt erste Wahl.

26.06.2009 - 16:56 Uhr

Hallo byob,

ich sehe keinen Host-Code. Wo und wie hostest Du Deinen WCF-Dienst?

25.06.2009 - 22:35 Uhr

Hallo Nergal,

die Frage nach Rechnungserstellung mir Word kommt dauernd. Schau: PlainTextContentControl über Funktion ändern

25.06.2009 - 22:00 Uhr

... und habe auch kein Visual Studio zur Hand um es einfach zu ändern.

Es geht auch ohne Visual Studio ganz einfach. Die Struktur eines typisierten DataSets ist nichts weiter, als ein XSD-Schema. Du musst also nur die XSD-Datei des gewünschten DataSets anpassen und die dann neue DataSet-Klassen generieren lassen. Dazu verwendest Du das Kommandozeilentool XSD.EXE, welches seit .NET 1.0 im Framework-Verzeichnis verfügbar ist. Hilfe zur genauen Anwendung findest Du hier: http://msdn.microsoft.com/de-de/library/x6c1kb0s.aspx.

Aber unter uns, warum verwendest Du nicht Visual C# 2008 Express? Das ist auch kostenlos und enthält den DataSet-Designer sowie jede Menge anderer Dinge, die in SharpDevelop fehlen. Nicht dass ich die Arbeit der SharpDevelop-Community nicht wertschätzen würde, aber es fehlen einfach noch wichtige Dinge, wie z.B. den DataSet-Designer, die Datenbank-Tools oder den WPF-Designer. Die Entwicklungsumgebung ist das wichtigste Handwerkszeug. Da möchte ich stets das Beste haben, was ich mir leisten kann. http://www.microsoft.com/germany/Express/.

Ich habe aber noch eine andere Frage zu deinem Applikationserver. Ich hoffe das ist ok. Ist mir schon etwas unangenehm, dass ich dich so mit Fragen löcher. 😕 Wie du weißt versuche ich mich gerade zum ersten mal an einer Server/Client Anwendung. Da wird wohl am Ende eine Flasche Wein, Whisky oder Ähnliches fällig 😉 Das ist schon in Ordnung.

Wenn ich mir die Klasse Core ansehe, dann sieht man, dass zuerst die Dienste Locking und Security veröffentlicht werden.

Aber wie läuft das bei den Business Services?

Diese beiden Dienste spielen eine Sonderrolle, da sie praktisch zur Infrastruktur des Applikationsservers gehören. Bei diesen beiden Diensten musste ich sicherstellen, dass sie vor allen anderen erzeugt werden. Deshalb habe ich sie selber erzeugt und via Direct Remoting auch manuell veröffentlicht. Bei den anderen Diensten (den Geschäftsdiensten) überlasse ich das der Remoting-Infrastruktur. Diese lädt auch automatisch die nötigen Assemblies nach (Ein Feature, welches ich übrigens bei WCF schmerzlichst vermisse!). Wie Du sehen kannst, sind im Host-Projekt keine Verweise auf die Geschäftsdienst-Projekte vorhanden. Es genügt, den Assembly-Namen bei der Service-Konfiguration in der App.config einzutragen. Remoting lädt die Assembly und deren Abhängigkeiten automatisch und erzeugt je nach Aktivierungstyp Instanzen. Bei Singleton-Aktivierung wird beim ersten Clientzugriff eine einzelne Instanz erzeugt, welche auch alle weiteren Anfragen verarbeitet und solange lebt, bis ihre Lease abgelaufen ist. Singlton aktivierte Dienste können Variablen zwischen zwei Aufrufen speichern, müssen aber threadsicher sein. Bei SingleCall aktivierten Diensten (das sollte der bevorzugte Standard sein) wird für jeden Client-Anfrage eine eigene Instanz erzeugt, welche aber gleich wieder zerstört wird, sobald die aufgerufene Remote-Methode verarbeitet wurde. Die Dienstinstanz lebt also nur für einen Methodenaufruf, skaliert sehr gut und muss nicht threadsicher sein. Du hast also keine Kontrolle, wann eine Instanz erzeugt und wann diese wieder entsorgt wird. Das macht aber gar nichts. Ich will mich damit auch nicht selber rumärgern. Diese Automatik verleiht Remoting eine außerordentliche Robustheit. Wenn es z.B. in einem Geschäftsdienst zu einer unbehandelten Ausnahme kommt, juckt das die anderen Clients nicht die Bohne, da bei jeder Anfrage eine neue Instanz des Dienstes erzeugt wird. Selbst bei Singleton-Aktivierung läuft das System weiter, da Remoting beim nächsten Clientaufruf einfach eine neue Singleton-Instanz erstellt. Wenn also ein Client mal ungültige Daten sendet, die im Geschäftsdienst zu einer Ausnahme führen, merken die anderen Clients davon nichts (Es sei denn es wurden Remote-Events implementiert; Dann würden die anderen Clients plötzlich nicht mehr benachrichtigt!).

Etwas weiter oben steht: RemotingConfiguration.Configure(configFile, true);

Werden denn automatisch alle Einträge der Konfigurationsdatei unter <service> als WellKnownServiceTypeEntry verfügbar und de TraceEvent Befehl erzeugt dann automatisch Objekte und veröffentlicht sie...klingt ziemlich abenteuerlich?! Zumal ich weiter oben noch den Marshal Befehl für die Veröffentlichung der beiden festen Dienste ausgemacht habe...

Kannst du mir noch mal auf die Sprünge helfen?

Das ist viel einfacher, als Du denkst. RemotingConfiguration.Configure parst und verarbeitet die angegebene Konfigurationsdatei (also die App.config). Das bedutetm dass nach Aufruf von Configure der Server bereits voll Einsatzbereit ist und alle in der App.config eingetragenen Dienste automatisch schon als WellKnowServiceTypes registriert sind. Im einfachsten Fall sieht ein Remoting-Host so aus:


public static void Main()
{
    // Remoting-Dienste laden und starten
    RemotingConfiguration.Configure("SomeHost.exe.config", true); 

    // Server solange laufen lassen, bis jemand Enter drückt
    Console.ReadLine();
}

Das reicht völlig aus. Man kann alles in der App.config festlegen. Das sollte man auch. Es sei denn, man hat gute Gründe eine Ausnahme zu machen, wie ich z.B. beim Locking- und beim SecurityService.
Der TraceEvent-Befehl hat mit Remoting überhaupt nichts zu tun. Die Schleife fragt nur die registrierten Dienste ab und schreibst sie mit TraveEvent in die registrierten Protokolle. Standardmäßig auf die Konsole und optional in der App.config zuschaltbar auch ins Windows-Ereignisprotokoll oder in eine Textdatei. Wenn Du Dir die App.config des Servers mal genauer anschaust, wird Du sicher die ganzen Tracing-Einstellungsmöglichkeiten bemerken. Die Textausgabe, die Du im Konsolenfenster des Servers standardmäßig siehst, ist lediglich die Protokollierung, welcher auf die Konsole umgelenkt wurde. Du könntest das auch stattdessen in eine Datei schreiben lassen, oder ins Eventlog, oder auf die Konsole und in eine Datei oder einfach überall hin. Du könntest auch einen eigenen TraceListener schreiben, welcher die Protokolleinträge sammelt und sie Dir jede Stunde per E-Mail schickt. Den Listener müsstest Du dann nur in der App.config eintragen und die Assembly ins Ausführungsverzeichnis des Servers kopieren. Wir arbeiten hier ja komponentenorientiert! Natürlich auch bei Infrastruktur-Komponenten wie der Protokollierung.

Stell Dir vor Dein Kunde ruft an, und sagt, dass die Lagerbuchungen nicht mehr funktionieren, seit der Server neu gestartet worden ist. Du lässt Dir dann die Logdatei schickten und kannst sofort feststellen, ob der Lagerverwaltungsdienst beim korrekt Serverstart registriert wurde, oder nicht. Falls nicht, muss der Fehler in der App.config liegen. Vielen Leuten ist nicht bewusst, dass das .NET Framework ein exzellentes Tracing-System (Tracing = Logging) mitbringt. Du kannst verschiedene Schweregrade von Meldungen oder Fehlern definieren und darüber den Detailgrad der gewünschten Protokollierung einstellen. Außerdem kannst Du Schalter definieren und damit das Tracing für einzelne Module/Bereiche Deiner Anwendung ein- und ausschalten. Komischerweise kennt fast jeder Log4Net (was ein von Java portiertes Tracing-System ist), aber fast niemand das im Framework enthaltene Tracing im Namensraum System.Diagnostics. Und das obwohl es dem Java-Port in nichts nachsteht. Weitere Infos zum Tracing findest Du hier: http://msdn.microsoft.com/de-de/library/system.diagnostics.tracesource(VS.80).aspx

Wenn ich mir die geniale Einfachheit (ohne auf Leistung und Stabilität verzichten zu müssen) von Remoting so ansehe, ist mir auch klar, warum ich mit WCF immer noch nicht nichtig warm geworden bin. Vom Handling her, macht WCF zwei Schritte zurück. Ich will mich nicht selber um das Laden meiner Dienste kümmern und für jeden Typ eine Host-Instanz erstellen, um dessen Start, Ende und ggf. Abbrüche im Fehlerfall ich mich dann auch selber kümmern muss. Ich möchte auch keinen Client-Code von einem Tool generieren lassen und auf dem Client plötzlich mit anderen Typen arbeiten, als auf dem Server. Das macht mit WCF einfach alles keinen Spaß. Da bleibe ich lieber bei Remoting.

25.06.2009 - 18:41 Uhr

Wenn du weiter COM Klassen hinzufügst, musst du dann das Control neu registrieren?

Was für ein Control? Du meinst mit Sicherheit die COM-Wrapper-Assembly. Ja, die muss neu registriert werden. In meinem konkreten Fall wird das von einem Deployment-Modul, welches auch automatisch die neuesten DLLs von einem File-Server zieht, erledigt.

Ganz wichtig! Alte Version vor dem Ersetzen Deregistrieren! Sonst besteht die Gefahr, dass Rückstände in der Registry verbleiben und zu bizarren Resultaten führen. Auch wenn Du das per CreateObject (Late Binding) ansprichst.

Access unterstützt leider keine SxS (Selbstbeschreibende COM-Komponenten), sonst könnte man auf die lästige Registrierung komplett verzichten.

25.06.2009 - 14:53 Uhr

Die MySQL-DataAdapter-Implementierung kann den char(36) aus der Datenbank scheinbar nicht automatisch in einen Guid umwandeln (Im DataSet ist der Datentyp der noch auf Guid eingestellt). Wenn Du auch im DataSet den Datentyp auf String umstellst, sollte das gehen.

Konsequenter Weise solltest Du dann alle Parameter und Variaben für IDs von Guid in String umändern.

25.06.2009 - 14:45 Uhr

Verwendest du eine Loader wie ich
>
beschrieben habe oder registrierst du jede COM-Assembly und greifst du dann über COM darauf zu?

In meinem konkreten Fall habe ich eine einzige COM-Wrapper-Assembly, die per RAGASM registriert wird. In diese Assembly werden alle COM-sichtbaren Wrapper-Klassen reingeklatscht. In Access reicht dann die Einbindung EINES Verweises und man hat Zugriff auf sämtliche gewrappte .NET-Komponenten.

Wie bereits gesagt: Das muss nicht schön sein, sondern zweckmäßig und so einfach, dass auch der klassische Access-Entwickler gut damit zurecht kommt.

25.06.2009 - 14:39 Uhr

sicher dass du hier WCF meinst und nicht vllt. WPF?

Natürlich! Sorry, ich meine natürlich WPF.

25.06.2009 - 08:28 Uhr

Hallo msycho,

Du kannst COM-Objekte explizit mit System.Runtime.InteropServices.Marshal.ReleaseComObject freigeben.

24.06.2009 - 22:40 Uhr

Hallo bvsn,

GTK# habe ich mir auch schon angesehen. Das hat nicht annähernd die Power von Windows.Forms. Es gibt kein vernünftiges Grid-Steuerelement, was für mich ein K.O.-Kriterium darstellt. DataBinding fehlt komplett. Und und und. Vor zehn Jahren hätte mich GTK# vielleicht noch beeindruckt, aber jetzt fühle ich mich da in die GUI-Steinzeit zurückversetzt. Da müssen die Linux-Buben noch ein paar Briketts drauflegen. Java Swing hat da mittlerweile mehr auf dem Kasten (obwohl da auch noch Welten dazwischen sind).

24.06.2009 - 22:16 Uhr

Du meinst vermutlich so, ja?


>

Wenn ich Dich richtig verstanden habe, willst Du die COM-DLLs der alten OL 4.0 in Deiner C#-Applikation nutzen. Dafür müssen generell überhauptkeine Wrapper geschrieben werden. COM-Verweis einbinden und einfach verwenden (Marshal.ReleaseComObject zum expliziten freigeben von COM-Objekten aber nicht vergessen).

Anders sieht es aus, wenn Du Deine C#-Assemblies in Access VBA verwenden willst. Dann musst Du COM-Wrapper schreiben. Das muss aber nicht unbedingt sehr aufwändig sein. Ich mache das oft so, dass ich nur die Funktionen in meinem COM-Wrapper-Projekt verfügbar mache, die wirklich unbedingt in Access gebraucht werden. Da die COM-Wrapper eh nur temporär sind (bis alles migriert ist), müssen die auch keine superschöne API abgeben. Wenn man Modulweise auf .NET umstellt, reicht es oft pro .NET Formular eine Aufruffunktion zu schreiben. So viel Aufwand ist es deshalb gar nicht. Ein einmaliger großer Brocken ist die Integrationsschicht zwischen Access- und .NET-Formularen. Schließlich müssen die Instanzen der .NET-Fenster auch von Access aus verwaltet werden können, da sie ja als MDIChilds im Access-Hauptfenster ausgeführt werden. Außerdem müssen die .NET-Formulare mit dem Acces-Hauptfenster kommunizieren können (wenn z.B. von einem .NET-Fenster aus ein Dialog geöffnet werden soll, der noch in Access implementiert ist). DIe .NET-Formulare dürfen allerdings das Access.Application-Objekt nicht direkt kennen (da es ja nach der kompletten Migration nicht mehr da sein wird). Deshalb ist eine Integrationsschicht mit abstrakten Schnittstellen erforderlich. Hat man diese Integrationsschicht aber einmal stehen, ist der weitere Aufwand relativ gering. Wenn man gut gekapselt hat, sollten auch Kollegen, die keine COM-Interop-Spezialisten sind, in der Lage sein, die Integrationsschicht zu benutzen.

24.06.2009 - 11:20 Uhr

Du kannst die COM-Verweise doch einfach in Dein Visual Studio Projekt aufnehmen. Visual Studio erstellt dann automatisch Wrapper-Assemblies. Bei Office allgemein wird das ja auch so gemacht. Du kannst mit dem alten COM-Objektmodell von Sage 4.0 unverändert auch in .NET arbeiten. Allerdings nur unter der vorraussetzung, dass es auch wirklich eine COM-DLL gibt und nicht alles mit Access-Klassenmodulen gemacht ist.

24.06.2009 - 09:58 Uhr

Hallo bvsn,

wenn man eine Eigenentwicklung einsetzt, sollte man immer wieder über den Tellerrand schauen und prüfen, wo man steht. Wenn ich erwäge auf eine Standardsystem umzustellen, muss ich es vorher ganz genau kennenlernen. Wer sich nur auf die Marketing-Prospekte der Hersteller und die Zusicherungen der Vertriebsmitarbeiter verlässt, wird hinterher oft unzufrieden sein. Aber zum Glück gibt es ja Demo-Versionen.

Natürlich kann man die .NET Libraries von Sage auch von .NET ansprechen, was nur bedingt von Nutzen ist, da die Oberfläche immer noch komplett in Access ist. Sage hält sich bisher bedeckt, was die Marschrichtung für die zukünftige OL-Oberfgläche angeht. Sowohl Windows.Forms, WCF als auch ASP.NET wurde da schon erwähnt. Da insbesondere WCF noch sehr neu - und unausgereift - ist, werden sie die Entscheidung für eine neue GUI-Technologie auch noch eine Weile hinausschieben. Die Endanwender haben jetzt erstmal Ribbons durch Access 2007 bekommen. Sieht toll aus, bringt aber nicht viel, da sich die Child-Formulare noch genauso anfühlen, wie unter Access 2003.

24.06.2009 - 09:43 Uhr

Hallo bvsn,

das sollte funktionieren. Das SQL-Skript zur Erstellung der Datenbank habe ich vom SQL Management Studio erzeugen lassen, deshalb stehen da so viele Zusatzattribute drin. Vieles davon ist optional und kann weggelassen werden. Du die SQL-Statements (auch die in den Geschäftskomponenten) eben durchsehen und ggf. an den MySQL-Dialekt anpassen (z.B. TOP heißt bei MySql LIMIT etc.). Das stelle ich mir aber nicht schwer vor. So viele Statements sind es ja nicht.

Wo ich mir nicht sicher bin, ist die Unterstützung von System.Transactions (Verteilte Transaktionen) seitens des MySQL-ADO.NET-Providers. Da solltest Du Dich vorher schlau machen.

Die Datenzugriffskomponente arbeitet mit ProviderFactory und kann deshalb sehr einfach auf andere Provider umgestellt werden.

Wenn Windows.Forms unter mono vernünftig funktionieren würde (was es nicht tut), könnte man das Beispielprojekt auch auf mono portieren und dann unter Linux laufen lassen.

24.06.2009 - 02:53 Uhr

Habe die Diskussion über Sage in einen speraten Thread abgetrennt, da es mit der eigentlichen Frage nur indirekt etwas zu hatte.
Hier der Sage-Thread: ){orange} .NET-Integration in Access-ERP-Lösung (Sage?)

24.06.2009 - 01:21 Uhr

Nein, es handelt sich um ein Individualsystem.

ERP-Software von der Stange konnte mich bis jetzt noch nicht überzeugen. Nichts was ich auf der letzten CeBit gesehen habe, hat mich vom Hocker gerissen. Aus Architektonischer Sicht ist das alles Stand Anfang der 90er Jahre. Alle rühren am alten Brei weiter und schmeißen nur neue Gewürze rein. Semiramis ist da noch das innovativste, konnte sich aber nicht durchsetzen und ging Pleite (Ich vermute mal, dass es an der Web-Oberfläche lag; Eine Web-Oberfläche ist für ein ERP-System höchstes für Gelegenheitsbenutzer als Zusatzfeature sinnvoll).

Das angepriesene Sage Evolution hat nicht wirklich eine fertige .NET-Integration. Das einzige was an der Oberfläche .NET ist, ist das Navigationsmenü auf der linken Seite. Und das ist nicht mal sauber gemacht, da Windows-Nachrichten über diverse Tastaturanschläge (z.B. Tab-Taste) nicht richtig an das Control weitergeleitet werden (Das ist bedingt durch Inkompatibilitäten in der Nachrichtenschleife zwischen Access und Windows Forms). Alle anderen Formulare sind Access-Formulare. Die greifen nach wie vor direkt auf die DB zu. Von einem zentralen Applikationsserver wurde zwar schon auf der letzten CeBit gesprochen, aber fertig ist da noch gar nix. Selbst die nuen .NET-Libraries sind bei Sage Evolution nicht aus einem Guss: Da gibt es Datenobjekte (OR-Mapping) für alle Entitäten, aber die Beleg-Engine (welche die eigentliche Buchungslogik implementiert) verwendet ihre eigenen Klassen. Strukturen für Vorgänge und Vorgangspositionen sind also z.B. doppelt vorhanden. Mit zentraler Geschäftslogik ist da nix. Auch die Implementierung der Datenzugriffsschicht verursacht Stirnrunzeln. Alles ist doppelt programmiert 1 x mit ADO.NET und 1 x mit altem ADO (COM). Das ganze wird durch ein API verborgen, damit es sich gleich anfühlt. Grund für dieses Konstrukt ist vermutlich die Kompatibilität zu Partner-Lösungen älterer Versionen, die natürlich nicht mal kurz alles in .NET neu schreiben können. ADO und ADO.NET sind so grundverschieden, da kommt nichts gutes dabei raus, wenn man sich auf gemeinsamen Nenner davon beschränkt.

Ich würde mich auf jeden Fall nicht trauen, ein System in diesem Zustand als Evolution zu bewerben. Das ist alles einfach noch nicht richtig fertig und zu unausgegohren. Wenn die Sage-Partner alle mitmachen und nicht bis zuletzt auf dem alten Access-Gaul reiten, kann daraus mal ein schönes System werden. Bis dahin hat Sage aber noch jede Menge zu tun. Das System ist nicht komponentenorientiert aufgebaut, sondern besteht hauptsächlich aus Access-dateien und Access-AddIns (im Sinne von Access-Dateien, deren Code man in anderen Access-dateien aufrufen kann; Nicht mit VSTO o.ä. zu verwechseln). Wenn man an den Standard-Masken was ändern muss/will, wird diese Änderung direkt im Access-VBA-Code bzw. im Access-Formulardesigner druchgeführt. Kein .NET Plug-In-System mit Windows.Forms oder CAB oder WPF. Meiner Meinung nach ist man zu viele Kompromisse eingegangen. Ich werde die Entwicklung von Sage auf jeden Fall weiter beobachten.

Eine Modulweise/Komponentenweise Umstellung halte ich für sinnvoller, als das von COM durchzogene Gewirr, was Sage jetzt schon als Evolution verkaufen will. Statt einzelne Schichten zu migieren, ganze Module mit Durchstich durch alle Schichten migrieren. In der Übergangszeit ist natürlich einiges an Infrastruktur doppelt vorhanden. Das muss man tolerieren. Damit auch ältere Komponenten mit neuen Komponenten kommunizieren können, müssen eben Wrapper geschrieben werden (kein neues .NET Geschäftsobjekt darf ComVisible sein!!! Nur Wrapper-Typen in dedizierten Wrapper-Assemblies). Diese kann man entsorgen, wenn alle Module/Komponenten umgestellt sind. Diese Strategie funktioniert aber nur dann, wenn auch die GUI einzelner Module auf .NET umgestellt werden kann (ohne die komplette GUI der Anwendung und aller Partner-AddOns neu zu schreiben). Access macht es dem .NET-Entwickler allerdings sehr schwer. Der naheliegende Ansatz, Windows.Forms Fenster per API-Funktion SetParent dem Access-Hauptfenster als MDI-Childs unterzuschieben, scheitert leider kläglich (Toolbars funktionieren nicht, Fenster oder Teile davon werden plötzlich nicht mehr korrekt gezeichnet, Tastenanschläge laden plötzlich in einem Access-Fenster, statt im eingebetteten .NET-Fenster, usw...). Ich habe aber mittlerweile einer Lösung gefunden, die sehr gut funktioniert. Hehe!

23.06.2009 - 23:41 Uhr

Was willst Du denn genau erreichen?

Es hört sich so an, als ob Du .NET-Module in eine Access-Lösung integrieren willst, oder liege ich da falsch? Falls ja, könnte ich Dir bestimmt weiterhelfen. Ich habe hier gerade auch eine große ERP Lösung am laufen, die halb Access 2003 und halb .NET 2.0 ist. Mittlerweile läuft auch die Windows.Forms-Integration stabil (und zwar als MDI-Child und nicht mit ShowDialog). Nur falls Dein Projekt in eine ähnliche Richtung geht.

Wenn ich genau weiß, was Du machen willst, kann ich Dir eher helfen.

23.06.2009 - 22:56 Uhr

@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.

23.06.2009 - 09:18 Uhr

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.

22.06.2009 - 23:23 Uhr

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?).

22.06.2009 - 22:59 Uhr

Hallo bvsn,

der zentrale Dienst auf dem Server kann die Clients per Callback (das funktioniert sowohl mit Remoting, als auch mit WCF) benachrichtigen, wenn sich am Status eines Produktionsauftrags etwas ändert, oder wenn ein neuer Auftrag eingebucht wurde. Die Clients müssen dazu eine Methode bereitstellen, die der Server aufrufen kann, um über Änderungen zu benachrichtigen. Jeder Client registriert sich beim Produktionsauftrags-Dienst und übergibt einen Verweis auf die Rückrufmethode an den Server, damit dieser über Änderungen benachrichtigen kann.

Ich würde die geänderten Daten bei der Benachrichtigung nicht unbedingt gleich übertragen. Wenn ein Client eine bestimmte Benachrichtigung über eine Statusänderung interessiert, kann er sich selber die neue Auftragsliste oder auch die Delta-Daten davon vom Server besorgen. Die Benachrichtigungen bleiben so kurz und knackig. So wird sichergestellt, dass sie möglichst alle Clients fast gleichzeitig erreichen. Der Server muss quasi nur sagen "Es hat sich was geändert". Je nach Komplexität des Systems kann es noch sinnvoll sein, ein paar Infos mitzugeben, was genau passiert ist (z.B. 1 x neu, 2 x geändert, 1 x gelöscht).

Transaktionen nicht vergessen (wegen der Threadsicherheit)!

Das Entity Framework soll mit .NET 4.0 besser n-Tier-Unterstützung bekommen: http://blogs.msdn.com/adonet/archive/2009/05/14/sneak-preview-n-tier-development-with-entity-framework-4-0.aspx
Natürlich ist das noch Zukunftsmusik und nützt in den aktuellen Projekten noch gar nix, aber es ist trotzdem gut zu wissen.

22.06.2009 - 22:34 Uhr

Hallo JCDenton,

ActiveX-Controls sind vom Grundaufbau her sehr komplex. VB6 verbirgt das vor dem Entwickler. Microsoft hat in .NET keine Unterstützung für das Erstellen von ActiveX-Controls eingebaut (in Beta 1 des .NET Frameworks 1.0, gab es noch entsprechende Wrapper-Klassen, die aber bereits in der Beta 2 entfernt wurden; Man hätte zu viele Kompromisse machen müssen).

Selbst wenn man was lauffähiges gebastelt bekommt, gibt es von Microsoft keinen Support und man steht ggf. sehr blöd da, wenn die Lösung nach irgendeinem Sicherheitsupdate plötzlich nicht mehr funktioniert.

Folgender Artikel befasst sich auch mit dem Thema: .NET Control als ActiveX

Zu Deiner Frage nach den Konstruktoren: In der COM-Welt gibt es generell nur parameterlose Konstruktoren. This behavior is by design 😉.

22.06.2009 - 22:19 Uhr

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.

22.06.2009 - 22:07 Uhr

Hallo djatsunrise,

wenn Du Verweise auf eine bestimmte Excel-Version in Deinem Projekt eingebunden hat, funktioniert die Anwendung auch nur mit genau dieser Excel-Version.

Um mehrere Excel-Versionen zu unterstützen, musst Du entweder alles spätgebunden (via Reflection) machen oder Assembly Binding Redirection durchführen. Weitere Infos darüber findest Du in der Forensuche (da Thema wurde schon öfter in verschiedenen Szenarien besprochen) und hier: [FAQ] Office (Word, Excel, Outlook, ...) in eigenen Anwendungen verwenden