使用 EF Core 3.1 为 SQL Server 序列获取“NEXT VALUE FOR” - 不可能?

mar*_*c_s 4 sql-server sequence entity-framework-core ef-core-3.1

我正在编写一个新的 ASP.NET Core Web API,我的要求之一是能够利用 EF Core 3.1 获取SQL Server 中定义的序列的下一个值作为我需要存储的记录的 ID .

我正在努力寻找一种方法来做到这一点 - 在 EF 6.x 中,我直接在DbContext后代上使用了一种方法,如下所示:

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

    return nextVal;
}
Run Code Online (Sandbox Code Playgroud)

对于高达 2.1 的 EF Core,我本来可以Database.ExecuteSqlCommand()用来运行 SQL 代码段并返回结果。但似乎在 EF Core 3.x 中,我不走运....

我知道有.FromSqlRaw().FromSqlInterpolated方法DbSet- 但因为我只需要返回序列的下一个值 (an INT),这不会飞。而且我也知道这些方法也存在于context.Database看起来非常接近我在 EF 6.x 中的级别 - 但在这里,这些方法只会返回受影响的行数 - 我还没有找到方法从 中发回新值SEQUENCE

难道真的是在 EF Core 3.x 中,我必须真正求助于古老的 ADO.NET 代码来获取该值??有没有真的没有办法来执行任意SQL代码片段,并取回从上下文一些成果?

Dav*_*oft 7

如果你想运行一个任意的 TSQL 批处理并返回一个标量值,你可以这样做:

var p = new SqlParameter("@result", System.Data.SqlDbType.Int);
p.Direction = System.Data.ParameterDirection.Output;
context.Database.ExecuteSqlRaw("set @result = next value for some_seq", p);
var nextVal = (int)p.Value;
Run Code Online (Sandbox Code Playgroud)


小智 7

在流畅的 api 配置中,您可以创建迁移,将 ID 自动设置为序列中的下一个值

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.HasSequence<int>("OrderNumbers");

    modelBuilder.Entity<Order>()
        .Property(o => o.OrderNo)
        .HasDefaultValueSql("NEXT VALUE FOR shared.OrderNumbers");
}
Run Code Online (Sandbox Code Playgroud)

用于创建序列:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.HasSequence<int>("OrderNumbers", schema: "shared")
        .StartsAt(1000)
        .IncrementsBy(5);
}
Run Code Online (Sandbox Code Playgroud)

从这里阅读更多信息:https://www.talkingdotnet.com/use-sql-server-sequence-in-entity-framework-core-primary-key/


Iva*_*oev 6

看起来执行原始 SQL 不是 EF Core 的优先级,所以到目前为止(EF Core 3.1)它只公开提供了一些基本的有限方法。FromSql需要实体类型或无键实体类型,和ExecuteSqlRaw/ExecuteSqlInterpolatedExecuteNonQuery返回受影响行的ADO.NET 的“现代”桥梁。

好消息是 EF Core 构建在公共服务架构之上,因此可用于添加一些缺失的功能。例如,服务可用于构建所谓的IRelationalCommand,它具有所有DbCommand执行方法,尤其是ExecuteScalar所讨论的 SQL 所需的方法。

由于 EF Core 模型支持序列,因此还有一个用于构建IRelationalCommand检索下一个值所需的服务(由 HiLo 值生成器在内部使用)。

话虽如此,以下是使用上述概念的自定义方法的示例实现:

using System;
using System.Globalization;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Update;

namespace Microsoft.EntityFrameworkCore
{
    public static partial class CustomExtensions
    {
        public static long GetNextSequenceValue(this DbContext context, string name, string schema = null)
        {
            var sqlGenerator = context.GetService<IUpdateSqlGenerator>();
            var sql = sqlGenerator.GenerateNextSequenceValueOperation(name, schema ?? context.Model.GetDefaultSchema());
            var rawCommandBuilder = context.GetService<IRawSqlCommandBuilder>();
            var command = rawCommandBuilder.Build(sql);
            var connection = context.GetService<IRelationalConnection>();
            var logger = context.GetService<IDiagnosticsLogger<DbLoggerCategory.Database.Command>>();
            var parameters = new RelationalCommandParameterObject(connection, null, null, context, logger);
            var result = command.ExecuteScalar(parameters);
            return Convert.ToInt64(result, CultureInfo.InvariantCulture);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 是的,我知道您可以使用输出“DbParameter”。但我的解决方案与数据库无关,并且从“var command = …”开始并以“result = …”结尾的代码可以用作创建“ExecuteScalar”等效帮助程序的基础,用于执行任意单值返回 SQL,这就是为什么我发布这个而不是明显的(和SqlServer)绑定解决方案。干杯。 (2认同)