2017-02-17 6 views
0

Сначала я работаю с базой данных Entity Framework, и у меня есть таблица, в которой хранятся исторические значения с использованием идентификаторов базовой линии. Мы храним ссылки parent/child в этой таблице с использованием идентификатора базовой линии. Следующие столбцы макияж этот дизайн: -Целочисленность базовой структуры Entity Framework

  • Id Int (первичный ключ - уникальный)
  • BaselineId Int (не уникальный, но делает однозначно удостоверения ревизии одного и того же пункта)
  • ParentBaselineId INT обнуляемым (имеются в виду базовая линия связанного лица, не ФК)
  • Последний бит (указано, что это самый последний базовый в серии)

Пример данных для ясности

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 будет сохранен. Когда два пользователя запускают этот метод одновременно, они сохраняют одинаковые идентификаторы базовой линии, так как сохранение не выполняется до того, как другие пользователи просмотрят следующий доступный идентификатор базовой линии.

  • Метод может добавить много элементов одновременно, которые должны быть связаны между собой исходными идентификаторами
  • Это должно быть одной транзакции 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 Greene «s, я был в состоянии использовать последовательность 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(); 
    } 
}