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
SQLiteMapper (OR Mapper für SQLite)
preli
myCSharp.de - Member

Avatar #avatar-2343.png


Dabei seit:
Beiträge: 345
Herkunft: Österreich

Themenstarter:

SQLiteMapper (OR Mapper für SQLite)

beantworten | zitieren | melden

Hallo allerseits

Im folgenden Beitrag werde ich meinen OR Mapper für SQLite kurz erklären.

Vorteile:
  • einfach und schnell zu benutzen
  • recht gute Performance (Details siehe weiter unten)
  • keine SQL Kenntnisse notwendig

Features:
  • automatisches Erstellen/Einfügen von neuen Objekten
  • Updaten und Löschen von Objekten
  • rudimentäre Suche nach Objekten (wird noch erweitert)
  • OR Mapper kann eine vorhandene Datenbankstruktur nutzen, oder die Datenbank automatisch aus den C#-Klassen erstellen (im Demobeispiel wird alles automatisch erstellt)
  • Automatische Durchführung von Transaktionen
  • Geschwindigkeitsvorteil durch Cache (erst beim Aufruf von SaveChanges werden alle Änderungen transaktionsgesichert in die Datei geschrieben)
  • Tabellennamen können frei gewählt werden (müssen also nicht mit Klassennamen übereinstimmen)
  • Automatische Datenbankbereinigung (nach großen Löschvorgängen), wenn die "leeren" Bereiche in der Datenbank einen gewissen Prozentsatz überschreiten

Benutzung:
Bei der Benutzung sind ein paar Kleinigkeiten zu beachten:
1.) Objekte die man verwalten/speichern möchte müssen eine Property des Typs Guid besitzen, die einen eindeutigen Schlüssel (PrimaryKey) darstellt. (Die Schlüssel werden vom OR-Mapper automatisch vergeben, es muss lediglich solch eine Eigenschaft vorhanden sein)
1.) Objekte müssen das Interface IPersistentObject implementieren.
Dieses Interface verlangt lediglich 2 Properties: 1) string TableName -> wie soll die Tabelle in der Datenbank heißen, die diesem Objekt entspricht; 2) string PrimaryKey -> wie heißt die Property die den Primärschlüssel darstellt (siehe Punkt 1)
Das war's auch schon.
Ein kleines Beispiel für solch ein Objekt könnte wie folgt aussehen:

    public class TestObject: IPersistentObject
    {

        public Guid TestObjectID
        {
            get;
            set;
        }

        public string Name
        {
            get;
            set;
        }

        public DateTime TestDate
        {
            get;
            set;
        }

        #region IPersistentObject Member

        public string TableName
        {
            get { return "TestObject"; }
        }

        public string PrimaryKey
        {
            get { return "TestObjectID"; }
        }

        #endregion
    }

ACHTUNG: SQLiteMapper versucht ALLE Properties (ausgenommen "TableName" und "PrimaryKey") in die Datenbank zu speichern. Daher nur primitive Datentypen benutzen (int, double, string, DateTime, ...)
Wird eine vorhandene Datenbankstruktur genutzt, müssen daher alle Properties als Attribute der Tabelle vorhanden sein.

Ein kleines Demo (ist auch im zip-Archiv enthalten):


string file = Path.Combine(Application.StartupPath, "test.db");
            
            //--------- CREATE/CONNECT Database --------------------

            //create mapper / db
            DBAccess mapper = DBAccess.CreateDB(file);
            ////use next line instead to connect to an existing file
            //DBAccess mapper = new DBAccess(file);


            //--------- Create/Insert new objects ------------------

            //create (and insert) two new objects
            TestObject obj1 = mapper.NewBusinessOject<TestObject>();
            obj1.Name = "TestObject 1";
            TestObject obj2 = mapper.NewBusinessOject<TestObject>();
            obj2.Name = "TestObject 2";


            //--------- Get/Search objects -------------------------

            //get all objects of a specific type
            List<TestObject> all_objects = mapper.GetObjects<TestObject>();

            //get only objects where a specific property (Name) has a specific value ("TestObject 2")
            List<TestObject> filtered_objects = mapper.GetObjects<TestObject>("Name", "TestObject 2");

            if (filtered_objects.Count == 1)
            {
                filtered_objects[0].TestDate = DateTime.Now.AddDays(1);
                mapper.Update(filtered_objects[0]);
            }
            else
                MessageBox.Show("ERROR: filter was wrong");

            //every object has to have a PrimaryKey of the type Guid
            Guid primaryKey = obj1.TestObjectID;
            //you can access such an object very quickly by using the following method
            TestObject found = mapper.GetByPrimaryKey<TestObject>(primaryKey);
            if (found != obj1)
                MessageBox.Show("ERROR: wrong PrimaryKey");


            //--------- save changes into the database ------------------

            //save changes (the two new objects (also the modified one) to disc/file)
            mapper.SaveChanges();


            MessageBox.Show("Finished", "Finished", MessageBoxButtons.OK, MessageBoxIcon.Information);

Bezüglich Geschwindigkeit
(Testsystem: Notebook mit 2x2 GHz, 3 Gig Ram und 120GB Festplatte mit 7200 rpm (zu 90% belegt))
+ Das Erstellen von 10.000 Objekten dauert 334 Millisekunden
+ Das Speichern von 10.000 Objekten in die Datenbank (auf die Datei) dauert 683 Millisekunden

