Laden...

SQLiteMapper (OR Mapper für SQLite)

Erstellt von preli vor 15 Jahren Letzter Beitrag vor 15 Jahren 8.090 Views
preli Themenstarter:in
343 Beiträge seit 2007
vor 15 Jahren
SQLiteMapper (OR Mapper für SQLite)

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

[- www.saftware.net -](http://www.saftware.net/)
1.457 Beiträge seit 2004
vor 15 Jahren

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.

preli Themenstarter:in
343 Beiträge seit 2007
vor 15 Jahren

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

[- www.saftware.net -](http://www.saftware.net/)
5.941 Beiträge seit 2005
vor 15 Jahren

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

preli Themenstarter:in
343 Beiträge seit 2007
vor 15 Jahren

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

[- www.saftware.net -](http://www.saftware.net/)
35 Beiträge seit 2006
vor 15 Jahren

we love C#...

1.457 Beiträge seit 2004
vor 15 Jahren

Was hat dein Beitrag mit diesem ORMapper für SQLite zu tun? preli hat ein ORMapper erstellt und keine OODB.

S
2 Beiträge seit 2007
vor 15 Jahren

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

MfG
Sven