Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
SQL-Befehl (String) aus Klasse und Attribute erstellen
resper
myCSharp.de - Member



Dabei seit:
Beiträge: 33
Herkunft: Kassel

Themenstarter:

SQL-Befehl (String) aus Klasse und Attribute erstellen

beantworten | zitieren | melden

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
Zitat
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);

Moderationshinweis von gfoidl (08.06.2017 - 19:57:55):

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

private Nachricht | Beiträge des Benutzers
gfoidl
myCSharp.de - Team

Avatar #avatar-2894.jpg


Dabei seit:
Beiträge: 7538
Herkunft: Waidring

beantworten | zitieren | melden

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.

Zitat
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!"
private Nachricht | Beiträge des Benutzers
resper
myCSharp.de - Member



Dabei seit:
Beiträge: 33
Herkunft: Kassel

Themenstarter:

beantworten | zitieren | melden

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.
private Nachricht | Beiträge des Benutzers
Stefan.Haegele
myCSharp.de - Member

Avatar #avatar-3068.jpg


Dabei seit:
Beiträge: 439
Herkunft: Untermeitingen

beantworten | zitieren | melden

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).
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Stefan.Haegele am .
private Nachricht | Beiträge des Benutzers
gfoidl
myCSharp.de - Team

Avatar #avatar-2894.jpg


Dabei seit:
Beiträge: 7538
Herkunft: Waidring

beantworten | zitieren | melden

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!"
private Nachricht | Beiträge des Benutzers
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 10072

beantworten | zitieren | melden

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.
private Nachricht | Beiträge des Benutzers
resper
myCSharp.de - Member



Dabei seit:
Beiträge: 33
Herkunft: Kassel

Themenstarter:

beantworten | zitieren | melden

Zitat von gfoidl
Das erspart dann auch das string.Join.

Das String.Join fand ich ganz praktisch wegen dem eingebauten Seperator
Zitat von FZelle
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.

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.
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von resper am .
private Nachricht | Beiträge des Benutzers
FZelle
myCSharp.de - Experte



Dabei seit:
Beiträge: 10072

beantworten | zitieren | melden

Zitat
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.
private Nachricht | Beiträge des Benutzers
RED-BARON
myCSharp.de - Member



Dabei seit:
Beiträge: 76

beantworten | zitieren | melden

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.
private Nachricht | Beiträge des Benutzers
Deaktiviertes Profil
myCSharp.de - Member



Dabei seit:
Beiträge: 996

beantworten | zitieren | melden


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.
private Nachricht | Beiträge des Benutzers