Laden...

EntityFrameworkCore: Einzelne Tabelle komplett cachen

Erstellt von Palladin007 vor 5 Jahren Letzter Beitrag vor 5 Jahren 2.132 Views
Palladin007 Themenstarter:in
2.079 Beiträge seit 2012
vor 5 Jahren
EntityFrameworkCore: Einzelne Tabelle komplett cachen

Hi,

ich suche nach einer Möglichkeit, mit EFCore einzelne Tabelleen für eine bestimmte Zeit komplett in einem Cache zu halten.
Dieser Cache soll entweder pro Session oder global für alle Sessions vorgehalten werden und entweder nach einer bestimmten Zeit oder nie verworfen werden. Außerdem soll der Cache manuell verworfen und neu gefüllt werden können.
Jedes Insert/Update/Delete soll entsprechend ebenfalls dorthin überführt werden.

EFCore kann bereits Daten im Cache halten, wenn man über eine konkrete ID darauf zugreift, im Falle einer normalen Query geht das aber nicht, die wird (nach meinem Test) immer zur Datenbank gegeben.

Konkret geht es um Datensätze, die sehr häufig gelesen, aber so gut wie nie geändert werden.
Es gibt nur eine Anwendung, die gleichzeitig in der Datenbank schreibt, alles Weitere hat nur lesenden Zugriff.

Ich hab den DBContext in einer extra Klasse verborgen, wenn man darüber auf die Daten zugreifen möchte, könnte ich also auch ein eigenes Caching-System dazwischen hängen, allerdings wäre es mir lieber, wenn es bereits etwas Vorhandenes gibt, idealerweise schon bei EFCore dabei.

Hat jemand Erfahrung damit und kann mir etwas empfehlen?

Beste Grüße

16.828 Beiträge seit 2008
vor 5 Jahren

Dieser Cache soll entweder pro Session oder global für alle Sessions vorgehalten werden und entweder nach einer bestimmten Zeit oder nie verworfen werden.

Was ist eine Session? Reden wir hier von einer Webanwendung?
Wenn ja, ASP.NET Core hat ein MemCache von Haus aus an Board.

Jedes Insert/Update/Delete soll entsprechend ebenfalls dorthin überführt werden.

Das ist Aufgabe Deiner Anwendungslogik.

Konkret geht es um Datensätze, die sehr häufig gelesen, aber so gut wie nie geändert werden.

Gehts um Settings? Wenn ja -> Option Pattern (alternativ einen Singleton via Injection).
Wäre kein Thema, das ich auf DAL Ebene lösen würde.

Palladin007 Themenstarter:in
2.079 Beiträge seit 2012
vor 5 Jahren

Was ist eine Session? Reden wir hier von einer Webanwendung?

Keine Web-Anwendung, sondern eine zentrale Server-Anwendung auf die viele Client-Anwendungen zugreifen.
Mit einer Session meine ich eine Datenbank-Verbindung.

Das ist Aufgabe Deiner Anwendungslogik.

Theoretisch könnte aber auch EFCore das übernehmen, macht es ja teilweise auch.
Zumindest speichert EFCore die Datenbank-Objekte anhand der ID und fragt dann nicht mehr bei der Datenbank nach, wenn es die ID schon kennt. Wenn ich eben diesen Datensatz ändere, wird auch der Datensatz im Cache aktualisiert.

Sowas erhoffe ich mir auch für eine ganze Tabelle.

Gehts um Settings? Wenn ja -> Option Pattern.

Es geht nicht nur um Settings, aber auch. Diese Settings werden nicht nur in der Server-Anwendung gelesen, damit fällt eine einfache COnfig-Datei raus.
Allerdings finde ich die Idee ganz charmant, eben jene Options aus der Datenbank zu laden, damit hätte ich ein entsprechendes Caching der Daten automatisch mit drin. Leider scheint es noch kein Framework zu geben, was das tut, also werd ich es wohl selber machen müssen 😕

Aber es sind auch nicht nur Settings, es gibt auch allgemeine Daten, die sich sehr selten ändern und wenn, dann nie von mehr als einem DBContext gleichzeitig. Gleichzeitig werden eben diese Daten bei jeder Aktion gelesen, somit lohnt es sich durchaus, diese Daten irgendwie im RAM zu halten.

16.828 Beiträge seit 2008
vor 5 Jahren

Keine Web-Anwendung, sondern eine zentrale Server-Anwendung auf die viele Client-Anwendungen zugreifen.
Mit einer Session meine ich eine Datenbank-Verbindung.

