Как получить следующее значение последовательности SQL Server в Entity Framework?

Я хочу использовать объекты sequence SQL Server в Entity Framework, чтобы показать числовую последовательность перед сохранением в базе данных.

В текущем сценарии я делаю что-то, связанное с увеличением на единицу в хранимой процедуре (предыдущее значение хранится в одной таблице) и передаю это значение в код С#.

Для этого мне понадобилась одна таблица, но теперь я хочу преобразовать ее в объект sequence (это даст какое-то преимущество?).

Я знаю, как создать последовательность и получить следующее значение в SQL Server.

Но я хочу знать, как получить следующее значение объекта sequence SQL Server в Entity Framework?

Я не могу найти полезных ответов в связанных вопросах в SO.

Заранее спасибо.

Ответы

Ответ 1

Вы можете создать простую хранимую процедуру в SQL Server, которая выбирает следующее значение последовательности следующим образом:

CREATE PROCEDURE dbo.GetNextSequenceValue 
AS 
BEGIN
    SELECT NEXT VALUE FOR dbo.TestSequence;
END

а затем вы можете импортировать эту хранимую процедуру в свою модель EDMX в Entity Framework и вызвать эту хранимую процедуру и получить значение последовательности следующим образом:

// get your EF context
using (YourEfContext ctx = new YourEfContext())
{
    // call the stored procedure function import   
    var results = ctx.GetNextSequenceValue();

    // from the results, get the first/single value
    int? nextSequenceValue = results.Single();

    // display the value, or use it whichever way you need it
    Console.WriteLine("Next sequence value is: {0}", nextSequenceValue.Value);
}

Обновление:, вы можете пропустить хранимую процедуру и просто запустить этот необработанный SQL-запрос из контекста EF:

public partial class YourEfContext : DbContext 
{
    .... (other EF stuff) ......

    // get your EF context
    public int GetNextSequenceValue()
    {
        var rawQuery = Database.SqlQuery<int>("SELECT NEXT VALUE FOR dbo.TestSequence;");
        var task = rawQuery.SingleAsync();
        int nextVal = task.Result;

        return nextVal;
    }
}

Ответ 2

Поскольку я использую Code First и не хочу иметь дополнительный DDL, это мой путь: (EF Core 2.1, SQL Server)

Определите последовательность:

protected override void OnModelCreating( ModelBuilder modelBuilder )
{
    modelBuilder.HasSequence("MySequence");
}

И чтобы получить его, я добавляю в контекст следующую функцию:

public int GetMySequence()
{
   SqlParameter result = new SqlParameter("@result", System.Data.SqlDbType.Int)
   {
      Direction = System.Data.ParameterDirection.Output
   };

   Database.ExecuteSqlCommand(
              "SELECT @result = (NEXT VALUE FOR MySequence)", result);

   return (int)result.Value;
}

Ответ 3

Так как эта функциональность не выходит из коробки, я решил написать класс расширения для DbContext, который выполняет эту работу. Посмотрите на этот кусок кода:

public enum Sequence
{
    [Description("sequence__name__goes__here")]
    ClientNr,
    [Description("another__sequence__name")]
    OrderNr,
}
public static class MyDbContextExtensions
{
    public static int NextValueForSequence(this MyDbContext pCtx, Sequence pSequence)
    {
        SqlParameter result = new SqlParameter("@result", System.Data.SqlDbType.Int)
        {
            Direction = System.Data.ParameterDirection.Output
        };
        var sequenceIdentifier = pSequence.GetType()
                    .GetMember(pSequence.ToString())
                    .First()
                    .GetCustomAttribute<DescriptionAttribute>()
                    .Description;
        pCtx.Database.ExecuteSqlCommand($"SELECT @result = (NEXT VALUE FOR [{sequenceIdentifier}]);", result);
        return (int)result.Value;
    }
}

Хотя я должен признать, что все эти вещи с размышлениями и комментариями для некоторых могут показаться излишним, мне все равно это нравится.

Это позволяет мне получить значение довольно элегантным способом

ctx.NextValueForSequence(Sequence.OrderNr);

Он также издевается над способом "доказательства типа", заставляя меня явно определять различные имена последовательностей в централизованном месте, а не просто передавать магические строки из любого места, где я хочу.

Если вы не хотите этого, просто измените метод, чтобы передать имя последовательности в виде строки. Это будет работать так же хорошо.

Ответ 4

Если вы хотите сделать это за пределами хранимой процедуры, вы можете создать класс сущности, который содержит только строку или int (независимо от вашей последовательности), а затем запустить некоторый необработанный SQL против него. Затем просто используйте свой объект или строку, но вы бы хотели.

 SEQ_TXN_ID txn_id= _context.SEQ_TXN_IDs.SqlQuery("SELECT txn_id_seq.NEXTVAL txn_ID FROM DUAL").FirstOrDefault();

Ответ 5

На всякий случай, если кто-то, кто работает с Entity Framework Core, в конечном итоге ищет здесь, это сработало для меня:

var connection = dbContext.Database.GetDbConnection();
connection.Open();
using (var cmd = connection.CreateCommand())
{
    cmd.CommandText = "SELECT NEXT VALUE FOR ACH.FileIDModifier;";
    var obj = cmd.ExecuteScalar();
    int anInt = (int)obj;
}