如何使用SQL Server和实体框架模拟序列(来自Oracle)的使用

本文关键字:Oracle 来自 Server SQL 何使用 实体 模拟 框架 | 更新日期: 2023-09-27 18:25:02

我们一直喜欢在Oracle数据库中使用序列,以便在整个数据库中创建全局唯一的主键ID。如此之多,以至于我们在使用SQL Server数据库时会模仿同样的东西:

CREATE TABLE MainSequence(
    Id int IDENTITY(1,1) CONSTRAINT pkMainSequence PRIMARY KEY
)

我正在尝试切换到实体框架,这对我来说是非常新的。创建一个扩展方法来快速获得下一个可用的全局唯一Id似乎是微不足道的。

public static int GetNextId( this Entities db ) {
    var ms = new MainSequence();
    db.MainSequences.AddObject( ms );
    db.SaveChanges( SaveOptions.None );
    return ms.Id;
}

由于它是一个标识列,所以我所要做的就是向数据库中添加一个新对象并保存更改,以便用实际值更新Id属性。这很好用。但当我试图将它用于外键相关表时,我似乎遇到了麻烦:

var dataId = db.GetNextId();
db.Datas.AddObject( Data.CreateData( dataId, someValueForThisColumn );
db.Caches.AddObject( 
                    Cache.CreateCache( db.GetNextId(),
                    DatabaseMethods.GetOrAddLocation( source.GetLocationText() ),
                    DateTime.Now,
                    dataId ) );

缓存表具有数据表的外键。当在此之后立即调用SaveChanges();时,将生成异常。

System.Data.SqlClient.SqlException:违反PRIMARY KEY约束"pkData"。无法在对象"dbo.Data"中插入重复的键。重复的键值为(78162)。语句已终止。

由于某种原因,新的数据行似乎试图被插入数据库两次,尽管我不确定为什么会这样。我已经确认,每次运行代码时,都会返回不同的MainSequence ID。似乎在获得新ID时调用db.SaveChanges是个问题,但没有其他方法可以用真正的int填充,我不明白为什么会有问题。我做错了什么?

如何使用SQL Server和实体框架模拟序列(来自Oracle)的使用

我的问题是线路db.SaveChanges( SaveOptions.None );。我使用此选项的原因是为了避免SaveOptions.AcceptAllChangesAfterSave,在我看来,通过从其他StackOverflow问题(无效或误解)收集的信息,我认为这将为我提供数据库事务的基本形式。我确实读了几遍MSDN文档:

保存更改后,将调用AcceptAllChangesAfterSave()方法,该方法将重置ObjectStateManager中的更改跟踪。

对我来说,这听起来很像交易变更跟踪。我认为,如果在保存调用中不接受更改,那么在出现错误的情况下,数据库"事务"将被回滚(后来我发现情况并非如此)。相反,所有这些都会使ObjectContext仍然认为更改未保存。因此,在下一次保存调用时,"未保存"的更改将被重新应用。Ta da,主键异常。这也解释了为什么我的主要序列是跳过值。