2009-06-04 12 views
0

Ich verwende LINQ zu SQL für MySql (mit DbLinq) in einer ASP.NET MVC-Website. Ich habe ein seltsames Cache-Problem. Betrachten Sie die folgenden Methoden in meiner Repository-Klasse:DbLinq - Cache-Problem

public IEnumerable<Message> GetInbox(int userId) 
{ 
    using(MyDataContext repo = new MyDataContext(new MySqlConnection("[Connectionstring]"))) 
    { 
    return repo.Messages.Where(m => m.MessageTo == userId); 
    } 
} 

public IEnumerable<Message> GetOutbox(int userId) 
{ 
    using (MyDataContext repo = new MyDataContext(new MySqlConnection("[Connectionstring]"))) 
    { 
    return repo.Messages.Where(m => m.MessageFrom == userId); 
    } 
} 

‚MyDataContext‘ ist die von DbLinq erzeugte Abbildung zu meiner Datenbank, die von Datacontext erbt. Ich benutze den Datenkontext hier nicht wieder (der obige Code sieht ein bisschen albern aus, aber ich wollte absolut sicher gehen, dass es kein Problem mit datacontext/mysqlconnection wiederverwendet).

Was passiert ist, egal welche der beiden Methoden ich anrufe, mit welcher userId, die Ergebnisse bleiben gleich. Zeitraum. Obwohl ich sehen kann, dass repo.Messages mehr als 10 Ergebnisse hat, mit variierenden Werten von MessageFrom und MessageTo, bekomme ich nur die zuerst abgefragten Ergebnisse zurück. Also, wenn ich rufe GetInbox(4374) es gibt mir Nachricht A und Nachricht B. Anruf GetInbox(526) hinterher gibt mir noch Nachricht A und B, auch wenn sind Nachrichten C und D wer tun haben eine userId von 526. Ich muss neu starten Anwendung, um Änderungen zu sehen.

Was geht hier vor? Ich bin mir sicher, dass ich etwas so Dummes mache, dass ich mich schäme, wenn mir jemand darauf hinweist. Wenn ich nicht etwas sehr dummes mache, dann finde ich dieses Thema sehr seltsam. Ich habe gelesen, dass DataContext nicht wiederverwendet wird, aber ich bin es nicht. Warum dieses Caching-Problem? Unten ist mein Controller-Code, aber ich bezweifle es ankommt:

[Authorize] 
public ActionResult Inbox(int userId) 
{ 
    Mailbox inbox = new Mailbox(userId, this.messageRepository.GetInbox(userId)); 
    return PartialView("Inbox", inbox); 
} 

Zwar gibt es ähnliche Fragen auf SO, habe ich nicht eine Antwort auf diese genaue Frage gefunden. Danke vielmals!

UPDATE: Ändern Sie den Code zu: return repo.Messages.ToList().Where(m => m.MessageFrom == userId); Fixes es, es in Ordnung, dann funktioniert. Scheint wie ein Cache-Problem. Ich möchte das natürlich nicht so regeln. Ändern des Codes, so dass der Datenkontext nicht nach der Abfrage entfernt wird nicht das Problem beheben.

+0

Das klingt vielleicht dumm, aber es hat nichts mit dem Asp.Net Caching-Mechanismus zu tun, oder? – Micah

+0

Nun, davor hatte ich zuerst Angst. Aber innerhalb meiner MailBox-Klasse kann ich klar sehen, dass ein leerer IEnumberable übergeben wird. Oder einen mit alten Daten für diese Angelegenheit. Das ist also nicht das Problem. – Razzie

+0

Wenn dies DbLinq ist (aus Ihrem Kommentar), sollten Sie die Tags umbenennen und ändern; DbLinq kann wenig teilen ... die Frage nach LINQ-to-SQL wird dir nicht helfen ;-p –

Antwort

1

Nun schien es, dass es ein Problem mit DbLinq war. Ich benutzte Quellcode von 3 Wochen alt und es gab einen Fehler in QueryCache (obwohl es immer war dort). Es gibt einen vollständigen Thread, der diese here abdeckt.

Ich aktualisierte die dblinq-Quelle. Querycache ist jetzt deaktiviert (impliziert einen Performance-Hit) und zumindest jetzt funktioniert es. Ich muss sehen, ob die Leistung akzeptabel ist. Ich muss gestehen, dass ich ein bisschen verwirrt bin, aber das, was ich versuche, ist ein übliches linq2sql-Muster. Danke allen.

1

Das Caching in LINQ-to-SQL ist mit DataContext verknüpft und beschränkt sich hauptsächlich auf das Identitätscaching. In den meisten Fällen wird eine Abfrage erneut ausgeführt, selbst wenn Sie dies zuvor getan haben. Es gibt ein paar Beispiele, wie .Single(x=>x.Id == id) (die spezielle Handhabung hat).

Da Sie jedes Mal einen neuen Datenkontext bekommen, glaube ich nicht, dass das der Täter ist. Ich bin aber auch etwas überrascht, dass der Code funktioniert ... sind Sie sicher, dass das repräsentativ ist?

