Laden...

Forenbeiträge von Expandable Ingesamt 42 Beiträge

08.04.2009 - 22:20 Uhr

Vermutlich muss es ... select new Lagerpositionen {...} heißen statt select new Lagerpositionen() {...} (Klammern weg).
Falls nicht: Funktioniert ... select lp?

22.02.2009 - 11:59 Uhr

Liegt eventuell am VSync. Dein Laptop ist schnell genug, beide Objekte einzeln mit 60 FPS zu rendern. Sofern er aber beide rendern soll, reicht's vielleicht nur noch für 58 fps. Mit aktiviertem VSync werden daraus dann 30.

04.02.2009 - 10:50 Uhr

"Per Hand" mit Linq2Xml wäre auch eine Möglichkeit.

19.12.2008 - 16:04 Uhr

Es passiert genau das, was du programmiert hast: Die innere For-Schleife erhöht i++ bei jedem Durchgang. Warum das so ist und warum das sinnvoll sein sollte, weiß ich nicht. Und wieso legt die innere Schleife ein z an?

06.05.2008 - 17:48 Uhr

Das hatte ich befürchtet. Vielen Dank!

06.05.2008 - 11:02 Uhr

Hallo allerseits,

folgende Frage: Wenn ich ein Event mit += abonniere, also MyEvent += new EventHandler(bla); kann ich es ja - MUSS ich es ja - mit -= wieder abbestellen, weil sonst der Abonnierer nicht garbage collected werden kann, wie man hier How to: Subscribe to and Unsubscribe from Events (C# Programming Guide) nachlesen kann:

Unsubscribing

To prevent your event handler from being invoked when the event is raised, simply unsubscribe from the event. In order to prevent resource leaks, it is important to unsubscribe from events before you dispose of a subscriber object. Until you unsubscribe from an event, the multicast delegate that underlies the event in the publishing object has a reference to the delegate that encapsulates the subscriber's event handler. As long as the publishing object holds that reference, your subscriber object will not be garbage collected.

Die Frage ist jetzt: Irgendwann vergesse ich einfach den Abonnierer, d.h., es gibt einfach keine Referenz mehr auf ihn, und er kann gc'ed werden. Wann bestelle ich dann am besten die Events ab, die der Abonnierer abonniert hat? Geht das irgendwie automatisch (wohl kaum)? Was ist die eleganteste Lösung? IDisposable definieren und jedes mal, wenn ich glaube, dass ich ein Objekt nicht mehr brauche Dispose() aufrufen?

Danke!

16.04.2008 - 22:22 Uhr

Danke für deine Hinweise. Dazu folgende Bemerkungen:

Du schreibst, dass man es nicht als Cache sehen sollte, aber dennoch pro ID nur ein Objekt existieren sollte. Wenn ich nun an zwei unterschiedlichen Stellen meines Programms auf dieses Objekt zugreifen will, muss es doch eine zentrale Stelle geben, wo ich, wenn ich ein Objekt mit einer gewissen ID anfordere, immer die gleiche Instanz zurückerhalte. Das habe ich halt einfach Cache genannt. Ich habe also praktisch eine große Bindinglist für alle Arten von Objekten, von denen ich mir immer einen Teil zum Anzeigen raussuche (je nach Filter- und Sortierbedingung).

Welcher User welche Einträge geschrieben hat, ist mir in 99,999% der Fällen egal (also die Sicht User.Entries). Welcher Eintrag von welchem User geschrieben wurde (also Entry.Author) brauche ich aber in 99,999% der Fälle. Folglich ist die Beziehung in diese Richtung implementiert.

Zu den Workerthreads schlägst du also vor: Objekte im GUI-Thread ändern (z.B. neuer Name für einen Benutzer setzen), dann die DB-Operationen im Workerthread ausführen? Also praktisch im GUI-Thread eine Liste von DB-Operationen, die ausgeführt werden müssen, aufbauen und dann zum Eintragen in die DB an einen Workerthread schicken. Hmm, klingt gut. Wäre in meine selbstgestrickte Lösung relativ leicht einzubauen... Danke, auf die Idee bin ich bislang noch nicht gekommen!

16.04.2008 - 20:30 Uhr

Hallo allerseits,

ich bin auf der Suche nach Literatur (oder Anregungen/Meinungen) zu folgendem (software-architektonischen) Problem (Zusammenfassung siehe unten), da ich irgendwie nicht genau weiß wonach ich eigentlich suchen soll...

Vereinfacht dargestellt habe ich in der DB eine Tabelle für User und eine für Einträge, wobei die Einträge-Tabelle einen FK für einen User hat, der der Autor des Eintrags ist. In meinem Objektmodell der Anwendung ist das dann auch genauso abgebildet. Also die Klasse Eintrag hat ein Property Autor, welche auf den entsprechenden User zeigt.

Die Anwendung verlangt, dass Einträge und User auf einer WinForms-Oberfläche dargestellt werden - und zwar immer die aktuellsten Daten. Soll heißen, ändert der Benutzer am aktuellen Rechner oder ein anderer Benutzer über's Netzwerk die Daten in der DB, muss die Anwendung möglichst performant die Änderungen anzeigen. Natürlich soll auch filtern, sortieren, etc. möglich sein.

Mein Problem damit ist jetzt nicht wirklich programmiertechnisch, sondern eher architektonischer Natur: Wann lade ich die User/Einträge Objekte? Wie aktualisiere ich sie in der GUI? Wie mache ich das threadsafe?

Ich habe bisher mit zwei Ansätzen herumgespielt:
1.) Selbstgestrickt. Eine Klasse PersistenceManager, die beim Starten der Anwendung alle Datensätze ausliest und im Speicher cacht. Änderungen werden auf die DB werden ausgelesen und im Cache vermerkt, dann wird ein CacheChanged-Event aufgerufen, und alle Views aktualisieren sich. Alle Objekte implementieren INotifyPropertyChanged, sodass Änderungen an den Objekten selbst sofort angezeigt werden können, neue/entfernte Objekte, etc. werden über BindingLists bequem in der GUI angezeigt (in Wirklichkeit gehört da aufgrund des Filterns/Sortierens noch mehr dazu, aber das ist jetzt nicht das Problem).

