2017-02-17 68 views
0

我正在使用实体框架数据库,并且我有一个使用基线ID存储历史值的表。我们使用基线ID在此表上存储父/子链接。下面列妆容这样的设计: -实体框架基线完整性

  • ID INT(主键 - 唯一)
  • BaselineId INT(不是唯一的,但做同样的项目的唯一身份修订)
  • ParentBaselineId INT可以为空(指链接的实体的基线,没有FK)
  • 最新位(表示,这是最近在一系列基线)

为了清楚

实施例的数据
Id BaselineId ParentBaselineId Latest 
1 1   NULL    0 
2 1   NULL    1 
3 2   1    0 
4 2   1    1 

这显示了两个项目,每个项目有两个修订版本。基线1是基线2的父代。

我的问题是由于下面列出的原因,我查找C#中下一个可用基线,并手动指定要保存的BaselineId/ParentBaselineId。当两个用户同时触发此方法时,他们保存相同的基线ID,因为在第二个用户查找下一个可用基线ID之前,保存没有完成。

  • 的方法可以一次必须由基线IDS连接在一起增加了许多项目
  • 这必须是一个SQL事务,因此它可以对错误完全回滚
  • SQL触发器不能用来设定基线,因为它们需要提前指示关系

我可以采取哪些措施来确保两个用户同时运行该方法不会使用相同的基准?

我的C#看起来像这样

using (var tx = new TransactionScope()) 
{ 
    using (var context = new DbContext(connectionString)) 
    { 
     int baseline = context.MyTable.Max(e => e.BaselineId); 
     context.MyTable.Add(new MyTable() {BaselineId = baseline + 1, Latest = true}); 
     context.MyTable.Add(new MyTable() { BaselineId = baseline + 2, ParentBaselineId = baseline + 1, Latest = true }); 
     context.SaveChanges(); 
    } 

    tx.Complete(); 
} 
+0

看一看[序列](http://stackoverflow.com/questions/27077461/how-to-get-next-value-of-sql-server-sequence-in-entity-framework) 。 –

+0

这很完美,非常感谢。如果你添加这个作为答案,我可以接受它。 – Joe

+0

如果有效,只需发布​​您的解决方案作为答案。 –

回答

0

使用@Steve格林的建议下,我能够使用SQL序列。在我的数据库中创建一个新序列并设置起始值以匹配我现有的数据后,我将代码更新为以下内容。

public long NextBaseline(DbContext context) 
{ 
    DataTable dt = new DataTable(); 
    var conn = context.Database.Connection; 
    var connectionState = conn.State; 
    try 
    { 
     if (connectionState != ConnectionState.Open) 
      conn.Open(); 
     using (var cmd = conn.CreateCommand()) 
     { 
      cmd.CommandText = "SELECT NEXT VALUE FOR MySequence;"; 
      using (var reader = cmd.ExecuteReader()) 
      { 
       dt.Load(reader); 
      } 
     } 
    } 
    catch (Exception ex) 
    { 
     throw new HCSSException(ex.Message, ex); 
    } 
    finally 
    { 
     if (connectionState != ConnectionState.Open) 
      conn.Close(); 
    } 
    return Convert.ToInt64(dt.AsEnumerable().First().ItemArray[0]); 
} 

public void Save() 
{ 
    using (var tx = new TransactionScope()) 
    { 
     using (var context = new DbContext(connectionString)) 
     { 
      var parent = new MyTable() { BaselineId = NextBaseline(context), Latest = true }; 
      var child = new MyTable() { BaselineId = NextBaseline(context), ParentBaselineId = parent.BaselineId, Latest = true } 
      context.MyTable.Add(parent); 
      context.MyTable.Add(child); 
      context.SaveChanges(); 
     } 

     tx.Complete(); 
    } 
}