【发布时间】:2020-01-25 20:08:24
【问题描述】:
我有一个网络应用程序,用户可以在其中发布产品报价。
有时 - 少于十分之一的情况 - 保存报价会导致数据库中的多个条目具有相同的数据(通常是 2 个条目,但已经出现 3 个甚至 4 个相同的条目)。
offer 类是一个简单的实体类,其中包含一些属性。它还连接到另外两个实体类 OfferImage 和 OfferCategory,它们存储关联的图像和商品应出现的类别。
将项目保存到数据库的代码如下(存储库类的一部分):
public class OfferRepository {
...
public async Task InsertAsync(Offer offer)
{
Context.Offer.Add(offer);
await Context.SaveChangesAsync();
}
...
}
在服务类中调用:
public class OfferService
{
...
public async Task CheckinAsync(Offer offer)
{
await repository.InsertAsync(offer);
}
...
}
这个服务类的那个方法被一个mvc控制器调用:
public async Task<IActionResult> Create(CreateOfferViewModel createOfferViewModel)
{
if (ModelState.IsValid)
{
...
//conversion of the view model object to an Offer object
...
await offerService.CheckinAsync(offer);
}
...
}
如您所见,结构相对简单。但是,此错误经常发生。
服务类的生命周期通过 startup.cs 中的依赖注入进行管理。 OfferRepository 和 OfferService 都添加了 scoped (services.AddScoped) 上下文类是这样添加的:
services.AddDbContext<Context>(options =>
{
options.UseSqlServer(
Configuration.GetConnectionString("DataConnection"),
sqlServerOptionsAction: sqlServerOptions =>
{
sqlServerOptions.EnableRetryOnFailure(maxRetryCount: 3, maxRetryDelay: TimeSpan.FromSeconds(3), errorNumbersToAdd: null);
}
);
});
为了进一步缩小问题范围,我运行了一个分析器并记录了 INSERT 语句(按时间顺序):
- SPID 59 - 2019-09-24 16:05:19.670 - 插入报价 ...
- SPID 57 - 2019-09-24 16:05:19.673 - 插入报价 ...
- SPID 59 - 2019-09-24 16:05:19.710 - 插入 OfferImage .../插入 OfferCategory ...
- SPID 57 - 2019-09-24 16:05:19.760 - 插入 OfferImage .../插入 OfferCategory ...
让我怀疑的是,执行 INSERT 有两个不同的进程 ID。由于默认情况下 DbContext 是作用域的,因此应该只有一个进程 ID,所有语句都在该进程 ID 下执行 - 还是我错了?如果我没记错的话,这意味着两个请求是并行执行的,这反过来又引发了关于如何发生这种情况的进一步问题。
如您所见,我有点困惑,希望能得到可以解释这一点或观察并解决过类似问题的人的帮助。
(SQL Server 是 13/2016 版,EF Core 是 2.2 版)
非常感谢!
【问题讨论】:
-
快速提问,您确定端点没有被客户端调用两次吗?
-
感谢您的提示。也是我最初的想法之一,所以我测试了它。但是多次点击网站上的“保存”按钮不会导致多次输入。以及多个请求,例如对于我们的用户和发生这种情况的情况,可以排除 Fiddler。在测试过程中,我什至能够自己观察到这种行为,而且我非常确定我只触发了一个请求。
-
您也可以尝试使用 Postman 将一个请求发送到您的端点。另一个问题,Offer 中是否有什么独特之处可以确保它被添加一次?然后你可以用它来做一个 AddOrUpdate ..
-
您如何管理 Context(以及服务和 repo)的生命周期?你在使用依赖注入吗?如果是这样,您正在使用什么生命周期(瞬态、范围等)。如果在不同的请求中使用相同的上下文,这可能是一个潜在的原因。
-
感谢您指出这一点。我将尝试用 Postman 重现问题并进一步调查。我还将 Insert() 更改为 Update() (也可以使用它,因为 Offer 附加了一个自动键)。但不幸的是,问题并没有解决。但即使它已经解决了,我也想知道发生了什么。
标签: c# sql-server asp.net-core asp.net-core-mvc entity-framework-core