Laden...

Modularer Aufbau mit Datenbankzugriff

Erstellt von JimStark vor 3 Jahren Letzter Beitrag vor 3 Jahren 732 Views
JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren
Modularer Aufbau mit Datenbankzugriff

Hi,

ich habe ein lokales Programm mit dem auf eine Datenquelle (z.B. SQLite-Datenbank) zugreifen kann.
Den Datenzugriff gestalte ich über ein Interface (GetOrders(), AddOrder(),...) damit ich die Datenquelle wechseln kann.

Nun möchte ich gerne dass man Module dynamisch hinzufügen kann. Also eine Art Pluginsystem.

Wie designe ich das jetzt am besten, sodass die Module auch Datenbank Zugriff haben und selbst Daten schreiben und auslesen können?

Ich habe mir schon überlegt dass ich in der Datenbank eine Tabelle anlege für alle Module die dann z.B. so aussieht:

| Module-Name/ID | Key | Value |

Also dass jedem Modul ein Key-Value-Paar zur Verfügung steht. Damit könnte ich auch im bisherigen Interface/Service eine generische Funktion hinzufügen (GetModuleKeyValue(moduleID, key), SetModuleKeyValue(...)). Das wird wahrscheinlich sehr unsauber, da ja mit dem Schlüssel ziemlich viel Information beschrieben werden muss (z.B. "Order-23-LieferantenApiUrl",...)

Hat da jemand noch einen Tipp für mich oder bessere Lösungsvorschläge?

5.658 Beiträge seit 2006
vor 3 Jahren

[FAQ] Eigene Anwendung pluginfähig machen

Bitte beachte unbedingt [Hinweis] Wie poste ich richtig?, Punkt 1.1 Erst suchen und in die Doku schauen, dann posten

Weeks of programming can save you hours of planning

JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren

Die Pluginfähigkeit ist nicht das Problem, das Datenbankdesign ist das Problem. Also dass ich im Interface auch Datenoperationen ausführen kann die ich nicht kenne. Was ich mir auch gedacht habe, in der oberen Tabelle einen JSON-String als Value zu speichern. Das wäre wahrscheinlich nicht gerade effizent, würde aber funktionieren 😁

5.658 Beiträge seit 2006
vor 3 Jahren

Warum sollten die Plugins überhaupt Zugriff auf die Datenbank bekommen? Das sollte eigentlich alles über das Interface gekapselt werden. Sonst könnte man einfach ein Plugin schreiben und die gesamte DB löschen.

Weeks of programming can save you hours of planning

D
261 Beiträge seit 2015
vor 3 Jahren

Wenn ich es richtig verstanden habe, dann sollen die Plugins ihren eigenen "Bereich" in der Datenbank erhalten.

Wenn es hier über sehr einfache Datenstrukturen (einfache Key-Value Paare) hinausgehen soll, dann müsstest du wahrscheinlich eine Schnittstelle bereitstellen, die es dem Plugin erlaubt Tabellen zu erstellen/modifizieren/löschen. Wenn die Schnittstelle von einem Plugin angesprochen wird, kannst du die Tabellennamen z.B. mit dem Pluginnamen als Präfix versehen. So hat das Plugin kein Zugriff auf deine eigenen Tabellen. Das Problem ist, dass das Plugin dann auch eventuell Migrationen bereitstellen muss, falls sich die Datenstruktur mit neuen Plugin-Versionen ändern soll. Außerdem musst du dann dem Benutzer auch Kontrolle darüber geben, welche Plugin Tabellen existieren und er sollte die Möglichkeit haben diese löschen zu können.

Sonst könnte man einfach ein Plugin schreiben und die gesamte DB löschen.

Sofern die Plugins Reflection o.ä. nutzen können, ist das sowieso der Fall.

M
368 Beiträge seit 2006
vor 3 Jahren

Zusätzlich könnte man sich für die Module und deren -gemeinsame- Datenbankoperationen das sog. "Repository Pattern" anschauen: https://www.norberteder.com/das-repository-pattern-anhand-eines-beispiels-inkl-tests/ (und die Rechte für die CRUD-Operationen wollen wohl auch angepasst werden)

Goalkicker.com // DNC Magazine for .NET Developers // .NET Blogs zum Folgen
Software is like cathedrals: first we build them, then we pray 😉

JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren

Wenn es hier über sehr einfache Datenstrukturen (einfache Key-Value Paare) hinausgehen soll, dann müsstest du wahrscheinlich eine Schnittstelle bereitstellen, die es dem Plugin erlaubt Tabellen zu erstellen/modifizieren/löschen. Wenn die Schnittstelle von einem Plugin angesprochen wird, kannst du die Tabellennamen z.B. mit dem Pluginnamen als Präfix versehen. So hat das Plugin kein Zugriff auf deine eigenen Tabellen.

Ganz genau. Das Problem dabei ist, das Plugin benutzt ja dann evtl. Objekte die dem DB-Interface und dem jeweiligen Service noch nicht bekannt sind. Das müsste ich dann mit einem generischen Typen lösen. So ähnlich wie hier: How can I use a generic type with entity framework core? Das teste ich mal mit LiteDB ähnlich wie bei Abts Projekt Data.LiteDBRepository

// Nachtrag:
Es ging doch einfacher als gedacht. Falls jemand mal was ähnliches braucht:


// Interface:
        Task AddPluginData<T>(string PluginIdentifier, T data);

        Task<List<T>> GetPluginData<T>(string PluginIdentifier);

// Implementierung mit LiteDB
        public Task AddPluginData<T>(string PluginIdentifier, T data)
        {
            return Task.Run(() =>
            {
                // Collection holen:
                var col = _db.GetCollection<T>($"Plugin_{PluginIdentifier}");
                col.Insert(data);
            });
        }

        public Task<List<T>> GetPluginData<T>(string PluginIdentifier)
        {
            return Task<List<T>>.Run(() =>
            {
                // Collection holen:
                var col = _db.GetCollection<T>($"Plugin_{PluginIdentifier}");
                return col.Query().ToList();
            });
        }

// Aufruf 

           await av.AddPluginData<Person>("MY_PLUGIN",
                new Person()
                {
                    Name = "Max Mustermann",
                    Age = 32
                }
                );


            await av.AddPluginData<Person>("MY_PLUGIN",
                new Person()
                {
                    Name = "Hans Dampf",
                    Age = 39
                }
                );


            foreach(var person in await av.GetPluginData<Person>("MY_PLUGIN"))
            {
                Console.WriteLine($"{person.Name} {person.Age}");
            }

        public class Person
        {
            public string Name { get; set; }
            public int Age { get; set; }
        }


Kann man natürlich noch ausbauen (CRUD). Die Frage ist es ob es mit einem SQL-Server auch so einfach geht 😁

Danke jedenfalls für eure Tipps!

5.658 Beiträge seit 2006
vor 3 Jahren

Du solltest dir mal anschauen, wie asynchrone Programmierung funktioniert. Hier kannst du einfach die asynchronen Methoden verwenden, die dir vom EF zur Verfügung gestellt werden.

Weeks of programming can save you hours of planning

JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren

Ja, in dem Beispiel nutze ich aber LiteDB, welches keine asynchronen Methoden zur Verfügung stellt. Das ist nur um den Interface gerecht zu werden.