用于内容版本控制的表不同步

本文关键字:同步 版本控制 用于 | 更新日期: 2023-09-27 18:10:22

我已经用两个表实现了内容版本控制。我知道这可能不是最佳实现,但这是我必须要做的。

一个是Contents,它有一个int Id和其他字段,如Title, HtmlContent等。

另一个是ContentVersions。它有一个指向Content Id的外键,还有一个表示内容修订的Version字段,还有一个IsCurrent标志,表示哪个版本是当前版本(其副本存在于Contents表中)。它也有其他字段,如标题,HtmlContent等。

当用户更新内容时,系统应该创建一个新的ContentVersion并更新相应的content。我正在使用实体框架,它看起来像这样:

        // ASP.NET MVC Action
        [HttpPost]
        public JsonResult Save(ContentVersion contentVersion)
        {
            using (var scope = new TransactionScope())
            {
                try
                {
                    var versions = contentVersionRepository.GetById(contentVersion.Id)
                            .OrderByDescending(v => v.Version)
                            .FirstOrDefault();
                    if (versions == null)
                        contentVersion.Version = 1;
                    else
                        contentVersion.Version = versions.Version + 1;
                    contentVersion.IsCurrent = false;
                    contentVersion.LastModified = DateTime.Now;
                    contentVersionRepository.Save(contentVersion);
                    contentVersionRepository.UpdateCurrent(contentVersion);
                    scope.Complete();
                    return Json(new { status = "success" }, JsonRequestBehavior.AllowGet);
                }
                catch (Exception ex)
                {
                    return Json(new { status = "fail" }, JsonRequestBehavior.AllowGet);
                }
            }
        }
    // EF repository
    public class ContentVersionRepository {
        private readonly MyEfContext context;
        private readonly DbSet<ContentVersion> dbset;
        public MyEfContext DataContext {
            get { return context; }
        }
        public IList<ContentVersion> GetById(int id)
        {
            return context
                .Database.SqlQuery<ContentVersion>(string.Format(@"select * From ContentVersions c Where c.Id = {0}", id))
                .OrderBy(v=>v.Version)
                .ToList();
        }
        public int Save(ContentVersion version)
        {
            Entities.Add(version);
            return context.SaveChanges();
        }
        public int UpdateCurrent(ContentVersion version)
        {
            context.Database.ExecuteSqlCommand(string.Format("Execute SP_UPDATE_CURRENT {0},{1};", version.Id, version.Version));
            return 0;
        }
        public DbSet Entities {
            get { return dbset; }
        }
        public ContentVersionRepository(MyEfContext context) {
            this.context = context;
            dbset = context.ContentVersions;
        }
    }
}

SP_UPDATE_CURRENT进程如下:

CREATE PROCEDURE [dbo].[SP_UPDATE_CURRENT](@Id INT, @Version INT)
AS
BEGIN
    UPDATE ContentVersions SET IsCurrent = CASE WHEN [Version] = @Version THEN 1 ELSE 0 END WHERE Id = @Id;
    UPDATE Contents SET
        Title = cv.Title,
        HtmlContent = cv.HtmlContent,
        Summary = cv.Summary
    FROM Contents c
    INNER JOIN ContentVersions cv ON c.Id = cv.Id
    WHERE cv.Id = @Id AND cv.IsCurrent = 1
END;
GO

然而,用户报告说,每隔一段时间,当保存新版本,内容是不同步的。也就是说:Content表没有正确更新。新的ContentVersion记录在那里,但是它的IsCurrent是false(0).这种情况并不总是发生,只是偶尔发生。

我的想法是,它是双击导致某种竞争条件,但我放了一些JS到位,以防止用户双击,我还添加了TransactionScope,以防万一。然而,这个问题仍然存在。

我已经看了一遍又一遍的代码,我不知道我做错了什么

用于内容版本控制的表不同步

你没有重复的版本号,对吧?在(contentid, version)上创建一个唯一的索引来确保。

使事务可序列化,根据定义排除所有并发性问题。

使用SQL Profiler和事务跟踪来确保所有数据库调用作为Save的一部分发生在单个会话和单个事务中。有时,调用可能会"滑出",这很难从源代码中看出。

顺便说一句,你的EF和原始SQL的混合有点奇怪,而且容易出错。如果EF维护的对象模型与数据库行为不同步,则该对象模型将未定义