CodeGo.net>当行可能不存在时,如何原子递增?
作者:互联网
假设我有一个DbSet< Item>派生自DbContext的MyContext.项,其中项定义如下.
public class Item
{
[Key]
public string Key { get; set; }
[ConcurrencyCheck]
public int Value { get; set; }
}
给定一个键,我想以原子方式递增相应的值,其中表中尚未存在的键的隐式值为0.也就是说,以原子方式递增表中不存在的键的结果为1.我目前的做法:
public static async Task IncrementAsync(string key)
{
using (var context = new MyContext())
{
while (true)
{
var item = await context.Items.FindAsync(key);
if (item == null)
{
context.Items.Add(new Item { Key = key, Value = 1 });
}
else
{
item.Value++;
}
try
{
await context.SaveChangesAsync();
break;
}
catch (DbUpdateException)
{
continue;
}
}
}
}
当对IncrementAsync的许多调用同时运行时,此操作将因实时锁定而失败.
>正确的方法是什么?
> while循环是否应该在使用范围之外,以便每次尝试都获得一个新的上下文?我尝试了一下,它使一切正常,但是我觉得创建和破坏这么多上下文的效率很低.
>我是否缺少有关上下文跟踪更改方式的信息?
我的实体框架经验基本上只用于查询,因此,如果您能解释这段代码中我做错的更好的细节,我将非常感谢.
编辑
因为选择的答案没有明确,所以将正确的代码放在这里.请注意,在DbUpdateException之后如何从不重用上下文.
public static async Task IncrementAsync(string key)
{
while (true)
{
using (var context = new MyContext())
{
var item = await context.Items.FindAsync(key);
if (item == null)
{
context.Items.Add(new Item { Key = key, Value = 1 });
}
else
{
item.Value++;
}
try
{
await context.SaveChangesAsync();
break;
}
catch (DbUpdateException)
{
continue;
}
}
}
}
解决方法:
您需要不尝试之间共享上下文.如果根本不对具有DbUpdateException的上下文执行任何操作,就好像您没有明确清理该上下文一样,它可能永远不会返回正常状态.
我希望外部环境会引起问题.如果根据时间发生对单个键的并发调用,则可能会创建错误的上下文设置(由于错误处理程序,该上下文设置会不断被忽略.
除非我误会了数据库中存在密钥的事实,否则不会删除“要添加”的版本.您最终将遇到以下情况之一:
Add "1", 2
要么
Add "1", 1
Update "1", 2
取决于您的第二次迭代是捕获第一次迭代的对象还是新的对象.
这些都不成功,因此您会遇到连续错误.
标签:entity-framework-6,c,net,entity-framework 来源: https://codeday.me/bug/20191120/2042929.html