D.h. jede Verbindung selbst soll gecached werden, aber nicht Client-übergreifend? 🤔

Sowas erhoffe ich mir auch für eine ganze Tabelle.

Gibt es nicht von Haus aus.

Palladin007 Themenstarter:in
2.079 Beiträge seit 2012
vor 5 Jahren

D.h. jede Verbindung selbst soll gecached werden, aber nicht Client-übergreifend?

Die Last entsteht in der Server-Anwendung, die Clients sagen nur, was auf dem Server gemacht werden soll. Auf den Clients zu cachen wäre also überflüssig.

Es gibt aber Daten, die muss der Server bei jeder Operation abrufen. Diese Daten ändern sich entweder nie oder nur sehr selten.

Gibt es nicht von Haus aus.

Gibt es ein Konzept, wo man sowas ergänzen kann?

Wenn nicht, dann werd ich wohl was Eigenes bauen müssen.

T
2.222 Beiträge seit 2008
vor 5 Jahren

Was du machen willst, dafür ist EF Core nicht gedachte bzw. ist dies kein genereller Use Case von EF Core oder anderen OR Mappern.
Diese betreiben zwar ein gewissen Caching von Datensätze aber eben nicht vollständig sondern nur teilweise.

Dein aktueller Ansatz, vor EF Core einen Cache aufzubauen, wäre auch der Ansatz den ich wählen würde.
Da dies aber immer ein spezielle Umsetzung ist, gibt es hier keine generische fertige Lösung.
Da du auch eigene Anforderungen hast, musst du schon vom Prinzip her was eigenes umsetzen.

Je nach Art der Server Anwendung, würde ich dann vorhandene Caching Mechanismen verwenden.
Bei Web Anwendungen eben den Http Cache.
Da wir nicht von einem Web reden kann dies ein TCP/UDP Server o.ä. sein?
Dann würde ich mit MemoryCache arbeiten und darüber meine Daten cachen.
Wie dann die genaue Implementierung aussieht höngt dann vom deinem aktuellen System ab.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

Palladin007 Themenstarter:in
2.079 Beiträge seit 2012
vor 5 Jahren

Hm - schade

Da wir nicht von einem Web reden kann dies ein TCP/UDP Server o.ä. sein?

Die Kommunikation läuft über WCF - falls das das ist, was Du meinst.

Dann würde ich mit MemoryCache arbeiten und darüber meine Daten cachen.

Daran hab ich auch schon gedacht, allerdings baut der auf Keys auf?
Irgendwie wirkt das, als würde ich den MemoryCache für etwas missbrauchen, was auch einfach eine simple Liste sein könnte, oder tut der noch mehr, als mir bewusst ist?

T
2.222 Beiträge seit 2008
vor 5 Jahren

Der Memory Cache ist eine saubere Caching Lösung mit der du auch durch due Cache Policy einstellen kannst ob der Cache Eintrag zu einer fixen oder nach einer bestimmten Dauer seitdem letzten Zugriff verfallen soll.

Natürlich kannst du auch eine einfache Liste nehmen aber gerade wenn es um Objekt Caching in Anwendungen geht, würde ich auch den Memory Cache verwenden.
Ich habe z.B. einen CacheHelper gebaut, der intern dann den Memory Cache verwendet.
Damit mache ich dann alle Cache Operationen, also meine Objekte im Cache ablegen, entfernen oder aus dem Cache holen.

Schau dir den Memory Cache mal an, dann wird es dir auch klarer.
Halte ich für den besseren Ansatz, wenn du mehr als eine Liste zum cachen willst.

Nachtrag:
Ich meine den MemoryCache aus System.Runtime.Caching.
Keine Ahnung warum du die Extentions für ASP .NET Core findest obwohl schon beim suchen nach MemoryCache die Klasse aus System.Runtime.Caching zu erst gefunden wird.
Und bei deiner Server Anwendung geht es auch nicht um ASP .NET Core oder etwa doch?

https://docs.microsoft.com/de-de/dotnet/api/system.runtime.caching.memorycache?view=netframework-4.7.2

Nachtrag 2:
Hier gibt es nur den Leitfaden für ASP .NET Core:
https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-2.2

Da es die Klasse MemoryCache so aber nicht in .NET Core gibt, musst du wohl damit arbeiten.
Ansonsten müsstest du mal schauen ob es eine andere fertige Lösung gibt.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

Palladin007 Themenstarter:in
2.079 Beiträge seit 2012
vor 5 Jahren

