用于内容版本控制的表不同步
本文关键字:同步 版本控制 用于 | 更新日期: 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维护的对象模型与数据库行为不同步,则该对象模型将未定义