2013-06-12 38 views
3

我有一個MVC3/.NET 4應用程序,它採用實體框架(4.3.1代碼優先)嘗試在一個異步方法失敗

我已經EF包裹成一個存儲庫/的UnitOfWork圖案作爲這裏所描述...

http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

通常,因爲它在文章中解釋說,當我需要一個新紀錄的創造我一直在做這個...

public ActionResult Create(Course course) 
{ 
    unitOfWork.CourseRepository.Add(course); 
    unitOfWork.Save(); 
    return RedirectToAction("Index"); 
} 

Howeve r,當不僅僅是將記錄保存到數據庫時,我需要將邏輯包裝到我稱爲IService的內容中。例如...

private ICourseService courseService; 
public ActionResult Create(Course course) 
{ 
    courseService.ProcessNewCourse(course); 
    return RedirectToAction("Index"); 
} 

在我的服務我有類似如下的...

public void ProcessNewCourse(Course course) 
{ 
    // Save the course to the database… 
    unitOfWork.CourseRepository.Add(course); 
    unitOfWork.Save(); 

    // Generate a PDF that email some people about the new course being created, which requires more use of the unitOfWork… 
    var someInformation = unitOfWork.AnotherRepository.GetStuff(); 
    var myPdfCreator = new PdfCreator(); 

    IEnumerable<People> people = unitOfWork.PeopleRepository.GetAllThatWantNotifiying(course); 

    foreach(var person in people) 
    { 
     var message = 「Hi 」 + person.FullName; 
     var attachment = myPdfCreator.CreatePdf(); 
     etc... 
     smtpClient.Send();   
    } 
} 

上面沒有實際的代碼(我的應用程序有沒有關係的課程,我使用視圖模型,並且我已經將PDF創建和電子郵件信息分離到其他類中),但正在發生的事情的要點如上所述!

我的問題是,生成PDF並通過電子郵件發送出去需要一些時間。用戶只需要知道該記錄已保存到數據庫,所以我想我會把代碼放在unitOfWork.Save()之下;變成一個異步方法。然後用戶可以被重定向,服務器可以愉快地花時間處理郵件,附件以及任何我需要它來做後保存。

這是我掙扎的地方。

我已經嘗試了幾件事情,目前正在ICourseService以下...

public class CourseService : ICourseService 
{ 

    private delegate void NotifyDelegate(Course course); 
    private NotifyDelegate notifyDelegate; 

    public CourseService() 
    { 
     notifyDelegate = new NotifyDelegate(this.Notify); 
    } 

    public void ProcessNewCourse(Course course) 
    { 
     // Save the course to the database… 
     unitOfWork.CourseRepository.Add(course); 
     unitOfWork.Save(); 

     notifyDelegate.BeginInvoke(course); 
    } 

    private void Notify(Course course) 
    { 
     // All the stuff under unitOfWork.Save(); moved here. 
    } 
} 

我的問題/問題

我隨機得到的錯誤:「已經有一個打開與此命令關聯的DataReader,必須先關閉它。「在Notify()方法中。

  1. 這是否與事實,我試圖共享unitOrWork,因此跨線程dbContext?

  2. 如果是這樣,有人可以友好地解釋爲什麼這是一個問題?

  3. 我應該給Notify方法一個新的unitOfWork實例嗎?

  4. 我是否使用正確的模式/類來異步調用方法?或者,我應該使用的東西沿線....

    new System.Threading.Tasks.Task(()=> {Notify(course);})。

    我必須說我已經變得非常混淆異步,並行和併發的條款!

  5. 任何指向文章的鏈接(c#async for idiots)將不勝感激!

非常感謝。

UPDATE:

多一點挖了我這個SO頁:https://stackoverflow.com/a/5491978/192999它說...

「要知道,雖然EF上下文不是線程安全的,即不能使用多個線程中的相同上下文。「

...所以我想要達到不可能的目標嗎?這是否意味着我應該爲我的新線程創建一個新的IUnitOfWork實例?

+1

我建議創建另一個Service類並在那裏移動Notify方法。在這個新的服務中,確保你有一個構造函數,它會創建一個新的unitOfWork,它具有與你在主線程中使用的不同的DbContext。或者更好的做法是,在主線程中移動所有與數據庫相關的調用,並保持生成的線程數據庫無需通話。 – rikitikitik

+0

@rikitikitik謝謝,我只是按照你的建議去做,並且產生了一個新的unitOfWork,而且所有的工作似乎都在進行中。我現在需要在構造函數中整理一些東西。 Re:將所有數據庫調用留在主線程上。這意味着用戶不得不等待所有數據庫調用完成,這是我想避免的。 – ETFairfax

+0

很高興你有它的工作。如果數據庫調用也需要很長時間(我雖然只是PDF創建),然後是的,移動它們。 – rikitikitik

回答

0

您可以創建一個輪詢後臺線程,與您的主流分開執行冗長的操作。此線程可以掃描數據庫中的新項目(或標記爲處理的項目)。這個解決方案非常簡單,即使應用程序崩潰也可以確保作業完成(當輪詢線程再次啓動時,它將被拾取)。

如果在請求文檔之後以及生成/發送之前應用程序崩潰的情況下,如果請求「丟失」並不可怕,也可以使用Synchronised Queue

有一件事情幾乎可以肯定 - 正如rikitikitik所說 - 你將需要使用一個新的工作單元,這意味着一個單獨的交易。

你也可以看看Best threading queue example/best practice