6

Ich denke wahrscheinlich nicht in die richtige Richtung. Ich bin ziemlich neu in Dependency Injection und ASP.Net Core.DbContext für Hintergrundaufgaben über Dependency Injection

Ich habe eine ASP.Net Core-Website, und eine der Aufgaben besteht darin, Daten aus einem Excel-Blatt in eine Datenbank zu importieren, die ein Benutzer hochladen wird. Die Excel-Tabellen können riesig sein, und die Datentransformationsaufgaben sind zeitaufwendig, daher möchte ich sie im Hintergrund ausführen. h. Der Benutzer lädt das Blatt hoch, die Antwort wird sofort gesendet und der Hintergrund-Job/Thread importiert die Daten.

Ich versuche, den Hintergrund-Job von laufen:

Task.Run(() => ProcessImport(model)); 

Das Problem, das ich laufen in ist, dass der Prozess Importmethode Dienste aufruft, die die AppDbContext über ASP.Net Dependency Injection Container Zugriff auf die Repository-Klassen ist als Scoped hinzugefügt und sobald die Antwort zurückgeschickt wird, wird der Kontext entsorgt. Ich bekomme eine Laufzeitausnahme, dass Sie einen Kontext nicht verwenden können, nachdem es entsorgt wurde.

Meine Frage ist, was ist der beste Weg, um mit dieser Situation umzugehen? Soll ich den AppDbContext Singleton machen? Soll ich eine neue Instanz von AppDbContext in der ProcessImport-Methode erstellen und weitergeben? Ich habe DbContext gelesen ist Thread nicht sicher, also ist das ein guter Ansatz?

+0

Nein: https://stackoverflow.com/questions/3266295/net-entity-framework-and-transactions/3266481#3266481 – Steven

+0

Sie haben Recht, aber wie mit laufenden Aufgaben in einem anderen Thread, die auf DbContext zugreifen müssen? –

+3

Das ist sowieso ein gefährliches Muster. Sie sollten keine ASP.Net-Anwendung verwenden, um [lang andauernde Fire & Forget-Aufgaben auszuführen] (http://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx). Wenn Sie es richtig machen, wird die Aufgabe in einem eigenen Prozess ausgeführt und das gesamte DI-Problem ist weg. –

Antwort

10

Sie sollten IServiceScopeFactory Instanz (es ist Singleton) in Ihre Aufgabe übergeben.

Innerhalb Aufgabe, wenn Daten ankommen, sollten Sie neue CreateScope() erstellen und Dienste aus diesem Bereich anfordern. Wenn der Datenprozess abgeschlossen ist, entfernen Sie diesen Bereich (aber halten Sie für den nächsten Lauf den Verweis auf IServiceScopeFactory).

Siehe zum Beispiel this. Ich leite kleine und schnelle Aufgaben mit dieser Bibliothek.

Für schwere/lange laufende Aufgaben, wie Gert schrieb, verlassen Sie sich nicht darauf, dass Ihre Aufgabe immer vollständig ausgeführt wird. Seien Sie bereit für den Neustart, seien Sie bereit für die erneute Verarbeitung der gleichen Daten.

+0

Ich verstehe nicht vollständig, wie man es in meinem Szenario anwendet. Ich habe eine Klasse namens ImportService, die vom Controller aufgerufen wird, wo ich derzeit die Repository-Klassen injiziere, um auf die Datenbank zuzugreifen. Ich möchte diese Repository-Klassen in meiner ProcessImport-Methode verwenden. Wenn ich das richtig verstanden, wenn ich den Körper des Verfahrens mit mit (var scope = ServiceScopeFactory.CreateScope()) einschließen {} wird es die injizierten Klassen zuletzt für die gesamte Lebensdauer des Blocks machen, oder wird es injizieren eine neue Instanz dieser Klassen? –

+0

Nein, der neue Bereich gibt Ihnen eine neue Instanz der Klassen db (und anderer Gültigkeitsbereiche). Wenn Sie nicht möchten, dass der Benutzer [request] darauf wartet, dass Ihr ProcessImport abgeschlossen ist, dürfen Sie keine Instanzen mit lebenslangem Gültigkeitsbereich mit ihm teilen. Der Anforderungsumfang wird bei der Beendigung der Anforderung beseitigt, wodurch der gesamte verfügbare Inhalt darin verfügbar ist. – Dmitry

3

Zum Abbau Ihre Fragen:

  1. was ist der beste Weg, um diese Situation zu bewältigen?

    Es ist nicht ideal, dass die API die lang andauernden Aufgaben bewältigt. Sie können den Prozess an eine Hintergrundanwendung delegieren

  2. Sollte ich den AppDbContext Singleton machen?

    Der dbContext sollte in einem Webanwendungsszenario nicht Singleton sein, da dies Probleme wie die Verwaltung von Transaktionen darstellen kann.

  3. Was ist der beste Weg, um mit dieser Situation umzugehen?

    Aufschlüsselung der verschiedenen Dienste/Prozess:

    • Die API, die eine Datei nehmen. Im Idealfall sollte die API die Datei nur als Eingabe nehmen und auf der Festplatte oder in der Datenbank speichern.
    • Ein Dienst, der die Datei verarbeitet. Erstellen Sie diese Komponente/diesen Dienst als separate Bibliothek. Dann konsumiere diese Bibliothek von einer Konsolen-App. Auf diese Weise können Sie die Leistung profilieren. Machen Sie es zum Feuern und vergessen Sie die Methode, indem Sie es als asynchrone Methode verwenden.Auf diese Weise haben Sie die Flexibilität, Ihren Code wiederzuverwenden.
    • Wenn Sie nicht viele Dateiuploads erwarten, können Sie die Bibliothek auf Ihrem API-Controller wiederverwenden und die Methode mit QueueBackgroundWorkItem ausführen. Siehe hier als Referenz: http://www.hanselman.com/blog/HowToRunBackgroundTasksInASPNET.aspx
  4. Soll ich eine neue Instanz von AppDbContext im ProcessImport Methode erstellen, und übergibt es an?

    Da es nicht Thread-sicher ist, erstellen und verwenden Sie in jedem Thread eine separate Instanz Ihrer dbContext-Klasse.

  5. Ich habe gelesen DbContext ist nicht threadsicher, also ist das ein guter Ansatz?

    Für eine ausführliche Anleitung von EF DbContext mit, Check-out in diesem Blog: "Soll ich die AppDbContext Singleton machen" http://mehdi.me/ambient-dbcontext-in-ef6/