Wozu gibt's einmal System.Runtime.Caching und Microsoft.Extensions.Caching.Memory?
Die scheinen beide ungefähr ähnlich zu laufen, nur dass die System.Runtime.Caching-Variante ein paar einfachere Nutzungsmöglichkeiten hat und wahrscheinlich älter ist. Ob das auch am Ende unterschiedlich arbeitet, weiß ich nicht.

Ich schau mir gerade Microsoft.Extensions.Caching.Memory-Variante an, das Einzige, was mir derzeit noch fehlt, ist die Möglichkeit, einen Cache beim Disposen von einem IoC-Scope zu leeren bzw. die dort erstellten Entries zu entfernen.
Aber es sollte möglich sein, das nachzurüsten, ähnlich wie Du sagst, mit einem eigenen CacheHelper, der den MemoryCache im Innern nutzt.

16.828 Beiträge seit 2008
vor 5 Jahren

Da es die Klasse MemoryCache so aber nicht in .NET Core gibt, musst du wohl damit arbeiten.

Wie Deiner Verlinkung zu entnehmen ist, gibt es MemoryCache sehr wohl für .NET Core bzw. allgemein für Multi-Plattform, inkl. Mono und Xamarin.

Im Falle von WCF muss man aber schauen, ob das NetFX hier den jeweiligen Standard unterstützt.
Die modernere Variante für WCF wäre eben jedoch gRPC (ab ASP.NET Core 3 mit an Board) oder JsonRPC.

Wozu gibt's einmal System.Runtime.Caching und Microsoft.Extensions.Caching.Memory?

Microsoft-Namespaces sind generellen die neuen Pakete. IMemoryCache ist hier der Weg der Wahl.
IMemoryCache ist viel flexibler in der Anwendung und es gibt eine Vielzahl von Providern (SQL/NoSQL-Datenbanken, Redis, InMemory...).

Die Last entsteht in der Server-Anwendung, die Clients sagen nur, was auf dem Server gemacht werden soll. Auf den Clients zu cachen wäre also überflüssig.

Das war nicht die Frage.

Die Frage war: willst Du die Dinge auf dem Server pro Verbindung, Pro Client oder Client-übergreifend in der gesamten Anwendung cachen?
Was bei Dir "eine Session" ist, ist mir nämlich leider immer noch nicht klar.

Es gibt auch in WCF einen Session Cache (basierend auf einer SQL Datenbank); diese "Session" wirkt sich aber auf den Client aus - nicht auf DAL-Ebene.
Dieser Mechanismus ist aber restriktiv und kann nicht so einfach erweitert werden.

Palladin007 Themenstarter:in
2.079 Beiträge seit 2012
vor 5 Jahren

Microsoft-Namespaces sind generellen die neuen Pakete. IMemoryCache ist hier der Weg der Wahl.
IMemoryCache ist viel flexibler in der Anwendung und es gibt eine Vielzahl von Providern (SQL/NoSQL-Datenbanken, Redis, InMemory...).

Dann bleib ich dabei 😄
Die Variante in System.Runtime.Cache war mir bisher gar nicht bekannt.

Die Frage war: willst Du die Dinge auf dem Server pro Verbindung, Pro Client oder Client-übergreifend in der gesamten Anwendung cachen?
Was bei Dir "eine Session" ist, ist mir nämlich leider immer noch nicht klar.

Pro WCF-Call wird ein Command ausgeführt. Jeder Command bekommt einen eigenen IoC-Scope (ich nutze Autofac) und pro Scope wird die DB-Connection aufrecht und die Command-Informationen bereit gehalten.
Mit "Session" meine ich einen solchen Scope oder die davon verwaltete DB-Connection. Ich möchte also einen Cache, der über alle Connections erhalten bleibt und einen Cache, der verworfen wird, wenn eine Connection beendet wird.

Ich möchte Daten Cachen, die entweder

  • vom Beginn der Anwendung bis zur Beendigung für alle Request
  • vom Beginn eines für ein Request begonnen Scopes bis zu dessem Ende
    gecacht werden können.

Oder anders formuliert:
Ich möchte Daten für die ganze Laufzeit der Anwendung im Cache vorhalten können. Dieser Cache kann außerhalb eines Requests oder während eines Requests aufgebaut werden und bleibt am Ende eines Requests erhalten.
Genauso möchte ich pro Request Daten vorbereiten können, die dann gecacht und an vielen Stellen genutzt werden. Am Ende eines Requests, soll dieser Cache wieder geleert werden.
Keiner der Caches muss nach Ende der Anwendung oder für andere Anwendungen verfügbar sein.

