加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > asp.Net > 正文

asp.net-mvc – 使用Web API不检查的并发检查

发布时间:2020-12-16 06:27:40 所属栏目:asp.Net 来源:网络整理
导读:使用EntityFramework v6,我整理了一个原型来演示Web Api和桌面应用程序中的并发检查. 实体: public static class IRowVersionExtensions{ public static string RowVersionAsString(this IRowVersion ivr) { return Convert.ToBase64String(ivr.RowVersion)
使用EntityFramework v6,我整理了一个原型来演示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; }
}

的DbContext:

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

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

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

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

Api控制器更新操作:

[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.');
    });
};

当我在控制器动作中断行时:

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

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

有人可以帮助我理解为什么它在桌面应用程序中运行良好但在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()之后执行(反之亦然),则会遇到相同的情况.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读