Laden...

Datenlisten immer nach gleichem Schema erstellen

Erstellt von Jessimaus vor 6 Jahren Letzter Beitrag vor 6 Jahren 5.457 Views
J
Jessimaus Themenstarter:in
6 Beiträge seit 2017
vor 6 Jahren
Datenlisten immer nach gleichem Schema erstellen

Hallo Leute,

habe gerade gesehen, dass man hier eigene Programme oder Teile davon zur 'Begutachtung' bzw. Diskussion vorstellen kann.
Ich benötige in unserem Projekt etliche Listen, so dass ich die immer nach dem gleichen Schema erstellen möchte.
Ich habe mich weitgehend an der Vorgehensweise im Doberenz orientiert.
Die unten beispielhaft angeführte Klasse LstArtikel würde ich dann als Basisklasse für DtoArtikel verwenden wollen,
die um die Bearbeitungsfunktionen Insert, Update und Delete erweitert ist.

Wie man schnell sieht, ist es nichts kompliziertes aber vielleicht gibt es ja den einen oder anderen hilfreichen Hinweis oder gar grundsätzliche Einwände.
Beispielsweise bin ich nicht sicher, ob das using nicht besser durch einen try/catch-Block ersetzt werden sollte.

Ich freue mich auf eure Kommentare
Liebe Grüße Jessimaus


#region using-Anweisungen
	using System;
	using System.Collections.ObjectModel;
	using System.Data;
	using System.Data.SqlClient;
	using System.Globalization;
	using Model.POCO;
	using Model.POCO.Database;
#endregion
namespace ViewModel.Lists{
	public class LstArtikelstamm : Collection<Artikelstamm>{

		private readonly	SqlConnection _SqlCon;
		private readonly	int _MandantenID;
		private const string COMMANDTEXT =
			@"SELECT * FROM [dbo].[ilfSelectArtikelliste](@MandantenID)";

		public LstArtikelstamm(MdbConnection MdbCon){
			if(MdbCon == null) return;

			_SqlCon		= MdbCon.SqlCon;
			_MandantenID	= MdbCon.MandantenID;
			fill_Liste();
		}	// End Konstruktor

		private void fill_Liste(){
			using(var _SqlReaderCommand = POCOModelBase.get_SqlCommand()){
				if(_SqlReaderCommand == null) return;

				_SqlReaderCommand.Connection	= _SqlCon;
				var _DrPrmMandantenID = new SqlParameter("@MandantenID", SqlDbType.Int){
					Direction = ParameterDirection.Input
				};
				_SqlReaderCommand.Parameters.Add(_DrPrmMandantenID);
				_SqlReaderCommand.CommandText = COMMANDTEXT;
				if(_SqlCon.State != ConnectionState.Open){
					_SqlCon.Open();
				}
				Clear();
				_SqlReaderCommand.Parameters[0].Value = _MandantenID;
				using(var _Reader = _SqlReaderCommand.ExecuteReader(CommandBehavior.CloseConnection)){
					if(!_Reader.HasRows) return;

					while(_Reader.Read()){
						var Listenartikel = new Artikelstamm{
							MandantenID	= _MandantenID,
							ArtikelID	= Convert.ToString(_Reader["ArtikelID"], CultureInfo.CurrentCulture),
							Artikelname	= Convert.ToString(_Reader["Artikelname"], CultureInfo.CurrentCulture),
							SteuersatzID	= Convert.ToInt32(_Reader["SteuersatzID"], CultureInfo.CurrentCulture),
							KategorieID 	= Convert.ToInt32(_Reader["KategorieID"], CultureInfo.CurrentCulture),
							GruppenID	= Convert.ToInt32(_Reader["GruppenID"], CultureInfo.CurrentCulture),
							Bearbeiter	= Convert.ToString(_Reader["Bearbeiter"], CultureInfo.CurrentCulture),
							LetzteAenderung	= Convert.ToDateTime(_Reader["LetzteAenderung"], CultureInfo.CurrentCulture),
							Zeilenversion	= _Reader.GetFieldValue<byte[]>(_Reader.GetOrdinal("Zeilenversion")),
							Anzahl		= Convert.ToInt32(_Reader["Anzahl"], CultureInfo.CurrentCulture)
						};
						Add(Listenartikel);
					}	// End While
				}	// End using - Reader
			}	// End Using - SQLCommand
		}	// End fill_Liste
	}	// End class
}	// End namespace