Funktioniert soweit ganz gut - dadurch, dass ich alle Daten im Cache habe, habe ich immer die gleichen Objekte. Nachteil: Überhaupt nicht threadsafe, es ist sogar absolut unmöglich von einem anderen Thread aus auf den PersistenceManager zuzugreifen, weil ja alle Objekte direkt von der GUI verwendet werden. Falls also was in einem Backgroundthread ablaufen soll, müssen erst Kopien der Objekte erzeugt werden und diese dann umständlich über einen SynchronizationContext wieder in den GUI-Thread PersistenceManager zurückgespiegelt werden.

2.) Linq to SQL. Scheint soweit auch ganz brauchbar zu sein. Nur: Einen DataContext für die ganze Anwendung? Führt letztlich ja auch nur zur gleichen Architektur wie meine Lösung und ist genauso nicht threadsafe. Außerdem habe ich weniger Kontrolle über den internen Cache des Kontexts, was manche Sachen umständlicher macht als die selbstgestrickte Lösung. Und mehrere DataContexts führen zu dem Problem, dass ein und das selbe Objekt ("inhaltlich") nicht mehr ein und das selbe Objekt für .NET ist (und operator== will ich nicht mit einem ID-Vergleich überladen). Außerdem: Ich kann einen User, den ich aus einem anderen DataContext habe, nicht zu einem Eintrag hinzufügen, den ich mit einem zweiten DataContext speichern will (Problematik disconnected model /attach- Linq2Sql).

