[英]Do I really need to create a new transaction here?
我正在從MVC控制器發送電子郵件。
[HttpPost]
public async Task<ActionResult> Send(SendModel model)
{
var userId = HttpContext.User.Identity.GetUserId();
// This method takes current user ID and retrieves the user from the DB
var thisUser = await GetThisApplicationUserAsync();
if (thisUser.FeedbackSendLimit <= 0) return RedirectToActionWithMessage(MessageType.Error, "You can't send emails anymore! You have exceeded your send limit!", "Send");
// Handling the case when the passed model is invalid
if (!ModelState.IsValid) return View(model);
using (var transaction = _dbContext.Database.BeginTransaction())
{
// _dbContext is a DbContext initialized in the controller's constructor
var companiesToSend = _dbContext
.Companies
.Where(x => model.CompanyIds.Contains(x.Id))
.ToArray();
try
{
// Each user has a limit of emails they can send monthly
thisUser.FeedbackSendLimit -= 1;
// Each company has a limit of how many emails we can address them as well
foreach (var company in companiesToSend)
{
company.FeedbackCounter -= 1;
}
var newSend = new FeedbackSend
{
Id = Guid.NewGuid(),
UserId = userId,
SentAt = DateTime.Now,
Companies = companiesToSend
};
_dbContext.FeedbackSends.Add(newSend);
await _dbContext.SaveChangesAsync();
// This generates an HTML email and sends it to specified email address
await SendFeedbackEmailAsync(model.ToEmail, thisUser, companiesToSend);
transaction.Commit();
}
catch (Exception e)
{
transaction.Rollback();
return RedirectToActionWithMessage(MessageType.Error, "An error occurred while trying to send feedback", "Send");
}
}
return RedirectToActionWithMessage(MessageType.Success, "Sent successfully", "Send");
}
這里有兩個問題:1.我真的需要在這里進行交易嗎? 在這種情況下,使用_dbContext.SaveChanges()
是否足夠? 如果SendFeedbackEmailAsync
失敗並且沒有發送電子郵件,我使用事務來還原所有內容。
transaction.Commit()
似乎沒有更新thisUser.FeedbackSendLimit
。 我是否應該using
block在事務中檢索用戶以使其正常工作? 技術:
您是否需要顯式事務:否。如果Try塊完成並調用SaveChanges,則更改將作為一項有效事務提交。 如果捕獲到異常,則不會發生SaveChanges,因此在處理Context時將回滾事務。
為什么不保存您的用戶更改? 這很可能是因為您的用戶是由GetThisApplicationUserAsync()方法中的另一個DbContext實例加載的,或者是由AsNoTracking()加載的或以其他方式分離的。
檢索數據並執行更新時,請在單個DbContext實例的范圍內進行操作。
using (var context = new MyDbContext())
{
var thisUser = context.Users.Where(x => x.Id == userId).Single();
var companiesToSend = context.Companies
.Where(x => model.CompanyIds.Contains(x.Id))
.ToArray();
//....
從那里,當調用上下文SaveChanges時,上下文會跟蹤該用戶並將其持久化。 處理它的較丑陋的方法是檢查上下文是否跟蹤thisUser(否)或上下文跟蹤具有相同PK的用戶(否,如果有新的DbContext),如果不是,則將該用戶附加到該用戶上下文,但是首先需要將用戶實例從它可能仍附加到的任何DbContext實例中分離出來。 凌亂。
我不喜歡初始化模塊級DbContext,而是確保實例被實例化並放置在所需的范圍內。 模塊級上下文使得難以預測方法鏈,其中某些方法決定調用SaveChanges時可能會無意中保存更改,並導致顯式事務的奇數放置,從而試圖規范行為。 使用塊使事情變得容易得多。 如果要DI上下文,則建議考慮使用Repository模式和/或DbContextFactory / UnitOfWork依賴項來啟用對結果(存儲庫)的模擬或對DbContext(工廠)的模擬。
我的首選模式是存儲庫(非通用)/ w Mehdime的工作單元的DbContextScopeFactory / Locator模式。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.