LINQs Where Methode ist zurückgestellt - was bedeutet, dass sie erst ausgeführt wird, wenn Sie die Daten iterieren (zum Beispiel mit foreach). Aber zu diesem Zeitpunkt haben Sie bereits den Datenkontext entsorgt! Hast du etwas aus dem Beispiel geschnippt?

Auch - indem Sie es eine SqlConnection geben (die Sie dann nicht Dispose()), können Sie Auswirkungen auf die Bereinigung - es möglicherweise besser, geben Sie es einfach (den Datenkontext) die Verbindungszeichenfolge.

+0

Wenn ich darüber nachdenke, ist das wirklich seltsam. Der Code ist repräsentativ, mit der kleinen Ausnahme, dass ich eine statische Eigenschaft für eine Hilfsklasse hatte, die eine neue SqlConnection zurückgegeben hat. Die generierte DataContext-Klasse von SqlMetal (oder in meinem Fall DbMetal) gibt es einen Konstruktor, der eine IDbConnection nimmt, also habe ich nicht viel darüber nachgedacht. Vielleicht liegt das Problem bei DbLinqs Implementierung von IQueryable.Where (Func ) die zum einen die Ausführung nicht verzögert? Ich werde das morgen bei der Arbeit untersuchen. – Razzie

1

Ich habe einen ziemlich ähnlichen Code geschrieben, der gut zu funktionieren scheint. Der einzige Unterschied besteht darin, dass, wie Marc vorschlägt, ich die Verbindungszeichenfolge übergebe und ToList für die Where-Methode aufruft. Meine Datenbank wird nicht automatisch generiert, sondern von DataContext abgeleitet. Der Code ist unten.

class Program 
{ 
    static void Main(string[] args) 
    { 
     List<Item> first = GetItems("F891778E-9C87-4620-8AC6-737F6482CECB").ToList(); 
     List<Item> second = GetItems("7CA18DD1-E23B-41AA-871B-8DEF6228F96C").ToList(); 
     Console.WriteLine(first.Count); 
     Console.WriteLine(second.Count); 
     Console.Read(); 
    } 

    static IEnumerable<Item> GetItems(string vendorId) 
    { 
     using (Database repo = new Database(@"connection_string_here")) 
     { 
      return repo.GetTable<Item>().Where(i => i.VendorId.ToString() == vendorId).ToList(); ; 
     } 
    } 
} 
+0

Danke, morgen bei der Arbeit werde ich ToList einen Versuch geben. Es gibt nicht viele Unterschiede in unserem Code. – Razzie

+0

ToList() bewirkt, dass alle nachfolgenden linq-Operationen als linq to Objekte ausgeführt werden, anstatt in SQL-Anweisungen konvertiert zu werden.Ich würde empfehlen, ToList() nicht zu verwenden, bis es einen guten Grund dafür gibt. –

+0

Ich weiß, aber es ist in meiner Situation in Ordnung. Ich kann zumindest testen, ob dies mein Problem irgendwie behebt, obwohl ich denke, das ist Wunschdenken :-) – Razzie

1

Beginnen Sie mit dem Schreiben eines Tests. Dies wird Ihnen sagen, ob sich Linq2Sql korrekt verhält. Etwas wie:

Wenn das gelingt, sollten Sie wirklich überprüfen, ob es die verzögerte Ausführung ist, die Probleme verursacht. Sie sollten Posteingangsmeldungen sofort aufzählen.

Eine andere Sache, die Probleme verursachen kann, ist die Tatsache, dass Sie beginnen, aufzuzählen, wenn der Datenkontext bereits entsorgt wird. Der einzige Weg, dies zu lösen, besteht darin, es überhaupt nicht zu entsorgen (und darauf zu vertrauen, dass der GC es aufräumt, wenn es den Gültigkeitsbereich verlässt), oder ein benutzerdefiniertes IDisposable-Objekt zu entwickeln, damit Sie es verwenden können. Etwas wie:

using(var inboxMessages = this.messageRepository.GetInbox(userId1)) 
{ 
    Assert.That(inboxMessages.All(m => m.MessageTo == userId1); 
} 
0

Ich würde vermeiden, DBLinq für Produktionscode zu verwenden ... viele Linq-To-SQL-Funktionen sind nicht implementiert, und das Durchlaufen des Quellcodes zeigt einen niedrigen Reifegrad ... viele der Methoden sind nicht implementiert oder als "nicht abgeschlossen" markiert.

... Sie wurden gewarnt!

+0

Sie haben Recht. Bei der Verwendung von DBLinq und vielen Methoden, die tatsächlich nicht implementiert wurden, bin ich auf viele Fehler gestoßen. Kurz nachdem ich diese Frage gestellt habe, wechsle ich von DBLinq zum Devart Linq2Sql Framework für MySql und es ist viel, viel besser. Ich hatte sehr wenig Probleme damit. Leider ist es nicht kostenlos, aber am Ende hat es mich viel mehr gerettet! – Razzie