使用存储过程但没有 return table 数据在 EF Core 中获取输出参数值

Get output parameter value in EF Core with stored procedure but without return table data

我有一个存储过程,应该 return 通过 id:

登录
CREATE PROCEDURE MyProcedure
(
    @id INT,
    @login NVARCHAR(50) OUTPUT
)
AS
BEGIN
    SELECT
            ID
        ,
            Login
FROM
    Users
WHERE
    ID = @id
END
GO

端点中的这段代码工作正常:

            var id = new SqlParameter
            {
                ParameterName = "id",
                SqlDbType = SqlDbType.Int,
                Value = 15,
            };
            var login = new SqlParameter
            {
                ParameterName = "login",
                SqlDbType = SqlDbType.NVarChar,
                Direction = ParameterDirection.Output,
                Size = 50,
            };
            var query = $"EXEC MyProcedure @{id}, @{login} OUTPUT";
            var result = _context.Set<User>()
                .FromSqlRaw(query, id, login).ToList();
            foreach (var item in result) Console.WriteLine(item.Login);

和class:

    public class User
    {
        public string Login { get; set; }
    }

和属性:

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<User>().HasNoKey();
        }

我得到一个包含一个搜索元素的列表,它的 Login 属性 就是我想要的。

但是如果我把SELECT改成(原来的程序是这样的,我改不了):

    SELECT
        @id = 
            ID
        ,
        @login = 
            Login

我收到错误消息The required column 'Login' was not present in the results of a 'FromSql' operation.

那么获得 Login 的正确方法是什么?

解决此问题的一个简单方法是使用 context.Database.ExecuteSqlInterpolated()context.Database.ExecuteSqlRaw() 而不是 context.Set<T>().FromSqlRaw():

这是一个完整的示例控制台程序,演示了该方法:

using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using Microsoft.Data.SqlClient;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;

namespace IssueConsoleTemplate
{
    public class User
    {
        public int UserId { get; set; }
        public string Login { get; set; }
    }
    
    public class Context : DbContext
    {
        public DbSet<User> Users { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder
                .UseSqlServer(
                    @"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So63991939")
                .UseLoggerFactory(LoggerFactory.Create(b => b
                    .AddConsole()
                    .AddFilter(level => level >= LogLevel.Information)))
                .EnableSensitiveDataLogging()
                .EnableDetailedErrors();
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<User>()
                .HasData(
                    new User {UserId = 1, Login = "John"},
                    new User {UserId = 2, Login = "Jane"});
        }
    }

    internal static class Program
    {
        private static void Main()
        {
            using var context = new Context();

            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();

            var connection = context.Database.GetDbConnection();
            connection.Open();
            using (var command = connection.CreateCommand())
            {
                command.CommandText = @"CREATE PROCEDURE uspGetUsers (
    @userCount INT OUTPUT
) AS
BEGIN
    SELECT 
        UserId,
        Login
    FROM
        Users;

    SELECT @userCount = @@ROWCOUNT;
END;";
                command.ExecuteNonQuery();
            }

            var userCountParameter = new SqlParameter("@myUserCountParam", SqlDbType.Int) {Direction = ParameterDirection.Output};
            context.Database.ExecuteSqlInterpolated($"EXEC uspGetUsers @userCount={userCountParameter} OUTPUT");
            
            Debug.Assert((int)userCountParameter.Value == 2);
        }
    }
}

您可以 运行 使用此 .NET Fiddle 的示例代码。