J
251 Beiträge seit 2012
vor 6 Jahren

Hallöchen

Beispielsweise bin ich nicht sicher, ob das using nicht besser durch einen try/catch-Block ersetzt werden sollte.

Also einen oder beide using gegen try/catch zu tauschen, wäre hier nicht gegeben. Objekt SQLCommand und SQLReader werden intialisiert und definitiv und sicher dispost, wenn sie nicht mehr gebraucht werden. Eine Verwendung die mehr als erwünscht ist.

Ein Problem, welches mir auffällt ist eher die SQL-Verbindung.
Du prüfst zwar richtig, ob die Verbindung offen ist mittels


 if(_SqlCon.State != ConnectionState.Open)
 {
       _SqlCon.Open();
 }

und öffnest sie notfalls.

  1. Wenn du sie öffnest, wieso schließt du sie dann nicht?
  2. Der sicherste Weg ist Eine sichere Methode wäre*: Eine Verbindung nur solange offen zu halten, solange sie verwendet wird. Leider ist nicht ganz erkenntlich woher die Verbindung kommt und wie lang ihre Lebensdauer schon besteht.

Vom Stil her würde ich den Select nicht als fest hartverdrahtetes Feld initialisieren.

Beim Konstruktor hätte ich wohl eher eine ArgumentNullException erwartet statt einfach nur ein "return", weil es wohl definitiv ein nicht gewünschtes Ergebnis ist, wenn der Parameter NULL ist.

*Kleine Korrektur nachdem ich mich des Wortes vergriffen habe und mir ein kleines Missgeschick durchgegangen ist.

16.806 Beiträge seit 2008
vor 6 Jahren

Der sicherste Weg ist: Eine Verbindung nur solange offen zu halten, solange sie verwendet wird. Leider ist nicht ganz erkenntlich woher die Verbindung kommt und wie lang ihre Lebensdauer schon besteht.

Der sicherste Weg ist eigentlich ADO.NET das Pooling zu überlassen.
Dafür ist es da.

T
2.219 Beiträge seit 2008
vor 6 Jahren

Mein Tipp wäre es erst einmal deinen Code sauber umzusetzen.
Wozu machst du eine Ableitung von Collection, wenn du scheinbar in deinem Code nichts davon implementierst.
Ebenfalls solltest du deinen Datenmodel Code von der Datenschicht trennen.
Hier solltest du dringend das Drei Schichten Modell umsetzen.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

709 Beiträge seit 2008
vor 6 Jahren

Wozu machst du eine Ableitung von Collection, wenn du scheinbar in deinem Code nichts davon implementierst.

Ganz unten wird die Add- und ziemlich mittig die Clear-Methode verwendet.

J
Jessimaus Themenstarter:in
6 Beiträge seit 2017
vor 6 Jahren

Hallo Jamikus,

vielen Dank für Deine Hinweise.

Die SQL-Verbindung ist Bestandteil von MdbCon und die ist in der Regel geschlossen.
Die Prüfung auf NULL im Konstruktor könnte man in der Tat weglassen, weil, wenn dieses Teil NULL ist, dann stirbt die ganze Anwendung ab.
Ich hatte mal die 30-Tage Testversion vom Resharper drauf und der hat rumgemeckert von wegen 'possible System.NullReferenzExceprion'.

Geschlossen wird die SQL-Verbindung automatisch zusammen mit dem Reader, wegen CommandBehavior.CloseConnection.

Wegen der using mache ich mir Gedanken, was passiert, wenn innerhalb dieser Blöcke irgendwas schiefgeht. Dann fliegt mir bzw. dem
Benutzer eine unbehandelte Ausnahme um die Ohren. Vielleicht sollte man innerhalb der using-Blöcke ein try/catch einbauen? Oder außen herum?

@T-Virus
Ich brauche eine Liste/Collection vom Typ Artikelstamm, warum soll ich also nicht von Collection ableiten?

Mit

var Artikelliste = new LstArtikelstamm(MdbCon);

baue ich mir die Liste, die ich ans DataGrid oder die Combobox binde. Einfacher geht's doch gar nicht, oder? Leite ich nicht von Collection ab,
sondern erstelle die Liste innerhalb einer einfachen Klasse, habe ich doch auch nichts anderes als die Liste aller Artikel.

