2016-03-24 8 views
0

Ich benutze ASP.MVC 5 und EF6. Mein Zweck besteht darin, die Datenbank mit Daten von einem anderen Server zu füllen und die Benutzerschnittstelle nicht zu blockieren.ASP.MVC EF6 Basis Füllung blockiert Benutzeroberfläche

Das Füllen der Datenbank dauert sehr lange, etwa 10-15 Minuten. Und der Benutzer kann zu diesem Zeitpunkt mit dem System arbeiten. Jetzt hat er einige Minuten lang einen sehr schlechten Frost. Also, die Tatsache ist, dass Benutzer Daten von der Basis laden und Aufgabe laden Daten in die Datenbank auf einmal.

Ich habe versucht, Datenbank-Füllcode zu einem anderen Threads zu setzen, aber es hat nicht geholfen. Wenn sich der Benutzer zum ersten Mal anmeldet, führt er eine Aktion aus, die das Füllen der Datenbank startet. Nach dieser Aktion kann der Benutzer alles andere tun und die Schnittstelle darf nicht blockiert werden.

Wenn Benutzer das System verwenden, muss er immer einige Daten aus meiner Datenbank laden. Zum Beispiel: wenn der Benutzer eine Seite auf 1 Minute sieht, gibt es 10000 geladene Daten, nach 2 Minuten gibt es mehr Daten und so weiter.

Und es gibt eine andere Frage: Ich habe die Fähigkeit, Daten mit etwa 10 Threads auf einmal zu downloaden. Wie kann ich diesen Code ändern, um dies zu tun?

p.s. Code kann hier einige Tippfehler enthalten

Antwort

0

Verwenden Sie nicht die Thread Klasse, es ist eine alte obskure, schwer zu verstehen/zu verwenden API, die für 99% der Anwendungsfälle obsolet geworden ist. Bevorzugen Sie Task.Run für CPU gebundene Arbeit, obwohl das nicht in asp.net verwenden. asp spielt nicht gut mit dir, wenn du Threads daraus stehlst, und es ist einfach, Deadlocks zu erstellen.

Wenn es mit einem echten asynchronen api (wie DbContext.SaveChangesAsync()) zu tun, nur async/await verwenden, nicht der Mühe, überhaupt mit Fäden, sie Ihnen in einem Web-Kontext keine Performance-Gewinne nicht geben, und sie es sogar verletzt (und auch die Skalierbarkeit).

0

Versuchen:

 public async Task<ActionResult> SomeAction() 
    { 
     await Task.Run(() => { ImportTask(); }); 
     return View(); 
    } 

    public async Task ImportTask() 
    { 
     using (var context = new ApplicationDBContext()) 
     { 
      context.Configuration.AutoDetectChangesEnabled = false; 
      for (var i = 0; i < 180; i++) 
      { 
       var data = GetSomeDataFromAnotherServer(i); 
       foreach (var dataPart in data) 
       { 
        var stat = context.Data.FirstOrDefault(x => x.EnumProperty == dataPart.EnumProperty && x.LongProperty == dataPart.LongProperty && x.DateTimeProperty == dataPart.DateTimeProperty) 
         ?? new DataType() 
                                                       { 
        ... 
        (some 
        another stat 
        filling) 

        context.Statistics.AddOrUpdate(stat); 
       } 
      } 
      await context.SaveChangesAsync(); 
     } 
    } 

Mit using (var context = new ApplicationDBContext()) innerhalb for (var i = 0; i < 180; i++) Ergebnisse in 180 schaffen & dispose Operationen von DbContext. Sie können context.Configuration.AutoDetectChangesEnabled = false; oder https://efbulkinsert.codeplex.com/

+0

Ich kann nicht sehen, nur 1 AppDBContext für alle Operationen zu verwenden. Problem ist, dass Kontext nach 1-2 Minuten mit Ausnahme plötzlich geschlossen werden kann. Wir vermeiden dieses Problem mit dem Owin-Kontext, aber es funktioniert nur für Aktionen und Task ist keine Aktion. – stationfuk

+0

Erstellen Sie den Kontext nach n Einfügungen neu oder verwenden Sie "System.Data.SqlClient.SqlBulkCopy". Ausnahme nach 1-2 Minuten ist wahrscheinlich "OutOfMemoryException". Siehe: [Schnellste Methode zum Einfügen in Entity Framework] (http: // stackoverflow.com/a/5942176/5246410) –

+0

'SaveChanges' sollte auch außerhalb der Schleife sein, so dass Sie nicht für jede Elementaktualisierung mit Datenbankaufruf enden. –

0

auch für Masseneinfügungen verwenden, um zu beschleunigen, Sie müssen keine Raw-Threads erstellen oder Aufgaben über Task.Run() in einem ASP.NET-Kontext ausführen, als Es ist hauptsächlich ein Performance-Hindernis in einem ASP.NET-Kontext. Ich würde es nicht für eine "Feuer-und-Vergessen" -Implementierung verwenden, da es höchst unzuverlässig ist. Wenn du ein Feuer wolltest und vergisst, dass du es benutzen kannst: HostingEnvironment.QueueBackgroundWorkItem, aber ich bin mir nicht sicher, ob das der beste Ansatz ist.

Haben Sie darüber nachgedacht, möglicherweise einen anderen Prozess zu erstellen, um dies durchzuführen, statt während einer Webanforderung? Vielleicht könnte ein Dienst, der auf dem Server läuft, überwachen, wenn sich jemand anmeldet und Daten bewegt? Eine andere Möglichkeit könnte sein, dass Sie diese Datenbewegung von einem Javascript im Browser aus aufrufen, um es abzuschalten?