gpt4 book ai didi

asp.net-mvc - 未检查 Web API 的并发检查

转载 作者:行者123 更新时间:2023-12-03 01:22:58 26 4
gpt4 key购买 nike

使用 EntityFramework v6,我构建了一个原型(prototype)来演示 Web Api 以及桌面应用程序中的并发检查。

实体:

public static class IRowVersionExtensions
{
public static string RowVersionAsString(this IRowVersion ivr)
{
return Convert.ToBase64String(ivr.RowVersion);
}

public static void SetRowVersion(this IRowVersion ivr, string rowVersion)
{
ivr.RowVersion = Convert.FromBase64String(rowVersion);
}
}

public interface IRowVersion
{
byte[] RowVersion { get; set; }
}

public class Department : IRowVersion
{
[Key]
public int Id { get; set; }

[Required, MaxLength(255)]
public string Name { get; set; }

public string Description { get; set; }

[Timestamp]
[ConcurrencyCheck]
public byte[] RowVersion { get; set; }
}

数据库上下文:

public class CompDbContext : DbContextEx
{
public CompDbContext()
: base("Company")
{
this.Configuration.LazyLoadingEnabled = false;
}

public DbSet<Department> Departments { get; set; }
}

桌面应用程序(控制台应用程序)具有以下代码,并按预期抛出 DbConcurrencyException:http://pastebin.com/i6yAmVGc

现在,API Controller - 当我在两个窗口中打开页面并编辑一个(并保存)然后尝试编辑/保存另一个时,它不会抛出异常:

API Controller 更新操作:

[HttpPatch, Route("")]
public Department UpdateDepartment(Department changed)
{
var original = dbContext.Departments.Find(changed.Id);
if (original == null)
this.NotFound();

if (Convert.ToBase64String(changed.RowVersion) != Convert.ToBase64String(original.RowVersion))
Console.WriteLine("Should error.");

original.RowVersion = changed.RowVersion;
original.Name = changed.Name;
original.Description = changed.Description;

dbContext.SaveChanges();

return original;
}

API 调用:

DepartmentVM.prototype.onSave = function (entity) {
var method = entity.id() ? 'PATCH' : 'PUT';
$.ajax({
url: '/api/departments',
method: method,
data: ko.toJSON(entity),
contentType: 'application/json',
dataType: 'JSON'
})
.done(function (data) {
alert('Saved');
entity.rowVersion(data.rowVersion);
entity.id(data.id);
})
.error(function (data) {
alert('Unable to save changes to department.');
});
};

当我在 Controller 操作中中断线路时:

if (Convert.ToBase64String(changed.RowVersion) != Convert.ToBase64String(original.RowVersion))

在第一次保存时,changed.RowVersion == original.RowVersion(完美)并且保存(如预期)。在第二页的保存中,changed.RowVersion!=original.RowVersion(完美)但它仍然保存,没有异常(exception)(不符合预期)。

有人可以帮助我理解为什么这在桌面应用程序中运行良好但在 Web API 中不起作用吗?

最佳答案

它不起作用,因为 EF 使用 RowVersion 的“原始”值来执行并发检查。在您的示例中,原始值(就 DbContext 而言)是来自数据库的值,因为它是使用 .Find() 从数据库加载的。

例如,更改的实体的 RowVersion 是 1,数据库中当前的 RowVersion 是 2...

// changed's RowVersion is 1

var original = dbContext.Departments.Find(changed.Id);
// original's RowVersion is 2

if (original == null)
this.NotFound();

if (Convert.ToBase64String(changed.RowVersion) != Convert.ToBase64String(original.RowVersion))
Console.WriteLine("Should error."); // 2 != 1, so prints this line


original.RowVersion = changed.RowVersion;
// original's "current" RowVersion is now 1
// ... but its "original" RowVersion is still 2!

original.Name = changed.Name;
original.Description = changed.Description;

dbContext.SaveChanges();
// UPDATE DEPT SET ... WHERE Id = ... AND RowVersion = 2
// (works, therefore no concurrency exception)

要实现此目的,您只需将传入实体添加到上下文中...

[HttpPatch, Route("")]
public Department UpdateDepartment(Department changed)
{
dbContext.Entry(changed).State = EntityState.Modified;
dbContext.SaveChanges();

// you'll get an exception if RowVersion has changed

return changed;
}

如果您只想更改名称和描述,您可以有选择地将这些属性标记为已更改,其余属性不更新...

[HttpPatch, Route("")]
public Department UpdateDepartment(Department changed)
{
dbContext.Entry(changed).State = EntityState.Unchanged;
dbContext.Entry(changed).Property(d => d.Name).IsModified = true;
dbContext.Entry(changed).Property(d => d.Description).IsModified = true;
dbContext.SaveChanges();

// you'll get an exception if RowVersion has changed

return changed;
}

控制台应用程序工作的原因有点幸运。存在竞争条件,如果 t1 中的 Find()t2 中的 SaveChanges() 之后执行(反之亦然),你会遇到同样的情况。

关于asp.net-mvc - 未检查 Web API 的并发检查,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21994266/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com