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
Benutzung von LiteDB mit Abstraktionsschicht
JimStark
myCSharp.de - Member

Avatar #dOpLzh7hN1az1g0eGRc0.jpg


Dabei seit:
Beiträge: 225

Themenstarter:

Benutzung von LiteDB mit Abstraktionsschicht

beantworten | zitieren | melden

Hi,

vielleicht kennt ja der ein oder andere LiteDB.
Da ein SQL-Server für dieses Projekt zu aufwendig wäre, würde ich gerne eine lokale Datenbank einsetzen. Vielleicht kennt ja jemand auch eine bessere Alternative.

Ich nehme als Beispiel wieder eine kleine Auftragsverwaltung - Auftrag - Kunde - Artikel.


    public class Article
    {
        public int ArticleID { get; set; }
        public string Title { get; set; }
        public Order Order { get; set; }
...

    public class Customer
    {
        public int CustomerID { get; set; }
        public string Firstname { get; set; }
        public string Lastname { get; set; }
        public List<Order> Orders { get; set; } = new List<Order>();
...
    public class Order
    {
        public int OrderID { get; set; }
        public string Title { get; set; }
        public Customer Customer { get; set; }
        public List<Article> Articles { get; set; } = new List<Article>();
...


Für die Datenabfrage habe ich mir einen kleine Abstraktionsschicht geschrieben:



    public class LocalDb : IAvService
    {
        private LiteDB.LiteDatabase _db;


        private ILiteCollection<Order> GetOrderCollection
        {
            get { return _db.GetCollection<Order>("orders"); }
        }
        private ILiteCollection<Customer> GetCustomerCollection
        {
            get { return _db.GetCollection<Customer>("customers"); }
        }

        private ILiteCollection<Article> GetArticleCollection
        {
            get { return _db.GetCollection<Article>("articles"); }
        }


        public LocalDb(string dbFile)
        {
            _db = new LiteDatabase(dbFile);
            BsonMapper.Global.Entity<Order>()
                .DbRef(x => x.Customer, "customers");
            BsonMapper.Global.Entity<Order>()
                .DbRef(x => x.Articles, "articles");
            BsonMapper.Global.Entity<Customer>()
                .DbRef(x => x.Orders, "orders");
            BsonMapper.Global.Entity<Article>()
                .DbRef(x => x.Order, "orders");
        }

        public Task<Customer> AddCustomer(Customer customer)
        {
            return Task<Customer>.Run(() =>
            {
                var col = GetCustomerCollection;
                var id  = col.Insert(customer);
                return col.FindById(id);
            });
        }
...


Erstelle ich einen neuen Auftrag mit Artikeln und Kunde löse ich das bisher so.
Da die Objekte (Artikel, Customer....) noch keine ID haben, muss ich sie erst separat in die Datenbank geben.


            IAvService av = new LocalDb("test.db");

            Order order = new Order
            {
                Title = "Test"
            };


            Article article1 = new Article
            {
                Title = "Arbeitszeit",
                Price = 60,
                Tax = 0.16
            };

            var dbArticle = av.AddArticle(article1).GetAwaiter().GetResult();

            Console.WriteLine($"Artikel ID: {dbArticle.ArticleID}");

            order.Articles.Add(dbArticle);

            order.Customer = av.AddCustomer(new Customer()
            {
                Firstname = "Max",
                Lastname = "Mustermann"
            }).GetAwaiter().GetResult();

            av.AddOrder(order);



Hier wollte ich fragen ob das soweit alles passt, da sich das ja durch mein komplettes Programm ziehen wird.
Was mich noch etwas stört ist bzw. was etwas unschön aussieht, dass ich alles erst Adden muss,
also es nicht direkt hinzufügen kann, wie bei EF: (Oder geht das doch irgendwie damit?)


Order order = new Order{
   Customer = new Customer{...}
}
Dieser Beitrag wurde 3 mal editiert, zum letzten Mal von JimStark am .
private Nachricht | Beiträge des Benutzers
witte
myCSharp.de - Member



Dabei seit:
Beiträge: 966

beantworten | zitieren | melden

Was soll eigentlich
.GetAwaiter().GetResult()
bezwecken? Sind diese Addxxx-Methoden wirklich asynchron? Warum verwendest du dann nicht await (mit Task.ConfigureAwait(false))? Das sieht deadlock-verdächtig aus. Implementiere das gleich richtig wenn du es überall einsetzen willst.
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15618
Herkunft: BW

beantworten | zitieren | melden

LiteDB hat kein Async support; das Wrappen in ein Task.Run() ist weit verbreiteter Workaround dafür.
Wenn man es dann aber wieder synchron umsetzt (.GetAwaiter().GetResult()) macht das wenig sinn; dann lieber alles nur Synchron anbieten.


    public class LocalDb : IAvService
    {
        private LiteDB.LiteDatabase _db;

Von der Umsetzung ist das soweit (grundlegend) korrekt; vom Naming im Sinne der Software Architektur nicht.
Im Endeffekt ist das weder ein Service, noch eine Db - sondern im Prinzip nichts anderes als (eine sehr einfache Variante) des Repository Pattern.


Falls Du Dich inspirieren lassen willst; ich hab zu LiteDB vor ein paar Jahren schon eine Lib geschrieben für ein damaliges Tool.
SchwabenCode.Data.LiteDBRepository
private Nachricht | Beiträge des Benutzers
JimStark
myCSharp.de - Member

Avatar #dOpLzh7hN1az1g0eGRc0.jpg


Dabei seit:
Beiträge: 225

Themenstarter:

beantworten | zitieren | melden

Zitat von witte
Sind diese Addxxx-Methoden wirklich asynchron? Warum verwendest du dann nicht await (mit Task.ConfigureAwait(false))?

Nein, ich wollte es im Interface aber als Task definieren, dass ich später evtl. mal eine asynchrone REST-, RPC-Schnittstelle oder ähnliches dazwischen klemmen könnte.
Der Aufruf soll später auch mit await stattfinden, das war nur ein Testaufruf in einer Konsole.

Zitat von Abt
Im Endeffekt ist das weder ein Service, noch eine Db - sondern im Prinzip nichts anderes als (eine sehr einfache Variante) des Repository Pattern.

Oh man, dieses Pattern vorher zu kennen wäre nützlich gewesen...
Wen es interessiert, hier habe ich auch ein gutes Beispiel gefunden: Implementieren von Repository-und Arbeitseinheiten Mustern in einer ASP.NET MVC-Anwendung (9 von 10)

Vielleicht baue ich direkt auf deinen Code auf

Danke euch beiden!
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von JimStark am .
private Nachricht | Beiträge des Benutzers
Abt
myCSharp.de - Team

Avatar #avatar-4119.png


Dabei seit:
Beiträge: 15618
Herkunft: BW

beantworten | zitieren | melden

Zitat von JimStark
Der Aufruf soll später auch mit await stattfinden, das war nur ein Testaufruf in einer Konsole.

Dann mach

public static async Task Main()
in Deiner Konsole.

Geht seit C# 7.1

Technisch gesehen macht der Compiler dann folgendes


// Wird automatisch erzeugt
public static void Main(string[] args)
{
    Main(args).GetAwaiter().GetResult();
}

// vom Entwickler
private static async Task Main(string[] args)
{
    ... // Main body here
}

Du merkst; wenn die Leute nicht Dein Code vollständig vor Augen haben und Du nichts erklärst, dann kommts zu Verwirrungen ;-)
private Nachricht | Beiträge des Benutzers