Laden...
Avatar #avatar-2724.gif
andreas-82 myCSharp.de - Member
Anwendungsentwickler Bocholt Dabei seit 12.04.2006 42 Beiträge
Benutzerbeschreibung
There's no knowledge that is not power

Forenbeiträge von andreas-82 Ingesamt 42 Beiträge

07.01.2011 - 18:22 Uhr

Danke schon mal für eure Antworten.

Vll hilft dir das auch etwas bei der Performance (Lazy Loading)
Klick Mich Lazy Loading steht auf false.
In meinem (Lern)Beispiel wollte ich mich mit eager loading auseinandersetzen.
...so allgemein formuliert ist es nicht richtig. Wenn er Objektgraphen mit Include() erzeugen will muß das Root-Entity vollständig sein. War zumindest in EF1 so. Wenn er nur "Daten aus einer einzelnen Tabelle" benötigt kann er mit anonymen Objekten / EntityDataReader arbeiten.

Das ist ja das was mich wundert, dass sobald ich ein include setze, dass Root-Entity vollständig zurückgegeben wird.
Wenn ich anstelle des Includes ein Nested Querie verwende, werden auch im Root-Entity nur die im Select-Block angegebenen Spalten ausgelesen:

var result = from c in context.Contacts.Include("Addresses")
                     select new { c.LastName,
                                  c.ContactID, 
                                  City = ((from a in c.Addresses 
                                           where a.ContactID == c.ContactID 
                                           select a.City).FirstOrDefault()) };

SQL-Profiler:

SELECT 
[Extent1].[ContactID] AS [ContactID], 
[Extent1].[LastName] AS [LastName], 
(SELECT TOP (1) 
	[Extent2].[City] AS [City]
	FROM [dbo].[Address] AS [Extent2]
	WHERE ([Extent1].[ContactID] = [Extent2].[ContactID]) AND ([Extent2].[ContactID] = [Extent1].[ContactID])) AS [C1]
FROM [dbo].[Contact] AS [Extent1]

Ich habe in einer App alle 5000 Patienten aller Stationen aller Krankenhäuser eines Trägers geladen, das Übertragen der Daten und deren Materialisierung hat 2 sec gedauert. Ich würde vorschlagen dass Du mit Deiner Lösung weitermachst und erst optimierst wenn Probleme auftauchen. Prinzipiell hast du recht - auch wenn ich alles immer so perfekt wie möglich machen möchte - ich denke die Geschwindigkeitsunterschiede wird man auch bei mehreren Datensätzen nicht merken.
Wundern tut mich die Sache jedoch trotzdem noch 😭

06.01.2011 - 19:54 Uhr

verwendetes Datenbanksystem: EntityFramework 4 / SqlServer2008

Hallo zusammen!
Ich arbeite mich gerade in Linq-Abfragen gegen ein EF4-Model ein und bin
beim Untersuchen verschiedener Queries mit dem Sql Profiler auf eine Frage gestoßen:

Es geht um eine einfache Kontakte - Adressen - Abfrage.
Die Frage ist, wenn ich in der Abfrage per 'Include' die Adressen mitlade, warum kann ich dann nicht über ein Select nur bestimmt Spalten des Contact-Entities laden? Sobald Include verwendet wird, werden immer auch alle Spalten vom Contact-Entity geladen

var result = from c in context.Contacts.Include("Addresses")
                     select new { c.LastName, c.ContactID, c.Addresses};

SQL-Profiler:

