Entwickler-Ecke

Datenbanken (inkl. ADO.NET) - SqlDataAdapter in SwitchCase mehrfach nutzen


JohnDyr - Sa 05.01.19 14:41
Titel: SqlDataAdapter in SwitchCase mehrfach nutzen
Liebe Community,

ich stehe mal wieder auf dem Schlauch. Der Code sagt glaube ich alles...


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
            switch (module)
            {
                case EModule.Carrier:
                    SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM [View1]", Connection);
                    da.Fill(res);
                    break;
                case EModule.Customer:
                    // wie kann ich das besser umsetzen? Ich möchte nicht jedesmal ein SqlDataAdapter Objekt erstellen :(
                    SqlDataAdapter da1 = new SqlDataAdapter("SELECT * FROM [View2]", Connection);
                    da1.Fill(res);
                    break;
                case EModule.Loader:
                    break;
                case EModule.Deloader:
                    break;
                default:
                    break;
            }
            return res;



Moderiert von user profile iconTh69: Topic aus WinForms verschoben am Sa 05.01.2019 um 14:32


Ralf Jansen - Sa 05.01.19 14:58

Und warum willst du keinen neuen SqlDataAdapter erzeugen?


JohnDyr - Sa 05.01.19 15:13

Naja, wenn man es sich ersparen kann, würde ich es mir gerne ersparen. Ich möchte das Programm so leichtgewichtig wie möglich programmieren. Mittlerweile habe ich folgendes vergeblich probiert...


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
            
DataTable res = new DataTable();

SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM '@table' ORDER BY [Zuletzt geändert] DESC", Connection);
da.SelectCommand.Parameters.AddWithValue("table", module.ToString());
da.Fill(res);

return res;


Funktioniert aber nicht...


Ralf Jansen - Sa 05.01.19 15:30

Parameter machen keine Textersetzung. Parameter sind nur zum austauschen von Werten gedacht, also nicht für Tabellen oder Spaltennamen.
Gerade darum sind die auch so sicher. Niemand kann das eigentliche Verhalten des SQL von außen ändern da nur Werte ausgetauscht werden können und man das Verhalten durch einfügen ~cleverer~ Werte nicht ändern kann.

Zitat:
Naja, wenn man es sich ersparen kann, würde ich es mir gerne ersparen.


Was willst du dir da sparen :gruebel: Neue Objekte erzeugen ist in einem OO System so ziemlich der natürlichst vorstellbare Vorgang. Gerade so ein Adapter ist eine ziemlich leere Hülle. Da hängt am Konstructor kein großes Preisschild dran.

Als Best Practice bei ADO.Net (das ist hier kein Winforms Problem vielleicht kann da mal einer verschieben) gilt eh die entsprechend nötigen Instanzen immer so spät wie möglich zu erzeugen und so früh wie möglich zu zerstören (Usings sind da extrem hilfreich). Das betrifft insbesondere die Connection. Und da alle anderen relevanten Klassen (Commands, Adapter etc.) von der Connection abhängen betrifft es die mit.


JohnDyr - Sa 05.01.19 15:35

Ja, habe eben weiter recherchiert und was ich möchte, kann man so nicht umsetzen. Dafür müsste ich Stored Procedures einsetzen. Meine Motivation ist halt, dass ich 4 Tabellen habe, welche von der Struktur her (fast) gleich sind. Jetzt den Datenbank Getter/Setter/Reader Code dafür 4 mal zu schreiben, finde ich uncool.

Zur vollständigkeithalber...

http://www.sommarskog.se/dynamic_sql.html


Th69 - Sa 05.01.19 15:49

Das kannst du schon, nur mußt du dann den SQL-String per + zusammenbauen:

C#-Quelltext
1:
string sql = "SELECT * FROM " + module + "...";                    


JohnDyr - Sa 05.01.19 16:07

user profile iconTh69 hat folgendes geschrieben Zum zitierten Posting springen:
Das kannst du schon, nur mußt du dann den SQL-String per + zusammenbauen:

C#-Quelltext
1:
string sql = "SELECT * FROM " + module + "...";                    


Genau DAS habe ich grade auch umgesetzt und es funktioniert. Ich frage mich ob diese Problemlösung "normal" ist. Ich kann ja nicht der erste sein, der dieses Problem hat.


Ralf Jansen - Sa 05.01.19 16:26

Zitat:
Ich frage mich ob diese Problemlösung "normal" ist.


Üblich und normal ja. Ob das gut ist ist eine andere Frage ;)

Zitat:
Jetzt den Datenbank Getter/Setter/Reader Code dafür 4 mal zu schreiben, finde ich uncool.


Der Code sollte doch identisch sein nur das SQL ändert sich. Wo ist das Problem das nötige SQL Statement zu erzeugen und dann an einen einheitlichen Code zu geben?


JohnDyr - Sa 05.01.19 16:52

Zitat:
Üblich und normal ja. Ob das gut ist ist eine andere Frage ;)


Nicht gut, wegen SQL Injection Gefahr - verstehe ich.

Zitat:
Der Code sollte doch identisch sein nur das SQL ändert sich. Wo ist das Problem das nötige SQL Statement zu erzeugen und dann an einen einheitlichen Code zu geben?