16.828 Beiträge seit 2008
vor 5 Jahren

Pro WCF-Call wird ein Command ausgeführt.

Also sprechen wir von einem normalen Request. Aber was ist "ein Command" ?
An für sich gibt es in der WCF-Sprache keinen "Command".

Mit "Session" meine ich einen solchen Scope oder die davon verwaltete DB-Connection.

Sorry, aber das ist keine Session im WCF Sinne.

Ich möchte Daten Cachen, die entweder

  • vom Beginn der Anwendung bis zur Beendigung für alle Request
  • vom Beginn eines für ein Request begonnen Scopes bis zu dessem Ende
    gecacht werden können.

Ich verstehe bei Dir die Separierung von Request und Scope nicht; nehme aber an, dass Du das gleiche meinst...?

Ergo sprechen wir von einem Application Cache (quasi ein Singleton) und einem Request-Cache, richtig?

Genauso möchte ich pro Request Daten vorbereiten können, die dann gecacht und an vielen Stellen genutzt werden. Am Ende eines Requests, soll dieser Cache wieder geleert werden.

Kannst Du dafür bitte ein Beispiel geben?
Der Sinn erschließt sich mir hier aktuell nicht wirklich.

Ich hab das Gefühl, dass Du mit dem Cache ein ganz anderes Problem "übertünchen" willst.
Was ist die tatsächliche Ursache, dass Du ein Cache benötigst? Man versucht ja generell in Applikationen das Caching zu vermeiden.
Ist die Ursache, dass die Anfragen auf die DB langsam werden, weil evtl. die Tabellen nicht optimiert sind?

Palladin007 Themenstarter:in
2.079 Beiträge seit 2012
vor 5 Jahren

Also sprechen wir von einem normalen Request. Aber was ist "ein Command" ?
An für sich gibt es in der WCF-Sprache keinen "Command".

Versteh es als ein Request, in einem ziemlich alten Projekt, mit dem ich viel arbeite, heißen die Dinge im Code "Command", die Bezeichnung hab ich mir wohl angewöhnt.

Sorry, aber das ist keine Session im WCF Sinne.

Ich mein ja auch eine Session im Datenbank-Sinne. Den Scope hab ich nur als zusätzliche Info erwähnt, weil es eben einen Scope gibt, der die DB-Connection auf macht und wieder aufräumt.

Ich verstehe bei Dir die Separierung von Request und Scope nicht; nehme aber an, dass Du das gleiche meinst...?

Technisch ist es verschieden. Ein Request ist das, was Du meinst (ich nutze z.B. MediatR) und ein Scope ist ein Autofac-Scope. Der RequestHandler macht aber immer einen eigenen Scope auf.
Technisch ist es also verschieden, praktisch fallen Request, Scope und DB-Connection immer auf den selben Zeitraum.

Ergo sprechen wir von einem Application Cache (quasi ein Singleton) und einem Request-Cache, richtig?

Genau

Ist die Ursache, dass die Anfragen auf die DB langsam werden, weil evtl. die Tabellen nicht optimiert sind?

Der Grund, weshalb ich nach einem Request-Cache suche, ist tatsächlich die Performance. Jeder Datenbank-Call braucht Zeit, wenn solche Call's sehr oft abgesetzt werden, kostet das auch viel Zeit.
Nun weiß ich aber an manchen Stellen schon, dass ich alles laden muss und kann das entsprechend vorher gesammelt laden, sodass alle folgenden Calls nur noch in den RAM müssen.

Aber ich gebe zu, wichtiger wäre der Application-Cache, der ist auch weniger riskant - zumindest auf die Daten bezogen, die ich darin haben will. Und einen der Anwendungsfälle werd ich versuchen, mit dem Option-Pattern abzudecken, diese Einstellungen ändern sich praktisch nie.

16.828 Beiträge seit 2008
vor 5 Jahren

Na wenn Du idealerweise schon ein CQRS-ähnlichen Aufbau aufgrund von IMediatR hast, dann kannst ja IMemoryCache als Application Cache relativ simpel integrieren.

Palladin007 Themenstarter:in
2.079 Beiträge seit 2012
vor 5 Jahren

Ja, das werde ich auch tun, ich hatte nur gehofft, es gibt ein Konzept, dass das in EFCore integriert werden kann, aber da sagte T-Virus ja schon, dass es das nicht gibt bzw. nicht vollständig oder wie ich es haben möchte.

Also werd ich wohl nicht drum herum kommen, etwas Eigenes vor EFCore zu hängen.