内容版本控制的表不同步

时间:2014-06-26 20:13:57

标签: c# sql-server asp.net-mvc entity-framework

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

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

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

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

        // 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 sproc看起来像:

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

但是,用户报告说,每隔一段时间,在保存新版本时,内容就会不同步。即:内容表未正确更新。新的ContentVersion记录存在,但其IsCurrent为false(0)。这并不总是偶尔发生。

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

我一遍又一遍地看着这段代码,我无法弄清楚我做错了什么。

1 个答案:

答案 0 :(得分:1)

你没有重复的版权号码,对吗?在(contentid,version)上创建一个唯一索引以确保。

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

将SQL事件探查器与事务跟踪一起使用,以确保作为Save的一部分的所有数据库调用都发生在单个会话和单个事务中。有时候,电话会“滑出”,这很难从源代码中辨别出来。

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