SELECT 
[Project1].[ContactID] AS [ContactID], 
[Project1].[LastName] AS [LastName], 
[Project1].[FirstName] AS [FirstName], 
[Project1].[Title] AS [Title], 
[Project1].[AddDate] AS [AddDate], 
[Project1].[ModifiedDate] AS [ModifiedDate], 
[Project1].[C1] AS [C1], 
[Project1].[addressID] AS [addressID], 
[Project1].[Street1] AS [Street1], 
[Project1].[Street2] AS [Street2], 
[Project1].[City] AS [City], 
[Project1].[StateProvince] AS [StateProvince], 
[Project1].[CountryRegion] AS [CountryRegion], 
[Project1].[PostalCode] AS [PostalCode], 
[Project1].[AddressType] AS [AddressType], 
[Project1].[ContactID1] AS [ContactID1], 
[Project1].[ModifiedDate1] AS [ModifiedDate1]
FROM ( SELECT 
	[Extent1].[ContactID] AS [ContactID], 
	[Extent1].[FirstName] AS [FirstName], 
	[Extent1].[LastName] AS [LastName], 
	[Extent1].[Title] AS [Title], 
	[Extent1].[AddDate] AS [AddDate], 
	[Extent1].[ModifiedDate] AS [ModifiedDate], 
	[Extent2].[addressID] AS [addressID], 
	[Extent2].[Street1] AS [Street1], 
	[Extent2].[Street2] AS [Street2], 
	[Extent2].[City] AS [City], 
	[Extent2].[StateProvince] AS [StateProvince], 
	[Extent2].[CountryRegion] AS [CountryRegion], 
	[Extent2].[PostalCode] AS [PostalCode], 
	[Extent2].[AddressType] AS [AddressType], 
	[Extent2].[ContactID] AS [ContactID1], 
	[Extent2].[ModifiedDate] AS [ModifiedDate1], 
	CASE WHEN ([Extent2].[addressID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
	FROM  [dbo].[Contact] AS [Extent1]
	LEFT OUTER JOIN [dbo].[Address] AS [Extent2] ON [Extent1].[ContactID] = [Extent2].[ContactID]
)  AS [Project1]
ORDER BY [Project1].[ContactID] ASC, [Project1].[C1] ASC

Macht sich das in der Performance nicht ab einer bestimmten Zeilen- und Spaltenanzahl bemerkbar, wenn einfach immer alle Spalten geladen werden?
Lässt sich dies mit Include überhaupt einschränken?
Ist es hier alternativ besser ein Nested-Query zu verwenden?

Vielen Dank schon mal für Ideen 😃

25.08.2009 - 21:35 Uhr

Hallo dN!3L,
ich möchte mich auch recht herzlich für diesen wirklich tollen Artikel bedanken 👍
Hab mich die letzte Stunde mal intensiv deinem Artikel und meinem VS gewidmet.
Echt gut nachvollziehbar und super geschrieben 🙂

In Sachen Linq, Lambda-Expressions und anonymen delegates ist mir jetzt endlich mal ein Licht aufgegangen 🙂

Einen schönen Abend noch und weiter so 👍

13.02.2009 - 19:39 Uhr

Noch ein Tipp:

Einen der überladenen Konstruktoren bei Invalidate() benutzen, wenn möglich, damit nicht die gesamte Zeichenfläche jedes Mal neugezeichnet wird:

PicBox.Invalidate(Rectangle);

13.02.2009 - 19:27 Uhr

Hab mal dieses Snippet benutzt.
Funzt gut!

    public static Image SetOpacity(Image original, float opacity)
    {
      Bitmap temp = new Bitmap(original.Width, original.Height);
      Graphics g = Graphics.FromImage(temp);
      System.Drawing.Imaging.ColorMatrix cm = new System.Drawing.Imaging.ColorMatrix();
      cm.Matrix33 = opacity;

      System.Drawing.Imaging.ImageAttributes ia = new System.Drawing.Imaging.ImageAttributes();
      ia.SetColorMatrix(cm, System.Drawing.Imaging.ColorMatrixFlag.Default, System.Drawing.Imaging.ColorAdjustType.Bitmap);
      g.DrawImage(original, new Rectangle(0, 0, temp.Width, temp.Height), 0, 0, original.Width, original.Height, GraphicsUnit.Pixel, ia);
      g.Dispose();
      return temp;
    }

(Quelle: http://dotnet-snippets.de)

13.02.2009 - 19:20 Uhr

bei GDI musst du m.E. die Pixel neu zeichnen, wenn sie ihr Farbe ändern sollen.

Die Meinung kann ich nur Teilen.
Bevor du jedoch auf die Idee kommst die GetPixel(); SetPixel(); Methoden zu benutzen (viiiiiel zu lahm), solltest dir vielleicht den Link mal anschauen:

Bitmap-Manipulation (MemBitmap)

13.02.2009 - 19:07 Uhr

Ich würde auf jeden Fall zur GDI raten, Controls zu animieren ist wirklich nicht gerade der elegante Weg.
Das objektorientierte Programmieren gehört aber nunmal dazu (und eigentlich zu allem was irgendwie .net ist).

Von daher kann ich auch die von V0ruhm_Lu$€r 87 und JAck30lena geposten Links empfehlen:
http://openbook.galileocomputing.de/csharp/
[Artikel] Flackernde Controls und flackerndes Zeichnen vermeiden
(inkl. Links des ersten Beitrages).

PS. Irgendwie hat es mich nicht halten können und ich hab ein kleines Beispiel als Anregung erstellt, wie man sowas in der Art lösen könnte:

Eine Art klassischer Bildschirmschoner:
3 Bälle oder was auch immer, die immer an den Kanten abprallen.

Vielleicht läuft das ganze noch flüssiger, wenn man Integer-Werte benutzen würde, ist aber meiner Meinung nach schon recht flüssig.
Und man sollte das ganze über ne generische List<Ballon> laufen lassen, aber ist ja nur nen Beispiel.

Klasse Ballon

    public class Ballon
    {
      private float _x = 0f;
      private float _y = 0f;
      private float _width = 40f;
      private float _height = 40f;
      private float _movDirectionX;
      private float _movDirectionY;
      private float _speed = 3f;
      private Pen _penForeground = new Pen(Color.Black, 2f);
      private SolidBrush _brushBackground = new SolidBrush(Color.Red);

      public float Speed
      {
        get { return _speed; }
      }

      public float MovDirectionX
      {
        get { return _movDirectionX; }
        set { _movDirectionX = value; }
      }

      public float MovDirectionY
      {
        get { return _movDirectionY; }
        set { _movDirectionY = value; }
      }

      public float X
      {
        get { return _x; }
      }

      public float Y
      {
        get { return _y; }
      }

      public float Width
      {
        get { return _width; }
      }

      public float Height
      {
        get { return _height; }
      }

      // Das Umschließende Rechteck zurückgeben, damit nicht die
      // ganze PictureBox neugezeichnet werden soll.
      // Wegen der Rahmenstärke wird das Rechteck um 2px erweitert
      public RectangleF Bounds
      {
        get
        {
          RectangleF recCurr = new RectangleF(_x, _y, _width, _height);
          recCurr.Inflate(3f, 3f);
          return recCurr;
        }
      }

      // Konstruktoren
      public Ballon()
      {
      }

      public Ballon(float StartX, float StartY, SolidBrush Background, float Speed)
      {
        _x = StartX;
        _y = StartY;
        _brushBackground = Background;
        _speed = Speed;
        _movDirectionX = Speed;
        _movDirectionY = Speed;
      }

      /// <summary>
      /// Bewegt das Objekt um die übergebenen Koordinaten
      /// </summary>
      /// <param name="x"></param>
      /// <param name="y"></param>
      public void Move()
      {
        this._x += _movDirectionX;
        this._y += _movDirectionY;
      }

      /// <summary>
      /// Zeichnet das Objekt auf das übergebene Graphics-Objekt
      /// </summary>
      /// <param name="g"></param>
      public void Paint(Graphics g)
      {
        // Vereinfachter Ballon -> Ellipse ;)
        g.FillEllipse(_brushBackground, new RectangleF(_x, _y, _width, _height));
        g.DrawEllipse(_penForeground, new RectangleF(_x, _y, _width, _height));
      }
    }

Beispiel Form (kann in eine leere Form kopiert werden, da die Controls alle zur Laufzeit erzeugt werden. Die Klasse oben muss aber auch noch rein).

  public partial class Form1 : Form
  {
    //+---------------------------------------------------------------------------
    //| Eigenschaften
    //+---------------------------------------------------------------------------
    private Timer _timer;
    private Ballon _ballon1;
    private Ballon _ballon2;
    private Ballon _ballon3;
    private PictureBox _pbx;


    //+---------------------------------------------------------------------------
    //| Konstruktor
    //+---------------------------------------------------------------------------
    public Form1()
    {
      InitializeComponent();

      this.Width = 840;
      this.Height = 655;

      // PictureBox erstellen
      _pbx = new PictureBox()
      {
        Width = 800,
        Height = 600,
        Location = new Point(10, 10),
        BorderStyle = BorderStyle.FixedSingle,
        BackColor = Color.PeachPuff        
      };

      _pbx.Paint += new PaintEventHandler(pbx_Paint); // Paint EventHandler
      this.Controls.Add(_pbx);

      // Timer initialisieren
      _timer = new Timer()
      {
        Enabled = true,
        Interval = 30
      };
      _timer.Tick += new EventHandler(_timer_Tick);

      // 3 Ballons referenzieren
      _ballon1 = new Ballon(0f, 0f, new SolidBrush(Color.Red), 5f);
      _ballon2 = new Ballon(450f, 240f, new SolidBrush(Color.Blue), 8.4f);
      _ballon3 = new Ballon(320f, 520f, new SolidBrush(Color.Yellow), 6f);
    }


    void pbx_Paint(object sender, PaintEventArgs e)
    {
      e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
      // Immer im Paint-Ereignis zeichnen!
      _ballon1.Paint(e.Graphics);
      _ballon2.Paint(e.Graphics);
      _ballon3.Paint(e.Graphics);
    }


    void _timer_Tick(object sender, EventArgs e)
    {
      // Den Bereich in dem die Ballons WAREN neuzeichnen
      _pbx.Invalidate(Rectangle.Round(_ballon1.Bounds));
      _pbx.Invalidate(Rectangle.Round(_ballon2.Bounds));
      _pbx.Invalidate(Rectangle.Round(_ballon3.Bounds));

      MovingDirection(_ballon1);
      MovingDirection(_ballon2);
      MovingDirection(_ballon3);

      // Ballons bewegen
      _ballon1.Move();
      _ballon2.Move();
      _ballon3.Move();

      // Den Bereich in dem die Ballons jetzt SIND neuzeichnen
      _pbx.Invalidate(Rectangle.Round(_ballon1.Bounds));
      _pbx.Invalidate(Rectangle.Round(_ballon2.Bounds));
      _pbx.Invalidate(Rectangle.Round(_ballon3.Bounds));
    }

    // Bestimmt die Richtung, in die Bewegt wird
    private void MovingDirection(Ballon Ballon)
    {
      if (Ballon.X + Ballon.Width >= _pbx.Width) Ballon.MovDirectionX = -Ballon.Speed;
      if (Ballon.X <= 0) Ballon.MovDirectionX = Ballon.Speed;
      if (Ballon.Y + Ballon.Height >= _pbx.Height) Ballon.MovDirectionY = -Ballon.Speed;
      if (Ballon.Y <= 0) Ballon.MovDirectionY = Ballon.Speed;
    }
}
12.02.2009 - 17:36 Uhr

N'Abend!
Folgendes Szenario:

  • Typisiertes DS (Kochrezepte).
  • Tabelle Zutat und Tabelle Einheit
  • Die Zutat besitzt einen Fremdschlüssel auf Einheit (Standardeinheit: Gramm, mL, etc.)
  • DataRelation mit Fremdschlüsseleinränkung dazwischen

Funktioniert an sich super, mir ist nur etwas (für mich) komisches aufgefallen:
Wenn ich mein (leeres) DataSet über die Merge()-Methode zuerst mit der Zutatentabelle fülle, obwohl die Fremdschlüssel ja ins Leere verweisen, weil die Tabelle Einheiten noch nicht im DS ist, wird keine Ausnahme geworfen.

Ändere ich aber direkt danach manuell im DataGrid einen Schlüssel, bekomme ich sofort eine InvalidConstraintException, wie es eigentlich sein sollte 🤔

Ist das gewollt, das eine über Merge() gefüllte Tabelle keine Constraints beachtet, oder hab ich da irgendwo eine Eigenschaft oder sowas übersehen?
Ps. EnforceConstraints des DS ist auf true.

10.02.2009 - 18:13 Uhr

Du hast das bool vergessen^^
Ausserdem würde ich lieber public bool IstAmSchneien {get; set; } schreiben

Hoppla!
Naja wenn schon dann:
public bool IstAmSchneien {get; }

Bin doch kein Wettergott 😉

Naja genug OFF-Topic 😁

10.02.2009 - 17:27 Uhr

Zitat von andreas-82:
C#-Code:
public IstAmSchneien = true;

Hier schneits gerade 😉

Ich hätte auch schreiben können

object IchBinEineErweiterungDesDataSets;

😁

10.02.2009 - 17:19 Uhr
class meinDS : DataSet
    {
      public IstAmSchneien = true;
    }

?(

10.02.2009 - 17:09 Uhr

Du hast das mit dem "By Value" noch immer nicht richtig verstanden. Das DataSet wird nicht im Speicher kopiert, wenn Du es "By Value" übergibst. Es wird nur die Referenz auf den Speicherbereich kopiert.

Versteh mich nicht falsch, ich wollte schon gerade ein Bsp. mit nem String hier posten, der einer Methode übergeben wird per value um zu zeigen, das dieser eine Kopie ist.
Vorher aber zum Glück noch dasselbe Bsp. mit nem DS, bzw. ner Klasse gemacht:
Man ist ja klar, das einzige was ne Kopie ist, ist die Objektreferenz.
Man wie peinlich X( - Amateur!

... räusper ... Schwamm drüber, das macht die ganze Sache auf jeden Fall wesentlich einfacher G

AccecptChanges musst Du eigentlich nie aufrufen, da das bereits der DataAdapter oder TableAdapter für Dich tut.

🙂 ...und zwar hab ich da auch in meiner BL was von, wie ich jetzt weiß G

Zitat von andreas-82:
'Kommunikation' der Schichten über das RowUpdated() Event?

Auf keinen Fall! BL und DAL sollten statuslos sein. Das DataSet wird vom Client bzw. der Präsentationsschicht gehalten und z.B. zum Speichern/Buchen etc. an die BL übergeben. Wenn Daten abgerufen werden, geben die entsprechenden Funktionen der BL ja DataSets/DataTables zurück.

Sehr schön, das kann ich mir dann auf jeden Fall merken:

  • Kein call by reference
  • Statuslosigkeit der BL und DAL

Danke nochmal für die Aufklärung 🙂

09.02.2009 - 19:16 Uhr

Ich bin leider nicht der XML-Profi, aber wir hatten ein ähnliches Problem bei der Serialisierung/Deserialisierung in einem Schulprojekt.

Das Ende vom Lied war, dass wir die Steuerung der Seriealisierung selbst in die Hand nehmen mussten.
Dazu muss deine zu serialisierende Klasse das Interface IXmlSerializable implementieren.
Das sind die drei Methoden GetSchema() [brauchste nicht], ReadXML(XmlReader) und WriteXML(XmlWriter)

Über die Methoden des XmlReader und XmlWriter kannst du genau programmieren, welche Daten deiner Klasse im Einzelnen serialisiert und deserialisiert werden sollen.
Deine Attribute wie [XmlIgnore] etc. benötigst du dann nicht, da du explizit sagts, was und wie serialisiert/deserialisiert werden soll.

BSP:


public void WriteXml(System.Xml.XmlWriter writer)
{
      writer.WriteStartElement("this.FormularID");
      writer.WriteString(this.FormID.ToString());
      writer.WriteEndElement();
      writer.WriteStartElement("this.FormularName");
      writer.WriteString(this.FormularName);
      writer.WriteEndElement();
      writer.WriteStartElement("this.IstPersonalisiert");
      writer.WriteString(this.IstPersonalisiert.ToString());
      writer.WriteEndElement();
...
}

Deine Klasse kannst du dann ganz einfach dem XmlSerializer 'zu fressen' geben!

http://msdn.microsoft.com/de-de/library/system.xml.serialization.ixmlserializable(VS.80).aspx

09.02.2009 - 18:51 Uhr

Warum möchtest du denn deinen PK ändern?
Wie auch immer:

Ich würde sagen so lange die Schlüssel in Beziehung miteinander stehen kannst du diese gar nicht ändern...
Höchstens einem anderen bestehenden FK zuweisen, den PK der ersten Tabelle ändern (wenn sie nicht noch in Beziehung mit anderen Zeilen steht) und dann erst den FK entsprechen umsetzen.

09.02.2009 - 18:40 Uhr

N'abend allerseits!
Wow, danke schonmal direkt der starken Resonanz hier 🙂
Also der Reihe nach 👅:

@ErfinderDesRades

Also son DataAdapter checkt ja selber, was zu updaten, inserten, deleten ist. Da werde ich eine Deibel tun, das ganze je nach Rowstate und was wieder auseinanderzuklamüsern.
Ich hab mir son DatasetAdapter gebastelt. Der kriegt mein Highlander-Dataset ("es kann nur eines geben"), und speichert, was zu speichern ist, und das wars dann auch schon mittm DAL
...
...
Äh - es geht um Kochrezepte?
Unter welchen Umständen bekommt man da einzelne Tabellen aus der DAL ins BL?
Geht sowas überhaupt - die Dinger sind doch mittnander verknüppert?

Hmm, ok irgendwie ist da was dran... also 'Gedanken-Planänderung':
-> Eine Zentrale Methode in dem DAL in der ich einfach mein DS reinwerfe quasi.
Welche Tables bzw. Rows eine Änderung besitzen, weiß das DS ja selber (RowState). Dazu weiter unten mehr...

Und ganz wichtiger Pattern ist auch: Kirche im Dorf lassen.

Wie gesagt, mir gehts hier in meiner Anwendung primär darum, mich in die Schichtentrennung und Datenklassen (richtig) einzudenken und umzusetzen. Ich stell mir z.B. vor, was wäre wenn ich jetzt 'zig Millionen' Datensätze gespeichert HÄTTE, etc. Da würd ich bspw. besser nicht einfach das gesamte DS auf einmal via SELECT * FROM aus der Datenbank laden 😉
Daher auch die Überlegung mit Merge()

Aber super-coolen Tabellen-Designer hasteda - was ist das, wo ist der her?

M@crotron hat's bereits 'verraten'. Das Zauberwort heißt SQL Server Management Studio 😉

Zum "richtigen und perfekten" Proggen ist mir noch was eingefallen:
Erstmal gefällt mir dein Datenmodell ausgezeichnet, würde bei mir strukturell dasselbe bei rauskommen.

Danke 🙂

Nur zusammengesetzte PrimKeys lehne ich für mich ab (also ist mir bisher gelungen, ohne auszukommen)...

... hat sich ja denke ich durch die wunderbare Erklärung von LaTino erübrigt, oder spricht sonst etwas gegeben zusammengesetzen Primärschlüssel? 🤔

Dann kann man noch anne Benamung rumbosseln: tbl-Prefix ist ziemlich ungünstig

Naja gut, sind wirklich meistens Tabellen drin, in so einer Datenbank 👅

@JuyJuka

Das wichtigste ist, dass möglichst wenig nach passendem Wort such "Steuerung" aus der Datenschicht in der Logikschicht landet.
Ich gebe einfach alles (DataRow, DataTable, DataSet, etc.) was ich speichern will nach "unten" und gut ist.
Da her sollte es so wenig Methoden und Objekte in der "Signatur" der Datenschicht geben wie möglich

Danke für die Erklärung 🙂
Ich werde mir in anderen Projekten meine BusinessObjects auch mal mit komplett eigenen Klassen zusammenbasteln. Dann kann ich Interfaces mit Sicherheit hervorragend einbauen!

Ich möchte noch darauf hinweisen, das DataTable/DataRow/DataSet nicht die einzige Möglichkeit sind. Ich persönlich würde einen O/R Mapper DataSet/~Table/~Row vorziehen, da (meiner Meinung nach) diese Klassen gegen das OOP-Grundprinzip der Kapselung verstossen. (Aber dazu kann man mit der Forumssuche einige Diskusionen finden ... :::

Irgendwie wusste ich das das kommt G
Ne im ernst, ich interessiere mich für alle möglichen Technologien und hab mir auch schon viele Threads (und Blogs 😉) dieser Art durchgelesen, nur bei einer Sache muss ich ja anfangen 👅
Wenn mein Verständniss um der Umgang mit ADO.net kein Problem mehr ist, widme ich mich mal NHibernate und/oder seinen Geschwistern 🙂

@Rainbird

Übergabe "By Reference" ist nicht üblich und sollte auch nicht zum Einsatz kommen... Das kann zu unbeabsichtigtem Verhalten führen. Wenn z.B. in der Aufgerufenen Funktion eine "By Reference" übergebene DataSet-Variable auf null gesetzt wird, ist die Variable im Gültigkeitsbereich des Aufrufers ebenfalls null, da beide die Selbe Referenz verwenden

Deine Erklärung der Nachteile von 'by reference' sind mir (jetzt) auch schlüssig. Nur viel mir spontan kein besserer Weg ein.

Aber mal sehen ob ich lansam auf den richtigen Weg komme:
Die DataRows werden doch z.B. im DAL auf Veränderungen überprüft (RowState) und bei erfolgreichem Update() die AcceptChanges() aufgerufen, richtig?
Da ich aber eine Kopie meines DataSet-Objektes (by value) übergebe, muss ich nun die AcceptChanges() - Methode selber aufrufen.
Ich würde eine Methode benutzen, die mir die Anzahl der geänderten Zeilen zurückgibt. Diese werden dann mit den zu ändernden Zeilen des DataSets der BL verglichen und bei Erfolg ein AcceptChanges() aufgerufen.
Bei Mißerfolg - hmm 🤔
'Kommunikation' der Schichten über das RowUpdated() Event?

Generell hätte ich also zum Programmstart 1 DataSet, in welches ich mir zunächst nur Zutaten, Einheiten und Kategorien komplett reinlade.
Die Daten aus tblRezept und der Verknüpfungstabelle tblZutatRezept KÖNNTE man doch nach und nach dazuladen, also nur jedes Mal, wenn aus einer Rezeptübersicht ein Rezept komplett angezeigt werden soll, oder? (Stickwort: Merge()). Hierzu die Tabelle besser vorher leeren?

Also immer her mit den Verbesserungsvorschlägen! 🙂

Ps. Die Sache mit den verteilten Transaktionen und System.Transactions muss ich etwas nach hinten verschieben. Hab da bisher noch nie was mit gemacht, steht aber in meinem ado.net-Buch was von drin.

08.02.2009 - 16:12 Uhr

verwendetes Datenbanksystem: SQLServer2005

Tagchen,
ich versuche mich gerade in der Aufteilung einer Datenanwendung in 3 Schichten und möchte den absolut richtigen Weg einschlagen (im Bezug auf DataSets).
Es geht in meinem Übungsbeispiel um ein Koch-Rezept-Programm (Freundin freut sich 😉)

Ein typisiertes DataSet mit Fremdschlüsseleinschränkungen etc. (ohne TableAdapter) stellt den 'DatenContainer' dar.

Die Datenschicht soll gegen ein Interface programmiert werden, in etwa so:


    int AktualisiereZutaten(ref DSKochbuch.tblZutatDataTable tblZutat);
    int AktualisiereEinheit(ref DSKochbuch.tblEinheitDataTable tblEinheit);
    int AktualisiereKategorie(ref DSKochbuch.tblKategorieDataTable tblKategorie);
    int AktualisiereRezept(ref DSKochbuch dsRezept);
    ...

Zu meinen Fragen:

  1. Bei Aktualisierungs-, Insert- oder Löschmethoden in der DAL:
    Ist es gängig ein DataSet oder DataTable, in denen die entsprechende 'RowStates' geändert sind, aus der BL der DAL per Reference zu übergeben und dort per DataAdapter bearbeiten zu lassen? Oder lieber 'normal' übergeben, die Anzahl der aktualisierten Zeilen zurückzugeben und AcceptChanges() manuell aufrufen (nicht so komfortabel irgendwie).

  2. Welcher Weg wäre am besten um nur einen Teil meines DataSets in der BL zu aktualisieren:
    Wenn ich z.B. über eine Methode aus der DAL eine DataTable bekommen würde. Dann müsste ich ja aus dem DataSet ein DataTable.Clear() aufrufen und die neue Table mit DataTable.Merge() in mein bestehendes DS einfügen (nicht sonderlich elegant).
    Oder auch hier lieber das DataTable-Objekt oder gleich das ganze DS per Reference einer Aktualisierungsmethode übergeben und mit dem DataAdapter 'aktualisieren lassen' ?

Würde mich über eure Meinung und Erfahrungen sehr freuen!
Solche 'trivialen Dinge' lassen mich nämlich machmal nicht los.
Ich sag mir immer, wenn ich schon programmiere, dann wenigstens sauber =)

18.11.2008 - 16:27 Uhr

Die Frage ist, wie kann solch eine Anwendung am schönsten Aufteilen?
In meinem ersten Ansatz habe ich mich für eine Benutzung von DataSets entschieden, weil dadurch das Handling in der UI erleichtert wird
(bitte nicht schreien, hab ein wenig UML-Mischen gespielt)

(Bild siehe Anhang)

In dem Kasten sind in einem getrennten Assembly meine typisierten DataSets die als DatanContainer dienen.

++Nun habe ich leider ein paar offene Fragen zu solch einer Struktur++1. Schnittstelle zur DataAccessLogic (IDalAccess): Wie kann ich meinen Datenrückweg, also den Schreibvorgang, am besten lösen? Für jede Tabelle eine separate Methode? Oder das ganze DS übergeben (nicht ellegant oder)?

  1. BusinessLogic so in der Art Ok (mit den Speichermethoden pro Tabelle)?
    Wie kann die m:n - Beziehung tblZutatRezept am besten gehandelt werden?

  2. An welcher Stelle erfolgt/erfolgen die Validierung(en) der Datenwerte?

  3. Mir ist gerade aufgefallen, ich habe gar keine BusinessObjekte. Sind diese in meinem Fall überflüssig, bzw. die DataSets ein Ersatz dafür?

  4. Wie würdet ihr das gegebene Bsp. (besser) aufteilen?

Bin irgendwie von Möglichkeiten überschüttet und sehe den richtigen Weg nicht 🤔
Würde mich deshalb sehr über Vorschläge freuen! =)

18.11.2008 - 16:26 Uhr

Hi, ich hab mir die Tage mal einige Beiträge bezüglich der Aufteilung in 3 Schichten (auch im Zusammenhang mit DataSets) durchgelesen und möchte meine Anwendungen natürlich möglichst gut trennen.

Was die DataSets dabei angeht, gibts ja eine ganze Menge verschiedenster Meinungen (Rainbird: Pro, JuyJuka: Contra, etc.).
Die Beiträge waren echt interessant. Genauso wird der Blog von Rainbird:
http://yellow-rainbird.de/blogs/rainbird/archive/2008/03/18/ich-m-246-chte-f-252-r-typisierte-datasets-eine-lanze-brechen.aspx

Jetzt möchte ich gerne wissen, ob ich es halbwegs verstanden habe.

Das Beispiel:
So, nicht lachen: Es handelt es sich um eine simple Datenstruktur für ein Kochbuch:

(DB-Diagramm siehe angehängtes Bild)

07.10.2008 - 14:49 Uhr

witzig: manchmal wundert man sich schon, was man anno dazumal geschrieben hat großes Grinsen

-> soll nicht heißen, dass das falsch ist, nur erinnern konnte ich mich daran nicht mehr 😉){gray}

Naja, 30.10.2007 ist schon fast ein Jahr hin, und bei fast 4000Beiträgen könnte ich mir auch nicht alles merken. 😉

Aber lass mich das mal ruhig als Lösung ansehen, sonst kann ich heute Nacht nicht schlafen 😁
Muss endlich mal was geschafft bekommen.
Hab die Woche Urlaub und das Ding ist Bestandteil von nem Abschlussprojekt von ner Abendschule Informatik... quäl mich seit gestern mit der Pixelzählerei da rum G

07.10.2008 - 14:35 Uhr

Hallo norman_timo,
Danke erstmal für deinen Link, ich hab die Threads und den Artikel 'BorderBug' vom Codeprojekt mal durchgelesen, wobei es mir hier weniger um Image-Resizing geht.
(http://www.codeproject.com/KB/graphics/BorderBug.aspx)

Ich halte mich mal einfach an ein Zitat von dir und betrachste das einfach als meine Lösung:

GDI+ wird bei Floating-Werten im Zusammenhang mit Pixelpositionen anfangen zu interpolieren (das ist der schon angesprochene Durchscheineffekt).

Das kann ich jetzt nicht direkt mit einer Quelle belegen, habe es aber selbst einmal evaluiert. Falls Du gestochen scharfe Linien/Bilder zeichnen willst, wäre es am sinnvolsten die Positionen selbst vorab zu runden.

Anders kann ich mir das Verhalten meines Designers echt nicht erklären... 🤔

07.10.2008 - 13:49 Uhr

setze den zu invalidierenden bereich immer genau einen pixel breiter und höher als er laut berechnung eigendlich benötigt wird.

Ist er doch schon (siehe blauen Bereich).
Das ist die List<Rectangle> die Invalidiert wird.

Genau am unteren Bereich ziehts mir die Striche weg, auch wenn ich noch 10Pixel dranhänge...

(muss doch nicht genau ein Pixel mehr sein, oder ?)

07.10.2008 - 13:35 Uhr

Hi JAck30lena,

der cast gegen int hackt den nachkommaanteil einfach ab. so wird aus einer 1.99999 eine 1

wenn du den float-wert übergibst, rundet er zum nächstgelegenen pixel und das wäre im oberen beispiel die 2

Das mit dem Nachkommaanteil abhacken stimmt schon.

Nur ein Pixel Versatz kann diesen Effekt nicht aufrufen.

In Abb.1 hab ich den Bounds-Bereich, der per Invalidate(bounds) aktualisiert wird mal blau mitzeichnen lassen. Ich hab extra unten am Frageelement mehr Platz gelassen.
Trotzdem kann man mit dem unteren Teil die horizontalen Striche vom Zahlenblock 'wegwischen', wenn man ihn senkrecht nach oben zieht und der Float-Konstruktor benutzt wird.

Und ich hab in Abb.2 mal einen Test gemacht: Hier rundet die GDI auf X und Y Achse unterschiedlich ?!??
Da müsste doch eigentlich das äquivalente Ergebnis gezeichnet werden oder sehe ich das falsch?

Komische GDI

Ps. Sorry wg. wenig aussagekräftigen Titel vorhin

07.10.2008 - 10:03 Uhr

Morgen @all!

Sitze hier jetzt schon mehrere Stunden (gestern auch schon) und zweifele schon fast an meinem Verstand.

Es geht um einen Formulardesigner und ein Problem beim Verschieben der Objekte (siehe Anhang).

Beim Verschieben des Fragenelementes nach oben sind immer (nur) die horizontalen Linien bei den Zahlenblöcken verschwunden.
Die Bounds-Bereiche der beiden Elemente die ich vor und nach dem Verschieben Invalidieren lassen sind 100%ig richtig.

Jetzt hab ich den Fehler beheben können und bin etwas baff.
Und zwar liegt es irgendwie am überladenen Konstruktor von DrawLine ob ich entweder die int- oder die float-Werte übergebe.

Meine Frage:
Sind beide Codeausschnitte äquivalent, bzw. müsste die GDI nicht in beiden Fällen dasselbe Ergebnis ausgeben?
Rundet die Funktion intern anders?

Vor allem ist die Ausgabe auch wirklich Pixelgenau gleich.
Nur beim verschieben bleiben mit unterem Code die Horiz. Striche der Zahlenblöcke bestehen


       float offset = _penBlock.Width / 2;
       .
       .
        g.DrawLine(_penBlock,
          ZahlenblockX - offset,
          ZahlenblockY - offset,
          ZahlenblockX + (3 * (_blockBreite + _penBlock.Width)) - offset,
          ZahlenblockY - offset);

       float offset = _penBlock.Width / 2;
       .
       .        
       g.DrawLine(_penBlock,
          (int)(ZahlenblockX - offset),
          (int)(ZahlenblockY - offset),
          (int)(ZahlenblockX + (3 * (_blockBreite + _penBlock.Width)) - offset),
          (int)(ZahlenblockY - offset));

Ps. Hab noch das hier gefunden:
http://www.tech-archive.net/Archive/Development/microsoft.public.win32.programmer.gdi/2004-08/0350.html

Bei mir ähnlich, mit Antialising gibts auch keine Probleme

03.10.2008 - 15:02 Uhr

Ihr habt mir beide sehr geholfen, System.Diagnostics.Debug.Write() war die Methode die ich gesucht habe. 🙂

02.10.2008 - 18:21 Uhr

Eventuell läßt sich auch der 'Intelligente Hintergrundübertragungsdienst' (Ab XP) von Microsoft nutzen, der auch für die Windows-Updates zuständig ist.

Vielleicht bringt dich das weiter.

http://www.simple-talk.com/dotnet/.net-tools/using-bits-to-upload-files-with-.net/

02.10.2008 - 18:00 Uhr

Tagchen!
Ich bin mir nicht sicher, aber gabs im VS nicht die Möglichkeit sich zu Debug-Zwecken Ausgaben in eine Art Konsole zu laden?

Ich bin leider bei der Suche nicht fündig geworden... X(

02.10.2008 - 17:28 Uhr

Sprich einen Server entwickeln, welcher bei uns im Büro läuft, und auf Port 80 lauscht

Ich weiß ja nicht um was für eine Art Daten es sich handelt, aber hier könnte man doch prima mit nem SQL-Server oder MySQL-Server arbeiten?

Dein Programm ruft dann regelmäßig die Daten vom Server ab... =)

19.09.2008 - 15:44 Uhr

Hey, witzig 😁
Kannte ich auch noch nicht!

19.09.2008 - 15:20 Uhr

Hi,
danke vielmals für eure Antworten 🙂

Suchst du vielleicht:
...
int index = kontoListe.IndexOf(zuErsetzendesKonto);
...

Ganz genau das hab ich gesucht! Danke! =)
Konnte mir irgendwie nicht vorstellen die es die Indexsuche eines Listenelementes anhand einer vorhandenen Referenz auf das zu suchende Objekt nicht schon fertig gibt... hätte ich auch selber finden müssen 😜

PS: Verwende Properties statt öffentlicher Membervariablen.

Mach ich doch sonst, ist doch nur ein Bsp.code gewesen 😭

@herbivore, schöne Erklärung, danke 🙂

Für den den's interessiert, so hab ich's eingebaut:

void frmHauptMouseDown(object sender, MouseEventArgs e)
    {
      Designer.tmpMouseX = e.X;
      Designer.tmpMouseY = e.Y;

      //+-------------------------------------------------------------------------------------------
      //| Wenn ein Objekt getroffen wird, wird in 'VerschiebenKlick' dessen Referenz in das Feld
      //| Designer.MarkiertesObjekt übertragen um zu kennzeichnen,
      //| dass verschoben werden soll, solange 'MarkiertesObjekt!=null'
      //| Eine Referenz des getroffenen Objektes wird zurückgegeben.
      //+-------------------------------------------------------------------------------------------
       CBasisElem BasisElemGetroffen = Designer.VerschiebenKlick(e.X, e.Y);

      //+-------------------------------------------------------------------------------------------
      //| Spezialfall rechte Maustaste:
      //| Hier soll kein Verschieben möglich sein, sondern je nach Element das
      //| Eigenschaftenfenster geöffnet werden
      //+-------------------------------------------------------------------------------------------
       if (e.Button == MouseButtons.Right)
       {
         Designer.MarkiertesObjekt = null; // Verschieben beenden

         if (BasisElemGetroffen is CFrage)
         {
           // Das Eigenschaftenfenster wird referenziert und die getroffene Frage
           // übergeben, damit die aktuellen Werte in die Steuerelemente geladen werden
           // können.
           DLGNeueFrage tmpDialog = new DLGNeueFrage((CFrage)BasisElemGetroffen);
           tmpDialog.ShowDialog();
           if (tmpDialog.Frage != null) // Wird auf Speichern geklickt
           {
             int index = Designer.Formular.ListSeiten[0].ListBasisElems.IndexOf(BasisElemGetroffen);
             Designer.Formular.ListSeiten[0].ListBasisElems[index] = tmpDialog.Frage;
           }
         }

       }
    }
17.09.2008 - 22:29 Uhr

Ich glaub ich hab gerade ein Brett vorm Kopf oder so:
Eigentlich doch simple Sache:

Das Problem ist, über eine Referenz eines Objektes einer List<T>, diesem Listenobjekt ein anderes Objekt zuzuweisen.

Hier ein abstraktes Beispiel:


  class CKonto
  {
    public CKonto(int Guthaben)
    {
      this.Guthaben = Guthaben;
    }

    public int Guthaben = 10;
  }

List<CKonto> listKonten = new List<CKonto>();

    private void Form1_Load(object sender, EventArgs e)
    {
      listKonten.Add(new CKonto(20));
      listKonten.Add(new CKonto(30));
      listKonten.Add(new CKonto(14));
    }

    private void cmdRefVeraendern_Click(object sender, EventArgs e)
    {
      // Referenz aus Listeneintrag holen
      CKonto ReferenzKonto = listKonten[1];

      // Neues Objekt CKonto mit anderen Eigenschaften erstellen
      CKonto GanzNeuesKonto = new CKonto(4);

      // Hier möchte ich die Referenz aus der Liste mit meinem neuen
      // Objekt 'überschreiben'
      ReferenzKonto = GanzNeuesKonto; // Geht nicht

    }

Ich denke das der Referenz 'ReferenzKonto' so eine neue Referenz und zwar die von 'GanzNeuesKonto' zugewiesen wird.
Ich müsste hier (wenn ich ich nur die Referenz der Liste habe, nicht aber den Index) sonst alle Eigenschaften einzeln ersetzen, das würde gehen, macht aber keiner: 🤔


      ReferenzKonto.Guthaben = GanzNeuesKonto.Guthaben;

Vielleicht Fragt ihr euch 'warum so umständlich' 😁
Es geht um einen Fragebogendesigner (CFormular, darin List<CSeite>, darin List<CBasisElem> (abstrakt)).

Wenn ich im Designer auf meine Formularseite klicke, bekomme ich eine Referenz auf das Objekt das ich getroffen habe zurück.
Diese übergebe ich im Konstruktor einem Eigenschaften Dialog wo man die Objekteigenschaften verändern kann.
Zurück bekomme ich ein neues Objekt, welches das andere ersetzen soll.

04.02.2008 - 17:15 Uhr

für so eine Abfrage bitte nicht die Maske direkt in der Abfrage verwenden. Für so etwas gibt es zwei Properties: MaskCompleted und MaskFull. Hey danke @Fabian!
Das war genau das wonach ich gesucht hatte! 👅

Hab da wohl irgendwie Tomaten auf den Augen gehabt oder so 🤔

Habs auf jeden Fall jetzt so gelöst:

    private void txtRelease_TypeValidationCompleted(object sender, TypeValidationEventArgs e)
    {
      if (txtRelease.MaskCompleted)
      {
        if (!e.IsValidInput)
        {
          MessageBox.Show("Ungültige Eingabe!");
          txtRelease.ResetText();
          txtRelease.Focus();
        }
        else
        {
          tmpLied.ReleaseDatum = (DateTime)e.ReturnValue;
        }
      }
    }

Vielen Dank nochmals!

03.02.2008 - 19:37 Uhr

Hi erstmal,
ich glaub ich steh gerade wie nen Ochse vorm Berg, wobei der Berg eigentlich nicht allzu groß sein kann:

Also ganz Simpel, ich habe eine MaskedTextbox in der ich ein Datum validieren möchte.
Über das TypeValidationCompleted-Event überprüfe ich, ob ein Gültiges Datum eingegeben wurde:

    private void txtRelease_TypeValidationCompleted(object sender, TypeValidationEventArgs e)
    {
      if (!e.IsValidInput)
      {
        MessageBox.Show("Ungültige Eingabe!");
        txtRelease.ResetText();
        txtRelease.Focus();
      }
      else
      {
        tmpLied.ReleaseDatum = (DateTime)e.ReturnValue;  
      }
    }

Das Problem ist jetzt, dass das Event auch ausgelöst wird, wenn der Focus der Textbox verlassen wird, auch wenn nichts eingegeben wurde.
In diesem Fall soll gar nichts passieren.

Da muss es doch eine simple Möglichkeit geben ??
Irgendwas in der Richtung:


if (!e.IsValidInput && txtRelease != "")

Nur die txtRelease ist ja nicht leer, es steht ja die Maske .__ drin!

23.10.2007 - 16:58 Uhr

Hey jaensen,
DANKE für deine Antwort, hat mein Problem auf Anhieb gelöst, juhu! =) 👍

Du musst dir ein Panel ableiten, dort gibt es dann ein AutoscrollPosition property welches du allerdings (komischerweise) nur im OnPaint setzen kannst. Die koordinaten dafür sind allerdings auch ein bisschen verquert, musst du dir dann mal im Debugger anschauen.

Kann die Eigenschaft im Übrigen in meinem UserControl direkt setzen, auch ohne OnPaint-Ereignis 👅


this.AutoScrollPosition = new Point(0, 0);
this.AutoScrollPosition = new Point(0, 0);

22.10.2007 - 15:49 Uhr

Hat denn keiner ne Idee? 🙁

Oder vielleicht nen Befehl um die 'Scrollposition' wieder auf den Ursprungszustand zurückzusetzen um erst dann die Label zu erstellen damit sie auf der richtigen Position landen...?

18.10.2007 - 18:45 Uhr

Hallo herbivore.

Hatte ich zuerst auch daran gedacht, allerdings handelt es sich um Panels die zeilenweise mit Daten gefüllt aus einer Datenbank ausgelesen werden:

Siehe Anhang


...
      private const Int16 StartXPanel = 3;
      private const Int16 StartYPanel = 159;
...
// Panel erstellen in dem die Labels für jede Zeile geadded werden
        Panel pan = new Panel();
        pan.Width = BreitePanel;
        pan.Height = HoehePanel;
        pan.BorderStyle = BorderStyle.FixedSingle;
        pan.Name = "Zeile";
        //pan.Anchor = AnchorStyles.Left;
        //pan.Dock = DockStyle.Left;

        pan.BackColor = hintergrundfarbe;
        pan.Location = new Point(StartXPanel, StartYPanel + yPositionRelativ); //Startposition des Panel
...

Wenn ich nach rechts gescrollt die Daten aktualisiere, bzw. die Sortierung ändere, ist der ganze Rummel verschoben (siehe Bild).

Ein Anchor bringt keine Änderung und ein Dock 'Pappt' logischerweise das Panel mit voller Höhe direkt an die linke Seite 🙁

18.10.2007 - 17:23 Uhr

Sorry, besseres Topic ist mir nicht eingefallen.
Also es geht kurz und schmerzlos darum ein Steuerelement das zur Laufzeit erzeugt wird an eine bestimmte Position (Location) erscheinen zu lassen, selbst wenn die Form nach rechts gescrollt ist.

Warum werden immer die 'sichtbaren' Koordinaten und nicht die 'wirklichen' des Forms für die .Location genommen ?(

Hier ein Bsp:

    private void button1_Click(object sender, EventArgs e)
    {
      Label testLabel = new Label();
      testLabel.Text = "Test";
      testLabel.Location = new Point(10, 10);
      testLabel.Size = new Size(50, 15);
      this.Controls.Add(testLabel);
    }

Form nach Click (so weit, so gut):

Fenster verkleinert:

Click-> Label nicht absolut links oben sondern links oben im sichtbaren Bereich:

Nach vergrößern des Fensters das Dilemma:

18.10.2007 - 16:53 Uhr

Vielleicht über das 'GotFocus'-Event ?
Dann haste nur kein Event mehr wenn das Textfeld schon den Focus besitzt... 🤔

13.04.2006 - 17:18 Uhr

Nochmal eine kurze Form-Frage:

Also unser Lehrer hat gesagt, dass man den Code aus Klassen, mit dem Code eines Formulars trennen soll, damit man die Klasse z.B. einer Windows-Anwendung auch für eine ASP-Anwendung benutzen könnte.

Soweit klar, aber wenn ich z.B. ein simpeles Formular habe dessen Werte ich einer Methode einer Klasse übergebe,
wie läuft dann die Fehlerbehandlung, bzw. Berichterstattung für den User von statten, der fehlerhafte Werte einträgt?

Per "if(wertfalsch) - then MessageBox("Falscher Wert")" in der Klasse ist ja schlecht, weil ich dann keine Trennung habe.

Und try-catch soll ja anscheinend nur 'im Notfall' benutzt werden, wenn ich euch richtig Verstanden habe. ?(

Die Eingabeüberprüfung vor dem Methodenaufruf anstatt innendrin zu machen ist doch auch dämlich...

Danke schonmal für Antworten 8)

13.04.2006 - 16:06 Uhr

Danke nochmal für die Antworten 🙂

Mache das ganze jetzt per


if (Finanzen.FinanzenDataset.Tables["Auszug"]!=null)
{
//Hier DB-Abfragen rein
}

und einem Close(); im catch{}-Block, weil das Programm bei falscher Datenbank sowiso nicht arbeiten kann.

Hoffe das ist so legitim 😉

12.04.2006 - 22:54 Uhr

Also wo soll ich anfangen.

Erst vielleicht nochmal kurz zur Verdeutlichung:

Ausschnitt aus Klasse für die Connection:

/* Stellt die Verbindung zur Datenbank über einen Adapter her 
       * und füllt das globale Dataset 'FinanzenDataset':
       * ----------------------------------------------------------
       * Eingabewerte:
       * - SQL-String
       * - Name für den aufgerufenen Datensatz zur Identifizierung
       * 
       * Rückgabewert:
       * - OleDBConnection
       */
public class CFinanzen
  {
    //Attribute
    public DataSet FinanzenDataset = new DataSet();
    private const string DBPfad = "daten\\sf-daten.mdb";
    private const string DBProv = "Microsoft.Jet.OLEDB.4.0";
    
public OleDbConnection DBConnect(string SqlString, string DatasetTableName)
    {
      string DBConnectString = "PROVIDER=" + DBProv + ";DATA SOURCE=" + DBPfad;

      OleDbConnection DBConnect = new OleDbConnection(DBConnectString);

      try
      {
        OleDbDataAdapter DBAdapter = new OleDbDataAdapter(SqlString, DBConnect);
        DBAdapter.Fill(FinanzenDataset, DatasetTableName);
      }
      catch (OleDbException innerEx)
      {
        throw new Exception("Fehler im Datenbankaufruf!", innerEx);
      }
      return DBConnect;
    }

}

Klasse für Form_Load

private void DataGridFuellen()
      {
        System.Collections.ArrayList list = new System.Collections.ArrayList();

        try
        {
//HIER DER KLASSENAUFRUF!
          Finanzen.DBConnect("SELECT * FROM tblAuszug", "Auszug");
        }
        catch (Exception ex)
        {          MessageBox.Show(ex.InnerException.Message,ex.Message,MessageBoxButtons.OK,MessageBoxIcon.Error);
        //return; ???
        }

//DAS ALLES NUR MACHEN, WENN KEINE AUSNAHME AUFGETRETEN IST:

        DataTable AuszugTabelle = Finanzen.FinanzenDataset.Tables["Auszug"];
[...]
        int AnzahlEintraege = AuszugTabelle.Columns.Count;
        
[...]
        /* Schleife in der die Datenbank ausgelesen wird
         */
        for (int i = 0; i < AnzahlEintraege; i++)
        {
          /* Werte werden für jede Zeile 'i' ausgelesen
           */
          AuszugID = (Int32)AuszugTabelle.Rows[i]["AuszugID"];
          Datum = (DateTime)AuszugTabelle.Rows[i]["Datum"];
          Betrag = Convert.ToDouble(AuszugTabelle.Rows[i]["Betrag"]);
          Bemerkung = AuszugTabelle.Rows[i]["Bemerkung"].ToString();
          AktionKonto = (bool)AuszugTabelle.Rows[i]["AktionKonto"];
          AktionKnippe = (bool)AuszugTabelle.Rows[i]["AktionKnippe"];
[...]
          DTfuerDataGrid.Rows.Add(Datum, Bemerkung, Betrag, ZwischenbetragKonto, ZwischenbetragKnippe);
        }
        DataGrid.DataSource = DTfuerDataGrid;      
      }

@LaTino:

Na, das riecht nach unzureichender Kapselung

Was genau meinste damit?

Im wesentlichen hat die Klasse ganz aehnlich wie die SqlCommand-Klasse die Connection als Property. Beim setzen dieser Property wird alles denkbare durchgeprüft und eine andere Eigenschaft gesetzt, die eine Enumeration ist...

Gut, also prinzipiell bsp. vorher den Pfad der Datei abfragen, die Connection abfragen, etc.
Dann kann man sich das mit dem try-catch sparen, wenn ich das richtig verstanden habe 😉

Nur wie dann den Aufruf

Finanzen.DBConnect("SELECT * FROM tblAuszug", "Auszug");

... auf gültigkeit abfragen?

Irgendwie über .state?

Bin etwas ratlos im Moment, aber will ja mal irgendwann gescheiten Code schreiben... 😭

12.04.2006 - 20:44 Uhr

Danke schonmal für die Antworten, das ging ja schnell 🙂

Also:

@norman_timo
Meine Funktion hat keine Rückgabewerte, deshalb funzt das so leider auch nicht.

-> Connecte die Datenbank über einen Methodenaufruf einer externen Klassen;
Dieser soll mit try - catch überprüft werden (OleDBException)

In meiner eigentlichen Klasse wird der Inhalt meiner DB-Abfrage dann in ein DataTable geladen, die Werte ausgelesen und etwas rumgerechnet und anschließend über ein DataGrid ausgegeben. ...

@LaTino
... deshalb wäre der try{} - Block auch ziemlich lang.
Mir gehts ja nur um die Datenbankverbindung, die aufgebaut und überprüft werden soll.

Trotzdem alles rein?

Hatte schonmal ein Close(); dahinter, aber
1.) bringt mich das in Zukunft auch nicht weiter und
2.) hat der Debugger komischerweise ein paar Zeilen weiter trotzdem noch eine unbehandelte Ausnahme gefunden (bei Columns.Count)

Wie kann denn das schon wieder?

@der Marcel
Danke, werde mir das mit dem == true / false mal abgewöhnen 😉

@all
Echt Klasse, hier lernt man ja richtig was 🙂

Nur fehlt noch die richtige Lösung... 🤔

12.04.2006 - 20:05 Uhr

Hi,
bin ein ziemlicher newbie in Sachen C#, und nebenbei ist dies mein erster Beitrag im Forum, also erstmal Hallo 😁

Zu meiner Frage:

Wenn ich eine Exception alla 'try - catch' abfange, wie bewerkstellige ich es dass im Fehlerfall bspw. bei einer Datenbankconnection im Anschluss diverse Abfragen nicht ausgeführt werden??

Mit einer Variable 'FehlerAufgetreten == true'
würde das ja gehen, aber gibts da nicht eine bessere programmiertechnisch 'sauberere' Alternative?


bool FehlerAufgetreten = false;
try
{
  Finanzen.DBConnect("SELECT AuszugID, Datum, Betrag, ZweckID, Bemerkung, AktionKnippe, AktionKonto FROM tblAuszug", "Auszug");
}
catch (Exception ex)
{         
  FehlerAufgetreten = true;
  MessageBox.Show(ex.InnerException.Message,ex.Message,MessageBoxButtons.OK,
                             MessageBoxIcon.Error);
}
if (FehlerAufgetreten == false)
{
  DataTable AuszugTabelle = Finanzen.FinanzenDataset.Tables["Auszug"];
  int AnzahlEintraege = AuszugTabelle.Columns.Count;
...
...
...
}


Den kompletten Block unten in den try{}-Block Packen ist ja auch wohl schlecht oder?

Vielen Dank schonmal im voraus!