**Lange Rede, kurzer Sinn - meine Fragen im einzelnen sind (an euch, also wie macht ihr's, und Literatur dazu)

  • Object Identity über Referenzvergleich per Standard operator== oder operator== mit ID-Vergleich überladen?
  • Wann welche Objekte laden? Ist cachen beim Appstart ok (die Anzahl der Objekte hält sich in Grenzen, bzw. es müssen z.B. nur alle User (worst case 1000) gecacht werden. Einträge kann man selektiv cachen, sodass z.B. im worst case nur max. 2000 Einträge im Cache liegen).
  • DB-Operationen im GUI-Thread oder in einem Backgroundthread?
  • Wie verbinde ich das alles? **

Vielen Dank!

11.04.2008 - 09:16 Uhr

Leider musste ich feststellen, dass der User mit einem Rechtsklick immer noch auf die Richtextbox zugreifen kann. Ich hab's jetzt so gelöst. Nach meinen bisherigen Tests ist damit keine Änderung des Textes mehr möglich, das Aussehen (Rahmen, Schrift, etc.) sind alle normal und man kann Text selektieren und kopieren:


public class MultilineLabel : RichTextBox
	{
		[DllImport("user32.dll", EntryPoint = "HideCaret")]
		public static extern long HideCaret(IntPtr hwnd);

		public MultilineLabel()
		{
			BorderStyle = BorderStyle.None;
			Cursor = Cursors.Arrow;

			IPalette palette = KryptonManager.CurrentGlobalPalette;
			ForeColor = palette.GetContentShortTextColor1(PaletteContentStyle.LabelNormalControl, PaletteState.Normal);
			Font = palette.GetContentShortTextFont(PaletteContentStyle.LabelNormalControl, PaletteState.Normal);
		}

		protected override void OnMouseUp(MouseEventArgs e)
		{
			HideCaret(this.Handle);
		}
	}

19.03.2008 - 16:47 Uhr

verwendetes Datenbanksystem: <VistaDB/Sql Server 2005>

Hallo alle zusammen,

ich weiß nicht, ob hier schon jemand Erfahrungen mit den Sync Services für ADO.NET gemacht hat. Ich hätte jedenfalls dazu eine Frage:

Ist es damit möglich, eine Hierarchie aufzubauen? Also Clients A, B und C synchronisieren sich mit Server D, die Clients E, und F synchronisieren sich mit Server G und Server D und G synchronisieren sich mit dem Hauptserver H?

Ich habe dazu irgendwie nichts gefunden, was die traurige Vermutung nahe legt, dass es wohl nicht so ohne weiteres geht.

Eine Idee wäre, für jede Synchronisationsebene eigene insert/lastupdate/delete Felder zu verwenden. Nur beschränkt mir das die Hierarchietiefe und ist umständlich, aufwändig und fehleranfällig.

Danke!

04.03.2008 - 16:54 Uhr

Danke, ich hab's jetzt doch einfach per Reflection gemacht:


void ControlDataGridView_CellFormatting(object sender, System.Windows.Forms.DataGridViewCellFormattingEventArgs e)
		{
			var grid = sender as ControlDataGridView;

			// Supports only two-level property depth
			if (grid.Columns[e.ColumnIndex].DataPropertyName.Contains("."))
			{
				var properties = grid.Columns[e.ColumnIndex].DataPropertyName.Split(new char[] { '.' });
				var propertyValue = grid.Rows [e.RowIndex].DataBoundItem.GetType ().GetProperty (properties [0]).GetValue (grid.Rows [e.RowIndex].DataBoundItem, null);
				e.Value = propertyValue.GetType().GetProperty(properties[1]).GetValue(propertyValue, null);
			}
		}
04.03.2008 - 09:10 Uhr

Hallo alle zusammen,

folgende vereinfachte Situation:


class User
{
   String name;
   Organization organization;
}

class Organization
{
   String name;
}

Nun binde ich eine List<User> an ein DataGridView. Dort soll nun in jeder Zeile der Username und der Name der zugehörigen Organisation stehen. Wie kriege ich das möglichst elegant hin, ohne zu User ein Property OrganizationName get { return organization.Name } hinzuzufügen?

Vielen Dank!

22.02.2008 - 16:59 Uhr

Ah, danke. Wusste ich doch, dass ich schon mal was von der Klasse gehört habe. Danke schön!

22.02.2008 - 16:22 Uhr

Hi,

ich habe folgendes Problem: Ich muss ein paar Datenobjekte und Listen aktualisieren, die an die GUI gebunden sind. Die neuen Daten werden in einem Thread geladen. Um diese dann letztlich aktualisieren zu können, muss diese Aktualisierung ja im GUI-Thread stattfinden, sonst wird eine CrossThreadException geworfen.

Mir ist klar, dass man mit Control.BeginInvoke() in den GUI-Thread wechseln kann. Nur was ist, wenn ich gerade dafür kein Control zur Verfügung habe, weil ich zu tief in meiner Business-Schicht bin? Wie kann ich sozusagen "manuell" in den GUI-Thread wechseln? Kann man irgendwie speichern, was der GUI-Thread "ist" und dann einen manuellen Context-Wechsel machen?

Bin bei Google und MSDN leider nicht fündig geworden, weiß aber auch nicht genau, was ich suche bzw. was ich brauche.

Danke,

  • Expandable
19.02.2008 - 14:44 Uhr

Danke, damit ist soweit alles geklärt.

15.02.2008 - 17:42 Uhr

Hmm, achso. Dann stellen sich mir zwei Fragen:

Kann man NetTcpBinding verwenden, wenn man über das Internet geht (dort wird im allgemeinen eher ein Http-Binding empfohlen)? Ich schreibe Client und Server selbst und beide in WCF. Braucht TCP ein Zertifikat zur Verifikation, oder sind andere Methoden ebenso sicher?

Was ist nun die bessere Vorgehensweise für folgendes Problem: 100 Clients verwenden einen Server. Die Clients schicken alle paar Minuten mal eine Nachricht an den Server, immer wenn das passiert, müssen alle anderen Clients vom Server darüber benachrichtigt werden.

Lösung a) Per Callbacks. Problem: Ich kann ja nicht alle 100 Verbindungen die ganze Zeit offen halten, oder? Weil was ist, wenn's mal 1000 Clients werden? Andere Lösung über Polling (z.B. alle 10 Sekunden frägt jeder Client nach, ob's was neues gibt, die neuen Daten kommen per Callback und dann wird die Verbindung geschlossen): Belastet den Server, auch wenn gar nix passiert.

Lösung b) Verbindung nur herstellen, wenn Client an Server 'ne Nachricht schickt. Der Server merkt sich die IP-Adressen der Clients. Wenn er an die Clients 'ne Nachricht schicken muss, fungiert der Server als Client und informiert damit 99 Server, dass etwas passiert ist. Problem: Firewalls, umständlich.

Danke!

15.02.2008 - 08:22 Uhr

Danke für Deine Antwort.

Gut klar, man bräuchte dann halt ein WsDualHttpBinding für Callbacks. Oder meintest Du, dass die von mir beschriebenen Probleme bei einem tcpBinding nicht auftreten?

13.02.2008 - 20:08 Uhr

Hallo allerseits,

ich habe hier ein kleines WCF-Problem. Offensichtlich habe ich da etwas missverstanden. Betrachten wir folgenden Service:


[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single) ]
	public class MyService : IService1
	{
public void Test(String n)
		{

			Console.WriteLine("erhalte call von Thread " + n);
			Thread.Sleep(5000);
			Console.WriteLine("kehre zurück für Thread " + n);
			
		}
}

In der app.config des Servers:


<service name="WcfServiceLibrary2.MyService">
				<host>
					<baseAddresses>
						<add baseAddress = "http://localhost:8083/Server" />
					</baseAddresses>
				</host>

				<endpoint address ="Entry" binding="wsHttpBinding" contract="WcfServiceLibrary2.IService1">
.....

Der Client ruft die Test-Methode auf aus mehreren Threads auf:


