Entity FrameworkでSQL Server sequence
オブジェクト を使用して、データベースに保存する前に番号の順序を表示したい。
現在のシナリオでは、ストアドプロシージャ(1つのテーブルに保存されている以前の値)を1つずつインクリメントして関連する何かを実行し、その値をC#コードに渡します。
これを実現するには1つのテーブルが必要でしたが、今ではsequence
オブジェクトに変換したいと思います(何か利点がありますか?)。
SQL Serverでシーケンスを作成して次の値を取得する方法を知っています。
しかし、Entity FrameworkでSQL Serverのsequence
オブジェクトの次の値を取得する方法を知りたいですか?
私は 関連する質問 で役に立つ答えを見つけることができません。
前もって感謝します。
次のような次のシーケンス値を選択する単純なストアドプロシージャをSQL Serverで作成できます。
CREATE PROCEDURE dbo.GetNextSequenceValue
AS
BEGIN
SELECT NEXT VALUE FOR dbo.TestSequence;
END
次に、そのストアドプロシージャをEntity FrameworkのEDMXモデルにインポートし、そのストアドプロシージャを呼び出して、次のようにシーケンス値を取得できます。
// 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);
}
更新:実際には、ストアドプロシージャをスキップして、EFコンテキストからこの生のSQLクエリを実行することができます。
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;
}
}
私は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;
}
この機能はすぐに使用できるため、仕事をする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);
また、「タイププルーフ」の方法を模倣して、必要な場所から魔法の文字列を渡すだけでなく、集中化された場所で異なるシーケンス名を明示的に定義するように制約します。
そのようにしたくない場合は、シーケンス名を文字列として渡すためにメソッドを変更するだけです。同じように機能します。
ストアドプロシージャの外部で実行したい場合は、文字列またはint(シーケンスが返すもの)のみを保持するエンティティクラスを作成し、それに対して生のSQLを実行できます。次に、オブジェクトまたは文字列を自由に使用します。
SEQ_TXN_ID txn_id= _context.SEQ_TXN_IDs.SqlQuery("SELECT txn_id_seq.NEXTVAL txn_ID FROM DUAL").FirstOrDefault();