我有一個MVC3/.NET 4應用程序,它採用實體框架(4.3.1代碼優先)嘗試在一個異步方法失敗
我已經EF包裹成一個存儲庫/的UnitOfWork圖案作爲這裏所描述...
通常,因爲它在文章中解釋說,當我需要一個新紀錄的創造我一直在做這個...
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()方法中。
這是否與事實,我試圖共享unitOrWork,因此跨線程dbContext?
如果是這樣,有人可以友好地解釋爲什麼這是一個問題?
我應該給Notify方法一個新的unitOfWork實例嗎?
我是否使用正確的模式/類來異步調用方法?或者,我應該使用的東西沿線....
new System.Threading.Tasks.Task(()=> {Notify(course);})。
我必須說我已經變得非常混淆異步,並行和併發的條款!
任何指向文章的鏈接(c#async for idiots)將不勝感激!
非常感謝。
UPDATE:
多一點挖了我這個SO頁:https://stackoverflow.com/a/5491978/192999它說...
「要知道,雖然EF上下文不是線程安全的,即不能使用多個線程中的相同上下文。「
...所以我想要達到不可能的目標嗎?這是否意味着我應該爲我的新線程創建一個新的IUnitOfWork實例?
我建議創建另一個Service類並在那裏移動Notify方法。在這個新的服務中,確保你有一個構造函數,它會創建一個新的unitOfWork,它具有與你在主線程中使用的不同的DbContext。或者更好的做法是,在主線程中移動所有與數據庫相關的調用,並保持生成的線程數據庫無需通話。 – rikitikitik
@rikitikitik謝謝,我只是按照你的建議去做,並且產生了一個新的unitOfWork,而且所有的工作似乎都在進行中。我現在需要在構造函數中整理一些東西。 Re:將所有數據庫調用留在主線程上。這意味着用戶不得不等待所有數據庫調用完成,這是我想避免的。 – ETFairfax
很高興你有它的工作。如果數據庫調用也需要很長時間(我雖然只是PDF創建),然後是的,移動它們。 – rikitikitik