void run()
		{
			

			var s= new Service1Client();
			
			var stop = new Stopwatch();

			Console.WriteLine("Thread " + Thread.CurrentThread.Name + " - Call to server: " + DateTime.Now.ToLongTimeString());

			stop.Start();
			s.Test(Thread.CurrentThread.Name);
			stop.Stop();

			Console.WriteLine("Thread " + Thread.CurrentThread.Name + " - Call to server ends: " + DateTime.Now.ToLongTimeString());
			Console.WriteLine("Thread " + Thread.CurrentThread.Name + " - Benötigte Zeit in ms: " + stop.ElapsedMilliseconds.ToString());

			Thread.Sleep(10000);
// erneuter aufruf
			s.Test(Thread.CurrentThread.Name + "b");
			Thread.Sleep(3000);
			s.Close();
		}

		static void Main(string[] args)
		{
			Program obj = new Program();
			for (int i = 0; i < 10; ++i)
			{
				Thread.Sleep(250);
				var t = new Thread(obj.run);
				t.Name = i.ToString();
				t.Start();
			}
			Thread.Sleep(10000);
			for (int i = 0; i < 10; ++i)
			{
				Thread.Sleep(250);
				var t = new Thread(obj.run);
				t.Name = i.ToString();
				t.Start();
			}

Folgendes Problem:
Die Threads des zweiten Durchlaufs in der Mainmethode werden vom Server erst abgearbeitet, nachdem die "b"-Threads des ersten Durchlaufs abgearbeitet sind. Vermutlich liegt also das Verbindungslimit bei 10. Mit anderen Worten: Braucht man die Verbindung nicht mehr, sollte man sie schließen, damit andere Threads sich verbinden können.

Mein Problem damit ist: Callbacks. Dafür zunächst vielleicht mal grob mein Problem von einem High-Level Standpunkt aus:

  • Die Clients sollen Daten, die ein Nutzer einträgt/ändert, an den Server senden.
  • Der Server erhält die Updates, und schickt die Updates an alle ihm bekannten Clients raus (dafür wären eben Callbacks gut gewesen).

Eigentlich hatte ich vorgesehen, dass ein Thread eine Verbindung öffnet, diese offen lässt, und der Server jederzeit per Callback dem Client was mitteilen kann. Offensichtlich geht das nicht, weil die Verbindung ja sofort nach dem Serveraufruf clientseitig wieder geschlossen werden muss, um nicht andere Anfragen zu blockieren. Die Callbacks treten periodisch immer wieder auf.

Muss ich also wirklich jeden Client für die Callbacks zum Server machen, und den Server zum Client? Geht das nicht irgendwie einfacher? Mit Callbacks wäre das relativ elegant gegangen. So habe ich ja umgekehrt wieder das Theater mit Authentifizierung, Zertifikaten und dem restlichen Security-Zeugs.

Ich hoffe, ihr versteht was ich meine und könnt mir hoffentlich sagen, dass eine elegantere Lösung existiert.

Danke!

07.02.2008 - 12:10 Uhr

Aber über welche Zeitspanne reden wir bei der Commit-Phase? Das sind Millisekunden. Natürlich sollten verteilte Transaktionen nur innerhalb eines schnellen und zuverlässigen Netzwerks verwendet werden, also NICHT übers Internet (wenn man von einem herkömmlichen DSL- oder Interconnect-Zugang ausgeht).

Das ist ja leider mein Problem. Ich muss Transaktionen teilweise sogar über GPRS laufen lassen, wenn's ganz blöd kommt.

Was ich brauche: Der Client, der per GPRS/UMTS oder "normales" Internet an den Server angebunden ist, schickt diesem eine Nachricht. Wenn der Server die Nachricht erhalten hat, muss der Client etwas aus der DB löschen und darf dies nicht noch einmal schicken. Prinzipiell sind Transaktionen ja genau dafür da, aber so wie ich das sehe, muss ich mit jeder Nachricht noch eine Guid mitschicken, sodass der Server die Nachricht ablehnen kann, falls er sie schon einmal empfangen hat?

06.02.2008 - 22:20 Uhr

Entweder ist die Transaktion erfolgreich oder nicht. Wenn - auch nur kurz vorher - was schief läuft, wird die komplette Transaktion (und alle geschachtelten Transaktionen) verworfen.

Danke für Deine Antwort.

Um das weiter zu vertiefen:

A startet eine Transaktion und ruft die Services B, C und D auf, die ebenfalls an der Transaktion teilnehmen. A beendet die Transaktion, A, B, C, und D melden Erfolg, der Transaction Manager schickt das Commit-Signal an alle vier "Prozesse" raus. A, B, und C führen die Transaktion durch - D erhält aufgrund eines Verbindungsabbruches das Commit-Signal nicht mehr. Da A, B und C die Transaktion schon ausgeführt haben, D dies aber vielleicht niemals mehr kann, ist das System doch in einem instabilen Zustand. Oder?

06.02.2008 - 20:32 Uhr

Hi alle zusammen,

ich hätte hier mal zwei Verständisfragen (bezogen auf WCF, gilt aber wohl auch allgemein bei Netzwerkprogrammierung):

  • Client A ruft eine Methode M auf dem Server B auf. M liefert etwas zurück. Reliable Messaging ist eingeschaltet (z.B. per WSHttpBinding). Was passiert, wenn A M aufruft, B beginnt, M abzuarbeiten, die Verbindung bricht ab und A erhält nie eine Bestätigung, dass M ausgeführt wurde, obwohl es ausgeführt worden ist? Was macht man, wenn A das unbedingt wissen muss, und M (mit diesen Parametern) auf keinen Fall ein zweites Mal ausgeführt werden darf (mal von Transaktionen abgesehen... also prinzipiell die Frage: Wieso soll das wirklich zuverlässig funktionieren)?

  • Ein ähnliches Problem sehe ich auch bei Transaktionen: Client A startet Transaktion T, ruft M auf, Server B übernimmt T und führt damit M aus. A committed T, der Transaction Manager TM fragt nach, ob sowohl A und B mit dem committen einverstanden sind. A und B sagen beide ja. TM sendet den Committ-Befehl an an A und B. Aber was ist, wenn kurz davor (also zwischen "ja, committen geht klar" und "committe T") die Verbindung zu B abbricht?

Danke!

20.12.2007 - 12:01 Uhr

Sehr geil! Genau das was ich brauche! Herzlichen dank! An SetStyle() hab ich gar nicht gedacht :wall:

20.12.2007 - 10:30 Uhr

Ja und wie verhindere ich da, dass der User was eingeben kann? Der Text-Eingabe-Cursor darf auch nicht erscheinen. Dann wäre die Lösung ja wie meine TextBox-Lösung (abfangen des Enter-Events), was wiederum zu Problemen führt. Außerdem ist klar ersichtlich, dass es sich um eine TextBox handelt. Ich brauche etwas mit weißem Hintergrund und möglichst keinem Rand (so wie der Text der eMail-Nachricht in Outlook).

20.12.2007 - 10:02 Uhr

Hallo allerseits,

ich habe folgendes Problem, und ich finde irgendwie keine vernünftige Lösung dazu: Ein User kann in einer TextBox einen Text eingeben, der "beliebig" lang sein kann (also genauer irgendwas zwischen 1 und ca 32.000 Zeichen).

Nun möchte ich diesen Text in meiner WinForms-GUI anzeigen. Ohne irgendwelche Formatierungsmöglichkeiten wie fett oder kursiv. Allerdings sollen Absätze erhalten bleiben. Außerdem muss es automatische Zeilenumbrüche geben, und vertikale Scrollbars müssen angezeigt werden, wenn der Text zu lang ist.

Der Text, der angezeigt werden soll, befindet sich innerhalb eines Panels. Die Textbreite und die Scrollbars müssen natürlich reagieren, wenn sich die Panelgröße ändert.

Folgendes habe ich bereits probiert:

  • Eine TextBox zum Anzeigen verwenden. Diese auf readonly setzen, und das Enter-Event so überschreiben, dass der Focus automatisch auf ein anderes Objekt gelegt wird, sodass der Schreibe-Cursor nicht angezeigt wird. Problem: Auf manchen Systemen kommt es da zu Darstellungsfehlern.

  • Ein Label verwenden. AutoSize auf true. Das Panel hat AutoScroll auf true.Beim Eintragen des Texts und beim Resizen des Panels wird ausgeführt:

labelContent.MaximumSize = new Size(panelContent.ClientSize.Width - 30, 99999);
panelContent.AutoScrollMinSize = labelContent.Size;

Funktioniert soweit einwandfrei. Auch beim Resizen (und ist meine bevorzugte Lösung, soweit). Einziges Problem: Beim Vergrößern/Verkleinern der Forum über den mittleren Button in der Titelzeile des Fensters wird zwar das Label und das Panel immer korrekt resized, allerdings ist die Scrollarea teilweise viel zu groß. Zieht man das Fenster dann am Rahmen größer oder kleiner, wird die Scrollarea dann allerdings wieder korrekt angezeigt.

  • Hässlichste Lösung: Selbst Hand anlegen mit GDI und selbst zeichnen.

Hat jemand eine gute Idee? Danke!

14.12.2007 - 10:17 Uhr

Schade, hatte gehofft, das geht irgendwie "automatischer". Na trotzdem danke 😉

12.12.2007 - 11:46 Uhr

Ok, um's nochmal klarer zumachen, anscheinend habe ich mich zu undeutlich ausgedrückt 🙂

Ich habe eine Liste allUsers, in der alle User gespeichert sind. Nun habe ich verschiedene Funktionen, die mir Teillisten von allUsers zurückliefern. Z.B. GetAllUsersWithA liefert mir alle User aus allUsers in einer Liste, die mit A beginnen.

Nun sind die GetAllUsersWithA- und GetAllUsersWithB-Listen an zwei DGVs gebunden. Ändere ich ein Objekt der Hauptliste, also z.B. ändere ich den Namen von "Anton" in "Alex", wird dies beim DGV mit den A-Leuten richtig angezeigt. Das Problem ist aber: Was ist, wenn ich "Anton" in "Gustav" umbenenne? Dann muss der aus der Liste von A raus. Was ist, wenn ich bei allUsers einen "Alex" hinzufüge? Der muss in die Liste rein.

Also ist mein Problem: Wie "synchronisiere" ich diese Listen am effizientesten?

12.12.2007 - 11:21 Uhr

Hi, danke für deine Antwort.

Wenn sich ein Objekt ändert, dann funktioniert's wunderbar. Wenn ich aber zu myList per .Add(new User()) einen User hinzufüge, dann kann's aus dem oben beschriebenen Grund ja nicht gehn.

12.12.2007 - 09:52 Uhr

Hallo alle zusammen,

ich habe ein Problem mit WinForms-Databinding. Und zwar möchte ich folgendes: In mehreren GridViews soll eine Liste von User-Objekten anzeigt werden. In jedem GridView wird jedoch nur ein Teil der Liste angezeigt (z.B. in einem nur die User, die mit A anfangen, bzw. auch wesentlich komplexere Abfragen etc.).

Ich habe nun eine Klasse, die mir per Linq2SQL den wünschten Datensatz zurückliefert und in der Klasse speichert:

BindingList<User> myList; // Hier wird alles gespeichert, was in der DB in der User-Tabelle steht. Übrigens: User implementiert INotifyPropertyChanged.


BindingList<User> GetStartsWithA(){ 
   var result = from u in myList where blabla select u;
   return new BindingList<User>(result.ToList()); // <- hier geht's schief!
}...
BindingList<User> GetAll() {return myList}...

etc. Nun ist mein Problem: Jede Bindinglist ist verschieden. Irgendwo in meinem Programm wird myList nun geändert - also etwas hinzugefügt/gelöscht, ein Objekt geändert, etc. Nun sollten aber alle GridViews die Änderung anzeigen - das geht natürlich nicht, weil zwar myList die entsprechenden Events auslöst, allerdings nicht die Liste, die von GetStartsWithA zurückgeliefert wird.

Wie löse ich das am besten? Mit einem Event in der Klasse "ListChanged", an dem sich alle GridViews anmelden? Nur was machen die bei einem ListChanged-Event? GetStartsWithA() erneut aufrufen und somit ihre DataSource erneuern? Geht das nicht effizienter? Vor allem muss ich ja auch den gerade aktuell selektierten Zeilenindex behalten, den Zustand der Scrollbars, etc.

Gibt's dafür bessere Möglichkeiten?

Vielen Dank!

21.11.2007 - 09:53 Uhr

Ok danke, jetzt ist alles klar.

20.11.2007 - 16:46 Uhr

Danke, das weiß ich. Aber wie macht die ComboBox das intern? Wenn die ComboBox auf die Liste in der DataSource zugreifen muss, z.B. um die Elemente auf dem Bildschirm zu rendern - ist das thread-safe? Wenn nein, was mach ich dann? Das ist ja Code von Microsoft, da habe ich ja überhaupt keinen Zugriff drauf!

19.11.2007 - 15:46 Uhr

Danke für Deine Mühen, aber das habe ich leider nicht gemeint:


// Liefert mir eine Liste von Gruppen zurück. Jedes Group-Objekt und diese Liste muss threadsafe sein.
List<Group> g = GroupManager.Groups;
// Setze g als DataSource für eine ComboBox
myComboBox.DataSource = g;

// Wenn nun Thread A macht:
g[0].Name = "Irgenwas";
// Sollte das mit der ComboBox ja kein Problem geben, oder? Da greift dann, dass das Property name (wie weiter oben im Thread) thread-safe implementiert ist.

// aber was ist, wenn Thread A
lock (g)
g.Add(new Group());
// macht? Ich kann den Zugriff der comboBox auf die DataSource ja nicht locken, oder wird auf die Daten in der Liste nur beim Zugriff auf myComboBox.DataSource zugegriffen? Also dass 
lock (g)
myComboBox.DataSource = g;
// für die thread-safety ausreicht?

Danke!

19.11.2007 - 08:22 Uhr

Ok, String war ein blödes Beispiel. Angenommen, ich gebe eine Liste von Group-Objekten zurück, die thread-safe implementiert sein müssen. Ich übergebe diese Liste an eine ComboBox zum Anzeigen. Jetzt verweisen die eigentliche Liste und die in der ComboBox gespeicherte Liste ja auf die selben Objekte. Wenn also der Writer-Thread was an der Liste ändert, kann bei der ComboBox ja was schief gehen, d.h., die ComboBox benötigt eigentlich eine Kopie der Liste und der Objekte, oder?

18.11.2007 - 16:28 Uhr

Vielleicht verstehe ich auch nur was nicht richtig.

Also, ich habe mit String s = group.Name eine Referenz auf das String-Objekt, dessen Zugriff ja thread-safe sein soll.

Diesen String übergebe ich nun an eine TextBox: myTextBox.Text = s. Später zeige ich mit myForm.Show() eine WinForm an, die meine TextBox mit dem String s enthält. Nun könnte es ja passieren, dass ein anderer Thread group.Name = "Neuer String" macht, während die TextBox gerade gerendert wird -> Darf ja eigentlich nicht sein. Oder übersehe ich einen Grund, aufgrund dessen das gar nicht passieren kann?

18.11.2007 - 12:23 Uhr

Original von herbivore
Hallo Expandable,

s ist natürlich nicht geschützt, wie sollte das gehen und warum sollte das hier nötig sein?

Wenn es nötig ist, müsstest du alle Zugriffe auf s locken.

Ich kann den Zugriff auf s aber nicht locken, wenn ich den String z.B. in eine TextBox schreibe. Wie ist das beste Vorgehen? Eine Kopie erzeugen? Was ist, wenn ich eine Liste von Group-Objekten an eine ListBox/ComboBox übergebe? Muss ich dann all diese Objekte kopieren?

Gibt es keine bessere Möglichkeit? Wie macht man sowas normalerweise am saubersten (die Architektur steht noch nicht 100% fest, also wenn ihr gute alternative Vorschläge habt, wäre jetzt ein guter Zeitpunkt...)

Besten Dank!

16.11.2007 - 13:57 Uhr

Ich hab im obigen Beispiel mal das lock(this) ersetzt.

Original von herbivore
Hallo Expandable,

erfolgt hierbei gleichzeitig ein lesender Zugriff auf s
nein, dass kann nicht sein, da get und set ja mit dem gleichen Sperrobjekt gesperrt sind.

Wenn nicht das passiert, was du erwartest, muss das einen anderen Grund haben.

Muss man dann praktisch von allen Objekten/Daten, die man außerhalb der Klasse speichern will (z.B. in einer TextBox, o.Ä.) eine Kopie erstellen?
Nein.

herbivore

herbivore, ich glaube, Du hast mich nicht ganz verstanden 😉 Also noch mal etwas ausführlicher:

Group g = new Group(); - Neue Gruppe erstellen. Hier kann nix schief gehen.
g.Name = "Gruppe 1"; - Wir sind im Lock, solange wir hier schreiben, kann niemand sonst schreiben oder lesen
String s = g.Name; - Wir speichern einen Verweis auf das String-Objekt in der Group-Klasse.
g.Name = "xxxxxx"; - Wir ändern das String-Objekt in der Group-Klasse. Dabei kann keiner mehr über Group.Name auf das String-Objekt lesend oder schreibend zugreifen, sehr wohl aber über s! Oder verstehe ich da was falsch?

16.11.2007 - 13:18 Uhr

Was Getter und Setter sind, weiß ich, danke.

Also quasi so, dass man das obige Beispiel abändert in:


public String NameThread
        {
            get 
            {
                lock (lockObj)
                {
                    return Name;
                }
            }
            set
            {
                Console.WriteLine("Erbitte Set-Lock");
                lock (lockObj)
                {
                    Console.WriteLine("Set-Lock erhalten, schreibe...");
                    Name = value;
                    Thread.Sleep(5000);
                }
                Console.WriteLine("Set-Lock released");
            }
        }

Gut, damit ist folgendes aber immer noch nicht thread-safe:

Group g = new Group();
g.Name = "Gruppe 1";
String s = g.Name;
g.Name = "xxxxxx"; <- erfolgt hierbei gleichzeitig ein lesender Zugriff auf s, geht irgendwas schief.

Muss man dann praktisch von allen Objekten/Daten, die man außerhalb der Klasse speichern will (z.B. in einer TextBox, o.Ä.) eine Kopie erstellen? Wie implementiert man das am saubersten?

14.11.2007 - 15:17 Uhr

Original von herbivore

Vermutlich wäre es jedoch sinnvoller, das lock in Getter und Setter Name zu packen und nicht um den Aufruf von Name herum zu bauen.

herbivore

Danke für Deine Antwort. Wie genau meinst Du das mit den Gettern und Settern?

14.11.2007 - 13:34 Uhr

Hallo alle zusammen,

ich stehe gerade vor folgendem Problem: Aus einer Datenbank werden Datensätze ausgelesen, diese werden in Business-Objekte verwandelt, beispielsweise vom Typ "Group". In einer GroupManager-Klasse wird eine Liste, also List<Group> aller ausgelesenen Gruppen gespeichert. Dabei werden die Gruppen einmal zum Programmstart ausgelesen, und dann nicht mehr.

Die GroupManager-Klasse hat Funktionen, mit denen man sich gewisse Gruppen aus der Liste holen kann, z.B. per GetGroupByName, GetGroupByID, etc.. Dabei werden immer Referenzen übergeben, d.h., Referenzen auf Objekte in der Liste sind überall im System verteilt.

Grund dafür: Wenn irgendwo irgendwas an einer Gruppe geändert wird, sollen alle irgendwo rumliegenden "Gruppen-Objekte" (streng genommen ist es ja nur ein einziges Objekt) ebenfalls aktualisiert werden, deswegen also keine Kopien.

Mein Problem ist nun: Ich habe zwei Threads, ein Thread liest meistens nur Daten in den Gruppen-Objekten, ein anderer schreibt meistens nur. Das bedeutet ja, dass ich bei jedem Zugriff auf eines der Objekte ein Lock machen muss, oder? Gibt es eine bessere Möglichkeit?

Zur Verdeutlichung ein Code-Beispiel, das wunderbar funktioniert (der Caller-Thread muss warten, bis Thread #2 den Lock wieder freigibt).


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ConsoleApplication1
{
    class Program
    {
     
        static void Main(string[] args)
        {
            var p = new Program();
            p.run();
        }

        private void run()
        {
            // Liefert mir in dem Fall das 2. Gruppen-Objekt der Liste zurück
            var u = DataAccess.Instance.GetByName();

            Thread t = new Thread(new ThreadStart(mythread));
            t.Start();

            Thread.Sleep(1000);

            Console.WriteLine("Versuche zu schreiben außerhalb...");
            // Versuche zu schreiben - locke mein Objekt, Thread #2 versucht ebenfalls, auf dieses Objekt  zu schreiben
            lock (u)
            {
                u.Name = "Baggaaaaaa";
            }

            Console.WriteLine("Geschreiben - Warte...");
            t.Join();

            Console.WriteLine("Done");
            Console.ReadLine();
        }

        private void mythread()
        {
            DataAccess.Instance.Write();
        }
    }

    

    class DataAccess : Singleton<DataAccess>
    {
        private List<Group> groups;

        public DataAccess()
        {
            groups = new List<Group> { new Group { Name = "Axel" }, new Group { Name = "Karin" } };
        }

        public Group GetByName()
        {
            var r = from u in groups
                    where u.Name == "Karin"
                    select u;

            return r.ToList()[0];
        }

        public void Write()
        {
            // Versuche zu schreiben, locke mein Objekt
            Console.WriteLine("vor writeLock");
            lock (groups[1])
            {
                Console.WriteLine("write-gelockt!");
                Thread.Sleep(10000);
            }
            Console.WriteLine("nach writeLock");
        }
    }

    class Group
    {
        public String Name { get; set; }
    }

    abstract class Singleton<T>
        where T : class, new()
    {
        private static T instance = null;

        static Singleton()
        {
            instance = new T();
        }

        public static T Instance
        {
            get { return instance; }
        }

        protected Singleton()
        {

        }
    }
}

Ist das so ok? Ist das so überhaupt peformant, wenn ständig irgendwas gelockt wird?

Danke!

18.10.2007 - 16:35 Uhr

Hallo,

vielen Dank für Deinen Input!

Zu (1): Ich schätze, diese Lösung wird nicht sehr performant sein, oder?
Zu (2): Gibt es denn eine Funktionalität, mit der sich das Polling effizient implementieren lässt? Oder einfach ganz trivial mit Query (bzw. Stored Procedure) auf die Änderungstabelle, Thread.Sleep(2000), wieder Query, wieder Sleep, etc.?
Zu (3): Damit kenne ich mich auch nicht aus, aber soweit ich das verstanden habe, ist das doch hauptsächlich zum Versenden von irgendwelchen Berichten per eMail oder so gedacht?

Gruß

18.10.2007 - 12:51 Uhr

Hallo,

ich habe folgendes Problem: Ich habe eine Replication-Topologie von SQL Servern 2005. Eine C#-Client Anwendung liest Daten aus dieser Datenbank aus. Nun soll die Anwendung Änderungen an der DB (Insert/Updates/Deletes) grafisch in einer GUI anzeigen. D.h., kommt z.B. per Replication ein neuer Datensatz hinzu, so soll dieser neue Datensatz in der GUI angezeigt werden.

Da es scheinbar keine Möglichkeit gibt, so ein Replication-Event automatisch abzufangen, dachte ich, ich mach es selbst per Trigger. Dazu fallen mir zwei Möglichkeiten ein, und ich wollte einfach mal fragen, was ihr davon haltet oder ob ihr bessere Lösungen kennt:

(1) Der Trigger ruft CRL-Code auf, der wiederum meine Anwendung verständigt (z.B. über WCF oder .NET Remoting).

(2) Der Trigger schreibt in eine extra für angelegte Tabelle, welche Tabelle und welche Zeile wie geändert wurde (insert/update/delete) und meine Client-App überprüft z.B. alle 2 Sekunden, ob sich in dieser Tabelle etwas geändert hat und aktualisiert die GUI dann entsprechend.

Da pro SQL Server nur eine Anwendung drauf zugreift, scheint mir (2) fast die bessere Lösung zu sein. Irgendwelche Anmerkungen?

Danke und viele Grüße!

17.04.2006 - 17:34 Uhr

Hier wird ein Zeitgeber-Server und -Client implementiert, ist ganz hilfreich, sogar mit Threading...

http://www.microsoft.com/germany/msdn/library/net/csharp/NetworkingMitCSharp.mspx?mfr=true

27.02.2006 - 13:29 Uhr

Hallo allerseits,

ich bin gerade in der Planungsphase für ein neues Projekt. Letztenendes ist es sowas ähnliches wie diese Forensoftware hier, normalerweise hätte ich dafür einfach PHP genommen.

Da das Programm jedoch selbstständig mit anderen Servern kommunizieren muss (um ggf. Daten abzugleichen), kommt PHP in diesem Fall eigentlich nicht in Frage. Nun habe ich mir überlegt, C# zu verwenden. Ein Webservices kommt aber auch nicht in Frage, da dieser, so wie ich Webservices verstehe, nach jedem Seitenaufruf alle Daten vergiss (wie PHP, mal von Sessions abgesehen).

Wenn schon C#, dann eine richige Applikation. Soll heißen, auf den Servern läuft ein ganz normales Programm, d.h., das "vergisst" seine Daten nie (nur, wenn das Programm geschlossen wird). Soweit so gut. Nur wie greife ich dann am besten per Webbrowser (IE und FireFox) auf den Server zu? Wie erstellt dieser die HTML-Seiten? Was ist da der beste Ansatz?

Ich habe schon viel dazu gegoogelt, aber nachdem ich nicht genau weiß, nach welchem Stichwort ich suchen muss, gestaltet sich das etwas schwierig 😉 Ist denn Ajax.NET so etwas in der Art, was ich brauche?

Also nochmal zusammenfassend: Ich will kein PHP verwenden, sondern ein normales C#-Programm, das über Intra-/Internet mit dem gleichen Programm auf anderen Servern kommunizieren kann, ansonsten aber nur über Webbrowser drauf zugegriffen wird.

Danke schon mal für Eure Hilfe!

  • Expandable