Laden...

SQL-Befehl (String) aus Klasse und Attribute erstellen

Erstellt von resper vor 6 Jahren Letzter Beitrag vor 6 Jahren 4.756 Views
R
resper Themenstarter:in
33 Beiträge seit 2017
vor 6 Jahren
SQL-Befehl (String) aus Klasse und Attribute erstellen

Hallo,

Leider ist mir für diesen Beitrag kein andere Titel eingefallen. Wie bereits aus meinen anderen Beiträgen ersichtlich beschäftige ich mich momentan mit der Integration von SQLlite in mein kleines Programm. Dazu bastel ich an eine Klasse für meine SQLite Operationen, die ich evtl. später auch für andere Projekte nutzen kann. Ähnlich wie bei der SQLite-net habe ich eine Methode, dass mir erlaubt aus Models Tabellen zu erstellen. Mich würde interessieren, wie die Profis unter Euch meine aktuelle Methode beurteilen und was ich evtl. verbessern könnte. Dabei geht es nicht direkt um die SQLite Funktion, der String der erzeugt wird macht als SQLite Befehl, das was er soll, sonder rein um den Weg zum String.

Meine Klasse für die Attribute:

class DbAttributes : Attribute
{
    public bool IsPrimary { get; set; }
    public bool IsAuto { get; set; }
    public bool IsInteger { get; set; }
    public bool IsText { get; set; }
}

Beispiel eines Models:

    class User
    {
        [DbAttributes(IsPrimary =true, IsAuto =true, IsInteger =true)]
        public long Id { get; set; }
        [DbAttributes(IsText =true)]
        public string Login { get; set; }
        [DbAttributes(IsText = true)]
        public string Firstname { get; set; }
        [DbAttributes(IsText = true)]
        public string Lastname { get; set; }
        [DbAttributes(IsText = true)]
        public string PersId { get; set; }
        [DbAttributes(IsText = true)]
        public string Password { get; set; }
        [DbAttributes(IsInteger =true)]
        public long Role { get; set; }
        [DbAttributes(IsInteger = true)]
        public long First { get; set; }
        [DbAttributes(IsInteger = true)]
        public long Active { get; set; }
    }

Meine Methode:

private void createTable<T>()
{
    PropertyInfo[] props = typeof(T).GetProperties();
    string[] cols = new string[props.Count()];
    for(int i = 0; i < props.Count(); i++)
    {
        string colString = String.Format("'{0}'", props[i].Name);
        var attrs = props[i].GetCustomAttributes(true);
        foreach (DbAttributes attr in attrs)
        {
            colString += (attr.IsInteger) ? " INTEGER" : "";
            colString += (attr.IsText) ? " TEXT" : "";
            colString += (attr.IsPrimary) ? " PRIMARY KEY" : "";
            colString += (attr.IsAuto) ? " AUTOINCREMENT" : "";
        }
        cols[i] = colString;
    }
    string createTable = String.Format("CREATE TABLE IF NOT EXISTS '{0}' ({1});", typeof(T).Name, String.Join(", ", cols));
            
    queryNon(createTable);
}
createTable<User>();

erzeugt dann folgenden String

CREATE TABLE IF NOT EXISTS 'User' ('Id' INTEGER PRIMARY KEY AUTOINCREMENT, 'Login' TEXT, 'Firstname' TEXT, 'Lastname' TEXT, 'PersId' TEXT, 'Password' TEXT, 'Role' INTEGER, 'First' INTEGER, 'Active' INTEGER);

Hinweis von gfoidl vor 6 Jahren

Ich hab den Titel leicht angepasst, so dass es (ein bischen) klarer wird.

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo resper,

ein paar Anmerkungen dazu:

  • ich würde die Model-Klassen (hier User) frei von den DB-Attributen lassen, damit diese "universeller" einsetzbar sind. Aktuell haben diese Klassen einen Bezug zu "deiner" Datenbank bzw. zu dem Mapper. Mir wären diesbezüglich POCOs lieber.
    D.h. als Alternative konfiguriere per Code das Mapping von Eigenschaften zu Spalten, etc. Schau dir dazu als Inspiration das Fluent Api von Entity Framework an.

  • Baue den String nicht einfach mit += zusammen. Auch wenn es vermutlich nur wenige String-Verkettung sein werden, ist ein StringBuilder passender.
    Außerdem hast du mit dem StringBuilder mehr Möglicheiten zum Bauen des Strings, daher auch sein Name.

  • Richtig schön wäre das umgesetzt durch Verwendung vom Builder-Pattern. Schau dir dies einmal an und versuche das so umzusetzen. Dadurch gewinnst du an Flexibilität, kannst das super (Unit-) Testen und bist auch offen für einfach Erweiterungen und Anpassungen.

Wie bereits aus meinen anderen Beiträgen ersichtlich beschäftige ich mich momentan

Nimm bitte nicht an, dass jeder potentielle Helfer weiß womit sich die Fragenstellenden beschäftigen 😉

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

R
resper Themenstarter:in
33 Beiträge seit 2017
vor 6 Jahren