Leider konnte ich das Projekt nicht als Anhang hinzufügen, da es recht groß ist (2 MB), kann aber hier heruntergeladen werden: www.saftware.net/SQLiteMapper_vers1.zip

Liebe Grüße
Preli
Attachments
private Nachricht | Beiträge des Benutzers
Timur Zanagar
myCSharp.de - Member

Avatar #avatar-3412.jpg


Dabei seit:
Beiträge: 1559

beantworten | zitieren | melden

Hallo preli,

Ich hab mir zwar nur deinen Beitrag angesehen und nicht den ORMapper, aber der sieht auf dem ersten Blick nicht schlecht aus.

Was mich aber persönlich interessieren würde, wäre ein direkter Vergleich zu ADO.NET.
private Nachricht | Beiträge des Benutzers
preli
myCSharp.de - Member

Avatar #avatar-2343.png


Dabei seit:
Beiträge: 345
Herkunft: Österreich

Themenstarter:

beantworten | zitieren | melden

Zitat von burning snow
Was mich aber persönlich interessieren würde, wäre ein direkter Vergleich zu ADO.NET.
Meinst du einen Performancevergleich?
Dort würde ADO wahrscheinlich schon eine Spur schneller sein, da mein Mapper ja intern auch ADO benutzt.
Jedoch könnte SQLiteMapper schon in manchen Fällen trotzdem schneller sein, wenn man mit Objekten "arbeitet" die im Cache sind.

Liebe Grüße
Preli
private Nachricht | Beiträge des Benutzers
Peter Bucher
myCSharp.de - Experte

Avatar #jVxXe7MDBPAimxdX3em3.jpg


Dabei seit:
Beiträge: 6141
Herkunft: Zentralschweiz

beantworten | zitieren | melden

Hallo preli

Ein direkter Vergleich zur Standard SQLite-API wäre interessanter, IMO :-)
(Klar ist dein Mapper langsamer, aber der Faktor wäre interessant als das derzeitige Bild)

Die Typen- und PropertyInfo[]-Objekte könntest du ggf. noch cachen, wenn mehrere gleiche hintereinander kommen (Oder nach einer anderen Strategie).


foreach (PropertyInfo pi in temp.GetType().GetProperties())

Das hier könntest du mit einem Constraint einschränken, dann brauchst du keine Laufzeit-Prüfung mehr:


        public T NewBusinessOject<T>()
        {
            T result = (T)Activator.CreateInstance(typeof(T));
            if (result is IPersistentObject)

=> (Den Cast auf IPersistenceObject kannst du auch weglassen, mit dem Constraint)


        public T NewBusinessOject<T>() where T: IPersistentObject
        {
            T result = (T)Activator.CreateInstance(typeof(T));
            IPersistentObject po = result;
        }

Die zwei GetObjects-Methoden könntest du auch ein bisschen zusammenfassen, die enthalten zuviel Redundanz.

<Command / Connection / etc..>.Close / Dispose müsstest du noch ergänzen.
Am besten ein using-Statement benutzen.
Alles in einer 3 Dateien ist ein bisschen knapp, ich würde das noch ein wenig trennen.

Die Medaille "Kürzester OR / Mapper ever" hast du von mir schon mal :-)

BTW: Schau dir mal SubSonic an (http://subsonicproject.com/)


Gruss Peter
--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

- https://peterbucher.ch/ - Meine persönliche Seite
- https://fpvspots.net/ - Spots für FPV Dronenflüge
private Nachricht | Beiträge des Benutzers
preli
myCSharp.de - Member

Avatar #avatar-2343.png


Dabei seit:
Beiträge: 345
Herkunft: Österreich

Themenstarter:

beantworten | zitieren | melden

Danke für die Tipps (und die Medaille)
+ Den Contraint auf IPersistentObject werde ich auf jeden Fall einbauen.
+ Die GetObjects Funktionen werden ohnehin noch erweitert, damit man auch erweiterte Suchfunktionen besitzt.
+ using-Statement bei der Connection werde ich auch noch einbauen

Liebe Grüße
Preli
private Nachricht | Beiträge des Benutzers
masta // thomas
myCSharp.de - Member

Avatar #avatar-2645.png


Dabei seit:
Beiträge: 36

beantworten | zitieren | melden

http://www.db4o.com/
we love C#...
private Nachricht | Beiträge des Benutzers
Timur Zanagar
myCSharp.de - Member

Avatar #avatar-3412.jpg


Dabei seit:
Beiträge: 1559

beantworten | zitieren | melden

Was hat dein Beitrag mit diesem ORMapper für SQLite zu tun? preli hat ein ORMapper erstellt und keine OODB.
private Nachricht | Beiträge des Benutzers
seilenbe
myCSharp.de - Member



Dabei seit:
Beiträge: 2
Herkunft: bei Leipzig

beantworten | zitieren | melden

Es gibt unter DBLinq schon einen Ansatz um SQLite und MySQL uvm. für Linq zugänglich zu machen.

MfG
Sven
private Nachricht | Beiträge des Benutzers