繁体   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