Hehe, mir gings auch eher um die verschachtelten foreach und das (mich störende) if im Original-Post. Zusätzlich dazu die Tatsache, dass erstmal jede Menge Instanzen allokiert werden, welche dann später erst genutzt werden.
Ich hab's jetzt so umgebaut, dass ich im Rahmen des Projektes damit leben kann.
Meine Frage zielte also eher auf Vereinfachung des ursprünglichen Codes ab bzw. wenn ich jetzt im Nachhinein darüber nachdenke, auch darauf, wie man mit (unbekannten?) Datenmengen umgeht bei einer Art Stapelverarbeitung. Der Code soll nichts anderes machen, als für alle Benutzer im System X eine Mail zu verschicken und sie über offene Aufgaben (Tasks) benachrichtigen. Bzw. war es die Vorarbeit dafür, nämlich das Finden der entsprechenden Benutzer und dazugehörigen offenen Aufgaben.
die Mailadresse des Benutzers holt und die Informationen über die offenen "Task"s in Mail-Text umwandelt sowie die Mail sendet.
Alle Properties von DomainServiceLocator sind Interfaces. Dort gibt es z.B. IOService, DBService, MailSender, UserService und so weiter. Die einzige Stelle, wo wir in dem Projekt bisher Service-Instanzen zur Laufzeit bestimmen, ist der DBMS-Typ (MySQL / MSSQL). Ansonsten haben alle Interfaces bisher genau 1 Implementierung.
kannst du diese 3 Punkte noch etwas genauer ausformulieren oder einen Alternativen Code vorschlagen?
Ziel dieser Methode ist es, eine Übersicht zu bekommen, für welchen "User" welche "Task"s offen sind. Dies wird im Nachgang benötigt, da je Task eine Mail verschickt werden soll. D.h. die Task- und User-Instanzen werden anschließend noch benötigt.
ich habe in einem DomainLayer eines Webservices folgende Methode so ähnlich mehrfach implementiert:
private Dictionary<long, List<TaskTO>> GetOpenTasksByUserIDs()
{
Dictionary<long, List<TaskTO>> result = new Dictionary<long, List<TaskTO>>();
var activeUsers = DomainServiceLocator.userService.GetAllActiveUsers();
foreach (UserTO user in activeUsers)
{
var openTasks = DomainServiceLocator.lpaService.GetAssignedOpenTasksForUserID(user.ID);
foreach (TaskTO openTask in openTasks)
{
if (!result.ContainsKey(user.ID))
{
result.Add(user.ID, new List<TaskTO>());
}
result[user.ID].Add(openTask);
}
}
return result;
}
Ich wüsste gern, ob man den Code vereinfachen kann. Insbesondere das if-Statement im inneren foreach stört mich (inkl. Klammersetzung 4 Zeilen Code um einen Eintrag hinzuzufügen falls er noch nicht existiert).
Ja, es müssen auf jedem Gerät alle 15 Mio Datensätze zur Verfügung stehen ... da es lediglich um das herausfinden eines Status' geht und nicht auszumachen ist, von welcher Nummer der Status benötigt wird, muss jedes Gerät "alles wissen".
@ Hajoseb:
Bei der Synchronisation von Gerät auf PC werde ich es jetzt so machen, dass nur alle geänderten Daten übertragen werden. Das passiert dann ca. 20x je Gerät am Abend, wahrscheinlich jeweils 1-5 MB.
Anschließend werden alle so gesammelten, neuen Daten in die lokale DB auf dem PC importiert.
Am Morgen erhält dann aber jedes Gerät ein komplett neues Datenpaket. Wahrscheinlich werde ich die Daten dazu 1x exportieren (MySQL -> SQL Server Compact / *.sdf-Datei) und diese Datei dann auf jedes Gerät kopieren. Wobei sich hier nach ersten Tests schon der nächste Flaschenhals anbahnt: Die Verbindung über das Cradle (ActiveSync oder per Software über rapi.dll) ist sehr langsam. Ich habe hier einen Durchsatz von ca. 250 KB/s gemessen. Bei großen Datenmengen muss ich wohl darüber nachdenken, die SD-Karten manuell per Kartenleser zu bespielen.
@ FZelle:
Per Transaction habe ich es bisher nicht versucht. Was ich aber gemacht habe ist folgendes:
Inserts/Updates per StringBuilder in Blöcken von 1000 Stück je Kommando abgesetzt
auf dem Desktop (MySQL) das Tabellenformat auf MyISAM umgestellt (vorher: InnoDB)
Bei einem Test mit 1 Mio Datensätzen ist die Performance jetzt um das 20-fache gestiegen.
Ok, ich komm dann wohl um eine embedded-DB nicht drum rum.
Wollte es erst mit SQLite machen, aber dann habe ich noch SQL Server Compact 3.5 entdeckt und werde es jetzt damit versuchen.
Ich habe mal eben ein paar Balstungstests (1 Mio Einträge) bzgl. der Synchronisation (updates, inserts) gemacht und bin doch recht erstaunt über die (geringe) Performance. Eventuell bediene ich aber auch das PreparedSTM falsch, dazu werde ich aber einen neuen Thread starten.
Also WLAN wird es keins geben, auch haben die Geräte keinerlei andere (dauerhafte) Konnektivität um irgendwelche WebServices abzufragen. Sie werden lediglich alle morgens einmal mit aktuellen Daten aus der DB versorgt und schieben Nachmittags alle gesammelten / geänderten Daten wieder zurück in das System (jeweils per Docking-Station / Cradle über USB).
@herbivore:
Das mit dem Dictionary habe ich schon versuch, das wird bereits ab 1 Mio Datensätzen kritisch. Dictionary<int, enum> frißt da ca. 60 MB, bei <string, enum> kommen wir sogar auf 80 MB ... bei 15 Mio Datensätzen wird das also nichts, gleich gar nicht auf einem mobilen Gerät.
Ist korrekt, neben dem Zeitpunkt der Datenänderung muss ich später auch wissen, auf welchem Gerät die Änderung vorgenommen wurde. Diese Information kann ich aber beim Import in die Datenbank herausfinden (Über Desktop-Tool, Gerät-Anschluss per ActiveSync).
Vielleicht hat ja noch jemand eine zündende Idee.
--
Noch weitere Hintergrundinformationen: Die 15 Mio Datensätze stehen bereits auf einem Desktop-System parat, sollen auf alle Geräte kopiert werden um dort ausgewählte Datensätze zu manipulieren. Anschließend wird 1x täglich synchronisiert (Abends von Geräte auf DB, morgens von DB auf Geräte).
ich bin mir nicht ganz sicher, ob mein Thema hier rein gehört und weiß auch nicht so recht, wonach ich suchen könnte.
Fangen wir einfach Mal an, mein Problem ist schnell geschildert: Ich habe ca. 15 Mio Zahlen (4- bis 9-stellige, beliebige Werte). Weiterhin habe ich 5 verschiedene Stati, hier mal anonym "Status1" bis "Status5" genannt. Die Daten kommen aus einer MySQL-DB und sollen auf ein mobiles Gerät (Win CE) kopiert werden. Die Datenorganisation ist mir überlassen. Weiterhin soll eine Synchronisation in beide Richtungen möglich sein, die Geräte-Anzahl wird außerdem auf ca. 20-30 steigen.
Die eigentliche Anwendung ist folgende: Auf dem Gerät wird eine Nummer gescannt / eingegeben. Anhand dieser Nummer soll der Status herausgefunden, angezeigt sowie eventuell editiert werden. Am Ende des Tages werden alle Daten von allen Geräten in die DB synchronisiert, am nächsten Morgen von der DB auf alle Geräte synchronisiert.
Meine große Frage ist nun, wie ich diese Daten möglichst einfach auf dem Gerät organisieren, auch unter dem Aspekt, dass ich oft in beide Richtungen synchronisieren muss.
--
( Arbeitsverzeichnis ist "\\": )
--
Hier mein erster Ansatz:
Leere Dateien - jeder Dateiname identifiziert eine Ziffer - angenordnet nach:
Vorteil: Sehr schnelle Suche (Suchergebnis ergibt sich anhand der existierenden Datei). Nachteil: Erstellen der (15 Mio) Dateien bei DB-Export dauert ewig ; Synchronisation sehr zeitaufwendig.
--
Zweiter Ansatz:
Für jeden Status eine Datei (welche alle zugehörigen Nummern zu diesem Status enthalten). Änderungen werden in einem anderen Verzeichnis hinterlegt:
Vorteil: Der DB-Export ist sehr schnell. Nachteil: Um eine Nummer zu finden, müssen im worst case alle Dateien durchsucht werden, was sehr lange dauern kann.
--
Dritter Ansatz:
Ähnlich wie Ansatz 2, mehr Dateien, dafür bessere Vorsortierung: Die erste Ziffer einer Nummer wird als Lookup im Verzeichnis codiert:
Vorteil: Für den Lookup von "54135" (also allgemein "5*") kann ich direkt nach \\in\5\ bzw. \\out\5\ navigieren und dort alle Dateien (maximal 2 x 5 Dateien) durchforsten. Nachteil: Ich kenne die Verteilung der Nummern nicht, im worst-case beginnen sehr viele Nummern mit der gleichen Ziffer und das System geht nicht auf.
--
Den dritten Ansatz könnte man natürlich noch verfeinern indem man anstelle der ersten Ziffer die ersten beiden oder ersten drei Ziffern in Verzeichnisse codiert.
Ich würde gern auf eine lokale DB auf dem Gerät verzichten (u.a. aus Zeitdruck). Was fallen euch noch für Ansätze ein, die Informationen in Dateien zu organisieren?
Ich habe eine Frage bzgl. des PropertyGrid: Für die Editierung div. Werte habe ich aus mehreren Gründen (Lokalisierung, Validierung, corp. design, ...) Controls gebaut.
Beispielsweise habe ich zur Editierung einer Position einer Komponenten (auf einem Zeichenfeld) ein Control gebaut (Screenshot siehe unten), welches per DropDown aus dem PropertyGrid angezeigt wird.
Ich suche nun nach einer Möglichkeit, dem Nutzer die Möglichkeit zu geben, auch ohne Öffnen des Controls den Wert zu editieren. Sprich um die Position zu ändern, soll der Nutzer
X = 15 , Y = 100
editieren können (also den Text im PropertyGrid), z.B. in
X = 15 , Y = 200
Weiß jemand, wie man die Editierung auf beide Varianten ermöglicht?
Das ganze habe ich in dem Control das beim PropertyGrid aufklappt gemacht, d.h. egal wo ich bin, wann immer Enter gedrückt wird, soll das Control geschlossen werden und der Status gemerkt werden, dass der Nutzer "auf ok geklickt" hat ( = Enter drücken ).
ich habe bereits die Forensuche sowie Google abgegrast, aber leider keine passende Antwort auf meine Frage gefunden. Um es euch einfach zu machen, habe ich soeben ein Minimalbeispiel aus meinem Projekt extrahiert (Code vereinfacht, auf 1 Problem reduziert).
Es geht um folgendes: In einem Projekt verwende ich das PropertyGrid um Objekt-Eigenschaften zu manipulieren. Aus div. Gründen (Lokalisierung, corp. design, Validierung, ...) habe ich eigene Controls für die Editierung von einigen Eigenschaften geschrieben. Nehmen wir beispielsweise die Position eines Elements. Da diese von meinem Zeichenfeld begrenzt ist und nicht kleiner als (0, 0) werden darf, kann ich durch die Editierung mittels eigenem Control die eingebene Position sehr einfach validieren (ich weiß, dass es auch über das PropertyGrid selbst geht).
Mein eigentliches Problem liegt darin, dass ich es nicht schaffe, die Enter-Taste als Bestätigung für die Änderung von Werten abzufangen. Es scheint so, dass diese automatisch dafür verwendet wird, das per DropDown angezeigte Control zu schließen.
Der Screenshot unten zeigt mein Control zur Editierung einer Position. Für das erste NumericUpDown (X-Koordinate) habe ich bereits folgendes versucht:
public LocationControl()
{
InitializeComponent();
this.numX.KeyDown += new KeyEventHandler(numX_KeyDown);
}
void numX_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
this.btnAccept.PerformClick();
}
}
Das ganze verhält sich wie folgt:
ENTER -> Event nicht ausgelöst, Control wird geschlossen
SHIFT + ENTER -> Event ausgelöst, alles ok
STRG + ENTER -> Event ausgelöst, alles ok
Ich würde gern auf die Enter-Taste als solche reagieren. Gibt es die Möglichkeit, diese nicht automatisch zum Schließend des DropDown-Controls zu verwenden?
Auch PreviewKeyDown bringt keinen Erfolg. Beim Drücken der Enter-Taste kommt das Event gar nicht erst beim Control an.
Vielleicht kann man das Control auch anders anzeigen? Ich mache es derzeit mittels
editorService.DropDownControl(control);
.
Hinweis:Ich habe das Beispiel als Code (VS 2008 Projekt) und kompiliert vorliegen ; sämtlicher Code zur Validierung, Lokalisierung etc. wurde entfernt. Ich hänge es gern als Anhang in einen weiteren Post.
genau den Workaround bin ich nun auch erstmal gegangen. Leider äußert sich das bei mir allerdings so, dass die Scrollbalken danach nicht immer korrekt positioniert sind. Der Ausschnitt stimmt, aber die Scrollbalken scrollen teilweise nicht zum korrekten Offset.
Wenn ich es mit einer kurzen Verzögerung mache (Thread.Sleep() im Millisekundenbereich), habe ich ein unschönes Flackern, aber die Scrollbalken haben die korrekte Position.
ich habe mein Problem anhand eines Minimalbeispiels nachgebaut: Ich habe ein UserControl, welchen Zweck auch immer das erfüllt (meins soll zum Zeichnen von irgendwelchen Objekten dienen). Dieses UserControl ist auf einem Panel untergebracht, bei dem das AutoScroll-Property auf true gesetzt ist.
Mein Problem äußert sich nun wie folgt: Ich selektiere mein UserControl, arbeite damit und verschiebe in diesem Zusammenhang die Scrollbalken des Panels, auf dem das UserControl untergebracht ist. Wechsle ich nun mit dem Fokus aus dem UserControl z.B. in eine Textbox außerhalb des Panels und kehre anschließend zurück, so wird der Scrollstatus nicht erhalten.
Das ganze resultiert darin, dass ich bei jedem Verlassen des UserControls und zurückkehren wieder ganz oben bin, also erneut nach unten scrollen muss. Drei Bilder sollen das Problem verdeutlichen, siehe Anhang.
Kann mir jemand erklären, woran das liegt und wie ich das springen der Scrollbalken bei Fokuswechsel verhindern kann?
nachdem ich nun ca. 30 min bei Google und hier im Forum gesucht aber nichts passendes gefunden habe, werde ich nun doch posten und hoffe nicht wieder mit einer "bitte Suche verwenden" Antwort abgeblockt zu werden.
Mein Problem ist schnell beschrieben: Ich benutze das PropertyGrid in einer WindowsForms-Anwendung um wahlweise eines oder mehrere Objekte zu editieren. Wähle ich nun ein Objekt an, so sollen alle mit Browsable(true) markierten Properties geändert werden dürfen.
Selektiere ich hingegen mehrere Elemente (des gleichen Typs), so binde ich sie als Array an das PropertyGrid und kann sie auch wunderbar bearbeiten. Ich möchte nun aber gern Einschränkungen machen, was die Editierung bei MultipleSelect angeht. So darf beispielsweise das Property "Name" meiner Objekte nicht gleich sein, über das PropertyGrid kann ich dies nach dem Binden mehrerer Objekte jedoch ändern.
Frage: Wie kann ich eine Einschränkung vornehmen, bei der ich Properties freigebe wenn nur 1 Objekt an das PropertyGrid gebunden ist, zur Editierung aber sperre wenn mehrere Objekte gebunden sind?
Beispiel:
public class MyElement
{
// Dieses Property soll nur editierbar sein, wenn 1 einziges
// Objekt an das PropertyGrid gebunden ist
[Browsable(true)]
public string Name { get; set; }
[Browsable(true)]
public int AnyValue { get; set; }
}
@ DaMoe80: Das gefällt mir doch schon ganz gut! Ich kann die Vorteile von Enums nutzen und brauche keinen (speziellen) Code für die Lokalisierung zu schreiben.
Einzig verbleibendes Problem: Die penible Pflege der Resource-Datei ;)
@ herbivore:
Wenn ich einen WebService aufrufe, den Aufruf entsprechend absichere, reicht doch für den Benutzer eine einfache Meldung "Achtung, beim Webaufruf ist etwas schief gelaufen". Sicherlich könnte man hier eine eigene Exception-Klasse erstellen und die wirklich aufgetretene Exception als InnerException ablegen, aber mir geht es in diesem Fall rein um die Verwaltung von lokalisierten Fehlermeldungen, vielleicht gibt es da ja einen besseren Ansatz.
@ Mr Evil:
Die Lösung mittels xml-Datei ist ja im Prinzip die gleiche, als wenn ich die Resourcen-Dateien von Visual Studio nehme.
Fakt ist doch, dass ich bei beiden Lösungen sehr schnell auf die Nase fallen kann, wenn eine neue Nachricht dazukommt und ich in irgendeiner Sprache vergesse, diese hinzuzufügen. Dort habe ich dann keine Chance, das zur Compilzeit herauszufinden (außer natürlich pre- oder post-Buildscript).
Ich hätte gern eine Lösung bei der ich auch sicher gehen kann, dass wirklich alles übersetzt wurde.
Nun brauche ich derzeit passend zu den jeweiligen Fehlercodes lokalisierte Fehlertexte. Ein Ansatz wäre folgender:
public string GetText(ErrorCodes code)
{
switch (code)
{
case ErrorCodes.WEBEXCEPTION:
return Properties.Resources.ERR_WEBEXCEPTION;
case ErrorCodes.WEBTIMEOUT:
return Properties.Resources.ERR_WEBTIMEOUT;
....
}
}
Die lokalisierten Texte liegen also in Resourcendateien, was ich sehr aufwändig in der Wartung finde (Texte können in Sprachen vergessen werden ohne dass man es merkt).
Ein anderer Ansatz wäre dieser:
public string GetText(ErrorCodes code)
{
string lang = Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName.ToLower();
switch (code)
{
case ErrorCodes.WEBEXCEPTION:
if (lang.Equals("de")) return "Unbekannter Fehler bei ...";
if (lang.Equals("en")) return "Unknown error during ...";
case ErrorCodes.WEBTIMEOUT:
if (lang.Equals("de")) return "Zeitüberschreitung der Internetverbindung";
if (lang.Equals("en")) return "Timeout error of internet connection");
....
}
}
Auch hier kann ich div. Texte vergessen.
Ich würde bei der Definition der Fehlercodes gern bei einem enum bleiben (da ich hier sichergehen kann, dass Fehlercodes nur einmal vergeben werden und der Zugriff sehr einfach von überall möglich ist).
Ich habe nun daran gedacht, das enum durch ein struct zu ersetzen und dort die entsprechenden Informationen abzulegen, was den Vorteil hätte, dass keine Informationen fehlen können und sämtliche Texte in einer Datei stehen. Mein Ansatz:
public struct ErrorCode
{
private int code;
private string langDE;
private string langEN;
public int Code { get { return this.code; } }
public string LangDE { get { return this.langDE; } }
public string LangEN { get { return this.LangEN; } }
public ErrorCode(int pCode, string pLangDE, string pLangEN)
{
this.code = pCode;
this.langDE = pLangDE;
this.langEN = pLangEN;
}
}
public static class ErrorCodes
{
public static ErrorCode WEBSERVICE_EXC
{
get { return new ErrorCode(100, "Fehler bei ..", "Error during .."); }
}
public static ErrorCode THREAD_EXC
{
get { return new ErrorCode(200, "Fehler bei ...", "Error during ..."); }
}
}
Nachteil hier: Ich kann (aus Versehen) Fehlercodes doppelt vergeben. Man stelle sich ein sehr lange Liste von Fehlern vor, wo ich durchaus die Zahl 300 mehrfach vergeben kann.
Kennt jemand eine elegantere Lösung zur Verwaltung von Fehlercodes und lokalisierten Fehlertexten?
Da es sich teilweise um Events von Timern oder Hardware-Ereignisse handelt, ist dies nicht möglich. Die Sprache meiner Oberfläche kann sich zudem jederzeit ändern.
Mein Fehler ist vermutlich, dass ich meine Ressourcen zu früh hole, und zwar in den Hintergrundthreads. Vermutlich sollte ich darauf achten, sämtliche Ressourcen erst im Hauptthread (nach Invoke) abzurufen und von dort zu setzen.
Ich werde mal prüfen, ob das den gewünschten Effekt hervorruft.
Ja, das ist mir mittlerweile klar, aber wie realisiere ich es, dass ich alle Threads benachrichtige? Ich habe es bisher nur geschafft, mit Thread.CurrentThread... die Culture zu setzen, weiß aber nicht, wie ich die anderen Threads "benachrichtige".
Wie reichst du die Sprachänderung von der GUI an deinen Backgroundworker weiter?
Ich habe gerade ein ähnliches Problem mit mehreren Threads (und div. Timer, die Aktionen auslösen). Wenn ich in der GUI die Sprache ändere, so betrifft das nur den einen Thread. Mir fehlt eine Möglichkeit, für alle laufenden Threads die Sprache zu ändern.
ich benötige eine Lösung um einen PHP-WebService (incl. wsdl) mit NET (C#) zu konsumieren. Leider finde ich nicht ein einziges, vollständiges Beispiel zu diesem Thema. Ich schaffe es, einen NET - WebService mit C# zu konsumieren oder aber einen PHP-WebService mit PHP zu konsumieren. Weiterhin gelingt es mir, einen Java-WebService (Apache/Axis2) mit PHP und C# zu konsumieren.
Im VS 08 kann ich die URL (.php?wsdl oder .wsdl) angeben, der Service wird auch gefunden, entsprechende Klassen (Client-Klasse) incl. Methoden generiert. Aber immer, wenn ich auf den Service zugreifen will, gibt es Fehlermeldungen wie:
Der Server hat keine sinnvolle Antwort gegeben; dies kann durch einen nicht übereinstimmenden Vertrag, ein vorzeitiges Herunterfahren der Sitzung oder durch einen internen Serverfehler verursacht sein.
Hat jemand ein funktionierendes Beispiel für die Kommunikation von PHP und NET?
ich versuche vergebens, das Icon aus einer Datei zu extrahieren, mit dem diese Datei verbunden ist.
Beispiel:
MP3-Dateien sind auf dem aktuellen Rechner mit Winamp verknüpft, also haben alle MP3s ein Winamp-Symbol.
Ich such nach einer Möglichkeit, die mir für die Dateiendung .mp3 (oder eine konkrete .mp3-Datei) das entsprechende Icon/Bild liefert, so wie es im Explorer angezeigt wird.
Habe leider mit der Forensuche nichts finden können