Hallo gfoidl,

danke für die Anpassung des Titels und die Verschiebung ins richtige Forum.

Zu Punkt 2:
Ich habe das ganze mal mit einem StringBuilder umgesetzt, war es ungefähr das was du meintest:

private void createTable<T>()
{
    PropertyInfo[] props = typeof(T).GetProperties();

    string[] cols = new string[props.Count()];

    for(int i = 0; i < props.Count(); i++)
    {
        StringBuilder colString = new StringBuilder(String.Format("'{0}'", props[i].Name));
        var attrs = props[i].GetCustomAttributes(true);
        foreach (DbAttributes attr in attrs)
        {
            if (attr.IsInteger) colString.Append(" INTEGER");
            if (attr.IsText) colString.Append(" TEXT");
            if (attr.IsPrimary) colString.Append(" PRIMARY KEY");
            if (attr.IsAuto) colString.Append(" AUTOINCREMENT");
        }
        cols[i] = colString.ToString();
    }

    string createTable = String.Format("CREATE TABLE IF NOT EXISTS '{0}' ({1});", typeof(T).Name, String.Join(", ", cols));
    queryNon(createTable);
}

Zu Punkt 1 und 3:
Da muss ich wohl noch etwas Zeit investieren. Da mein Englisch nicht sehr gut ist, brauche ich für solche etwas komplexeren Themen immer etwas länger, weil ich die Artikel in der Regel mehrfach lesen muss. Werde ich mich die nächsten Tage dran setzten.

463 Beiträge seit 2009
vor 6 Jahren

Nur mal so als Idee (so mache ich das ganze):

Ich habe alle SQL Befehle in der (jeweiligen) Datenbank gespeichert und lade diese am Anfang in eine Dictionary. Im Programm gibt es nur SQL Befehle für:

-> Das Laden der SQl Befehle aus den jeweiligen Datenbanken

Somit muss ich die Befehle nicht mühsam zusammensatzen und greife per ID auf die jeweils korrekten SQL Befehle zu. (Und zur Laufzeit habe ich natürlich immer nur die SQL Befehle des aktuellen Provider geladen).

6.911 Beiträge seit 2009
vor 6 Jahren

Hallo resper,

der StringBuilder gehört vor die for-Schleife, da mit dem der gesamte String gebaut werden soll.
Das erspart dann auch das string.Join.

Es sollte string createTable = myStringBuilder.ToString() sein.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

F
10.010 Beiträge seit 2004
vor 6 Jahren

Und ich würde das selbst implementieren komplett sein lassen, wenn es nicht der eigentliche Sinn ist das mit Attributen und Refelection zu lernen.

SQLite-NET hat das alles schon implementiert incl. Schemaupdate und allem.

R
resper Themenstarter:in
33 Beiträge seit 2017
vor 6 Jahren

Das erspart dann auch das string.Join.

Das String.Join fand ich ganz praktisch wegen dem eingebauten Seperator

Und ich würde das selbst implementieren komplett sein lassen, wenn es nicht der eigentliche Sinn ist das mit Attributen und Refelection zu lernen.


>
hat das alles schon implementiert incl. Schemaupdate und allem.

An dem Prinzip versuche ich mich zu orientieren. Aber so lerne ich am besten, wenn ich solche Funktionen nachbaue und versuche zu verstehen wie das funktioniert.

F
10.010 Beiträge seit 2004
vor 6 Jahren

An dem Prinzip versuche ich mich zu orientieren. Aber so lerne ich am besten, wenn ich solche Funktionen nachbaue und versuche zu verstehen wie das funktioniert.

Halte ich nach 30 Jahren Softwareentwicklung für den Falschen Weg.
Erst die Grundlagen richtig lernen und dann ausprobieren geht deutlich schneller und ist weniger frustrierend als erst bisschen lesen, dann schon mal anfangen und dann ständig nachfragen Frickeln usw zu müssen.

Aber ist deine Zeit und deine Nerven.

R
74 Beiträge seit 2006
vor 6 Jahren

SQL-Befehl (String) aus Klasse und Attribute erstellen ist evtl. nur der erste Schritt. Im zweiten Schritt wird das bauen des Befehls mit der tatsächlichen
Struktur der Tabellen abgeglichen. So dass "alte" Clients mit neuer Datenstruktur genauso sinnvoll arbeiten können wie neue Clients mit alter Struktur.
Für unsere mobile App gilt genau diese Anforderung, da niemals es möglich sein kann, dass die zentrale Applikation und erst recht nicht die mobilen Geräte
gleichzeitig mit einem Update versorgt werden können.
Ich weiß nicht ob die aktuellen Frameworks oder Provider so etwas unterstützen. Zu Zeiten von Android 2.1 habe ich nichts passendes gefunden.

D
985 Beiträge seit 2014
vor 6 Jahren

Eine mobile App sollte sich niemals direkt mit einer externen Datenbank verbinden. Darum gibt es das Problem dieser Art dort gar nicht.

Eine versionierte WebApi stellt die Funktionen auch für die alten Clients zur Verfügung.