Es gibt kein Problem. Funktionieren tut das auch. Ich finde es aus Sicht der Erweiterbarkeit und Wartbarkeit nur nicht so schön, wenn man fast den gleichen Code 4 mal hat (und das jeweils ca. 500 Zeilen).


Ralf Jansen - Sa 05.01.19 19:57

Zitat:
Ich finde es aus Sicht der Erweiterbarkeit und Wartbarkeit nur nicht so schön, wenn man fast den gleichen Code 4 mal hat (und das jeweils ca. 500 Zeilen).


Welchen Code brauch man da 4 mal?

Ein Beispiel

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
public DataTable GetDataforModule(EModule module)
{
    switch (module)
    {
        case EModule.Carrier:
            return GetData("SELECT col1, col2, col3 FROM [View1]", meinLieberConnectionString);
        case EModule.Customer:
            return GetData("SELECT col4, col5, col6 FROM [View2]", meinLieberConnectionString);
        // etc.
    }
}

public DataTable GetData(string sql, string conn)
{
    DataTable dataTable = new DataTable();
    using (var con = new SqlConnection(conn))
    {
        con.Open();
        using (var adapter = new SqlDataAdapter(sql, con))
        {
            adapter.Fill(dataTable);
        }
    }
    return dataTable;
}


JohnDyr - So 06.01.19 13:30

Zitat:
Welchen Code brauch man da 4 mal?


Ich habe noch diverse SQL "Getter" Methoden welche in etwa so aussehen:


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
GetNameById(id)
SELECT name FROM table1 WHERE id = @id

GetNameById(id)
SELECT name FROM table2 WHERE id = @id

GetNameById(id)
SELECT name FROM table3 WHERE id = @id


und das gleiche jetzt nicht nur für Name sondern auch weitere Informationen (Adresse, Land) usw. Der Aufbau dieser Tabellen ist identisch (Spalten sind gleich). Die Source ist halt immer eine andere.


Ralf Jansen - So 06.01.19 15:40

Eine Methode zu schreiben der man den Tabellennamen übergibt um das passende SQL zu basteln scheint dann aber jetzt auch nicht so aufwendig.

Das eigentliche Grundübel scheint dann wohl eher ein kaputtes Datenbankmodel zu sein. Mehrere Tabellen/Views mit identischen Aufbau zu haben hat ein Geschmäckle.


JohnDyr - So 06.01.19 16:38

user profile iconRalf Jansen hat folgendes geschrieben Zum zitierten Posting springen:
Eine Methode zu schreiben der man den Tabellennamen übergibt um das passende SQL zu basteln scheint dann aber jetzt auch nicht so aufwendig.

Das eigentliche Grundübel scheint dann wohl eher ein kaputtes Datenbankmodel zu sein. Mehrere Tabellen/Views mit identischen Aufbau zu haben hat ein Geschmäckle.


Ich zügel mich davor zu sagen, dass mein Datenmodell optimal ist... aber was besseres ist mir nicht eingefallen. Irgendwo ist es ja auch Geschmackssache. Kurze Erläuterung: Angenommen du hast zwei Stammdatensätze: Absender und Empfänger

Beide haben gleiche Attribute wie: Vorname, Nachname, Straße, Hausnummer, PLZ, Ort, Land

Würdest du jetzt zwei Tabellen erstellen mit den gleichen Attributen oder eine Tabelle und darin eine weitere Spalte mit "Typ"? Oder gibt es noch eine dritte Möglichkeit? Wie würde man denn sowas am besten abbilden..? :?:


Zitat:
Eine Methode zu schreiben der man den Tabellennamen übergibt um das passende SQL zu basteln scheint dann aber jetzt auch nicht so aufwendig.

Nein ist es nicht, habe deshalb diese Lösung umgesetzt.


Ralf Jansen - So 06.01.19 20:08

Zitat:
Würdest du jetzt zwei Tabellen erstellen mit den gleichen Attributen oder eine Tabelle und darin eine weitere Spalte mit "Typ"? Oder gibt es noch eine dritte Möglichkeit? Wie würde man denn sowas am besten abbilden..? :?:


Das ist allgemein schwer zu sagen. Bei einem Absender/Empfänger System kann es hilfreich sein da 2 Tabellen zu nehmen muss aber nicht.

So ohne Kontext würde ich eher zu einer Tabelle tendieren. Absender/Empfänger klingt eher nach einer Beziehung zu etwas. Also nicht zwei Tabellen und auch kein Typ in der einen Tabelle. Man ist Empfänger/Absender von etwas und dieses etwas ist eine Tabelle (z.B. Mail/Brief/Paket/Nachricht etc.) und diese Tabelle hat zwei Beziehungen zu einer Personen/Kunden/Benutzer etc. Tabelle. Die Beziehung zwischen Ding und Person ist also Absender oder Empfänger.

Oder in dem Fall das es z.b. mehrere Empfänger oder andere Rollen neben Absender und Empfänger geben könnte (Überbringer oder sowas) dann könnte es eine Mapping Tabelle zwischen Ding und Person geben kann an der dann ein entsprechender Typ hängt.