Gruß Jessimaus

1.040 Beiträge seit 2007
vor 6 Jahren

Vielleicht sollte man innerhalb der using-Blöcke ein try/catch einbauen? Oder außen herum?

using und try/catch schließen sich nicht gegenseitig aus und haben auch per se überhaupt nichts miteinander gleich. Das eine kümmert sich um das Aufräumen von Ressourcen, das andere um die Fehlerbehandlung.

T
2.219 Beiträge seit 2008
vor 6 Jahren

@pinki
Hatte ich leider übersehen, aber das Konzept ist aus meiner Sicht nicht sinnvoll.
Ableiten sollte man nur, wenn man die Methode überschrieben will.
In diesem Kontext würde ich das ganze Model und die Datenschicht trennen.
Solchen Code würde ich nie schreiben, da hier keine saubere Schichten Trennung herrscht und der Code jetzt schon ziemlich unleserlich ist.

@Jessimaus
Hier wäre es sinnvoll das Drei Schichten Modell zu fahren.
Dann brauchst du auch keine Ableitung von Collection, was ohne Neuimplemntierung der Methoden nicht zielführend ist.

Hier wäre es sinnvoller das Drei Schichten Modell umzusetzen.
Dann hast du deine Definitionsschicht in dem deine Klasse Artikelstamm ohne Methoden sondern rein aus Properties und Konstruktor besteht.

Darüber liegt dann die Datenschicht, in der deine Lese-/Schreibvorgänge gegen die DB liegen.
Hier hättest du dann z.B. eine ListArtikelstamm Methode, die dir einfach eine List<Artikelstamm> Liste gibt.

Über der Datenschicht liegt dann die Anwendungsschicht, die sowohl Business Logik als auch den Zugriff zur Datenschicht enthält.

So trennst du sauber deine Verantwortungen von deinen Datenmodell und den Datenspeicherung/-verarbeitung.
Aktuell wirfst du dort deinen Code in deine Klasse und gibst ihr dann auch noch Abhängigkeiten für die Datenbank.
Ebenfalls missbrauchst du eine Ableitung unnötig um eine Liste von Artikelstamm zu bekommen.
Solchen Code solltest du dringend überarbeiten.
Ich vermute mal, dass deine Artikelstamm Klasse sich dann auch um das Anlegen, Ändern, Löschen und auslesen aus der DB kümmert.
Dies würde dann aber auch bedeuten, dass deine gesamte Architektur schon in den Datenmodellen feste verdrahtungen zur DB hat.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

1.029 Beiträge seit 2010
vor 6 Jahren

Hi,

wenn es hier schon um Daten geht - möchte ich an dieser Stelle neben der bereits erwähnten 3-Schicht-Architektur noch auf folgende Patterns hinweisen:
a) UnitOfWorkPattern
b) RepositoryPattern

Speziell letztere sieht in deinem Fall sehr interessant aus.

Des Weiteren wären Bibliotheken wie z.B. "Dapper" noch sehr interessant um die SQL-Daten zu parsen...

LG

4.931 Beiträge seit 2008
vor 6 Jahren

Da scheint etwas von der Architektur her generell nicht zu stimmen, denn es gibt ja anscheinend schon einen "Model.POCO" sowie "Model.POCO.Database"-Namensbereich. Ob dann noch mal ein weiterer SQL-Code zum Datenzugriff notwendig ist, bezweifle ich mal...

463 Beiträge seit 2009
vor 6 Jahren

private const string COMMANDTEXT = @"SELECT * FROM [dbo].ilfSelectArtikelliste";[/csharp]

Für viele nur eine Kleinigkeit - für mich im Code immer ein NoGo: **SELECT ***....

3.003 Beiträge seit 2006
vor 6 Jahren

Für viele nur eine Kleinigkeit - für mich im Code immer ein NoGo: **SELECT ***....


SELECT * FROM (SELECT id, name, age FROM people) WHERE rownum <=100

"Immer" gibt es nicht.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

463 Beiträge seit 2009
vor 6 Jahren

"Immer" gibt es nicht.

LaTino

Zwar ein wenig aus dem Zusammenhang gerissen, aber ich denke du weisst genau, was ich meinte... 😃