我想在Entity Framework中使用SQL Server sequence
objects来显示数字序列,然后再将其保存到数据库中。
在当前场景中,我通过在存储过程中增加1来执行相关操作(存储在一个表中的先前值)并将该值传递给C#代码。
要实现这一点,我需要一个表,但现在我想将它转换为sequence
对象(它会给它带来什么好处吗?)。
我知道如何在SQL Server中创建序列并获取下一个值。
但我想知道如何在Entity Framework中获取SQL Server的sequence
对象的下一个值?
我无法在Related questions中找到有用的答案。
提前致谢。
答案 0 :(得分:39)
您可以在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;
}
}
答案 1 :(得分:13)
由于我使用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;
}
答案 2 :(得分:6)
EF 3.1:在DbContext中添加以下功能。使用Microsoft.Data.SqlClient进行引用;
public async Task<int> NextValueForSequence(SequenceEnum.Sequence sequence)
{
SqlParameter result = new SqlParameter("@result", System.Data.SqlDbType.Int)
{
Direction = System.Data.ParameterDirection.Output
};
var sequenceIdentifier = sequence.GetType().GetMember(sequence.ToString()).First().GetCustomAttribute<DescriptionAttribute>().Description;
await Database.ExecuteSqlRawAsync($"SELECT @result = (NEXT VALUE FOR [{sequenceIdentifier}])", result);
return (int)result.Value;
}
public class SequenceEnum
{
public enum Sequence
{
[Description("Generate_First_Sequence")]
FirstSequence,
[Description("Generate_Second_Sequence")]
SecondSequence,
}
}
在调用上下文时
await context.NextValueForSequence(SequenceEnum.Sequence.FirstSequence);
希望这会有所帮助:)
答案 3 :(得分:2)
以防万一其他使用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;
}
答案 4 :(得分:0)
如果你想在存储过程之外做,你可以创建一个只包含字符串或int的实体类(无论你的序列返回什么),然后对它运行一些原始SQL。然后只需使用你想要的对象或字符串。
SEQ_TXN_ID txn_id= _context.SEQ_TXN_IDs.SqlQuery("SELECT txn_id_seq.NEXTVAL txn_ID FROM DUAL").FirstOrDefault();
答案 5 :(得分:0)
由于无法立即使用此功能,因此我开始为完成该任务的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;
}
}
虽然我必须承认所有带有反射和注解的东西似乎有些矫kill过正,但我还是很喜欢。
它允许我以一种非常优雅的方式检索值
ctx.NextValueForSequence(Sequence.OrderNr);
它还模拟了一种“类型证明”方式,它使我无法在集中位置明确定义不同的序列名称,而不仅仅是从我想要的任何地方传递魔术字符串。
如果您不希望这样做,只需更改方法以将序列名称作为字符串传递即可。一样好。
答案 6 :(得分:0)
marc_s提供的更新后的答案在“ rawQuery.SingleAsync();”之后缺少task.wait()。没有wait()可能会导致竞争状态,因此尝试在查询实际上在异步(多线程)进程中运行之前获取结果。
答案 7 :(得分:0)
//This code worked for me after slight mods of other answers here. Try this:
public static Int64 GetNextFormSequenceValue(){
Int64 result = 0;
var db = new dbContext.db(); //change the name to whatever your LINQ names are
Int64 nextId = db.ExecuteQuery<Int64>("SELECT NEXT VALUE FOR YOURSEQNAME").FirstOrDefault();
if(nextId > 0){
result = nextId;
}
return result;
}