簡體   English   中英

我真的需要在這里創建新交易嗎?

[英]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失敗並且沒有發送電子郵件,我使用事務來還原所有內容。

  1. transaction.Commit()似乎沒有更新thisUser.FeedbackSendLimit 我是否應該using block在事務中檢索用戶以使其正常工作?

技術:

  • 實體框架6.0
  • ASP.NET MVC 5

您是否需要顯式事務:否。如果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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM