如何获取使用 EF Core 从存储过程中插入的记录的 id 值
Posted
技术标签:
【中文标题】如何获取使用 EF Core 从存储过程中插入的记录的 id 值【英文标题】:How do you get the id value of record inserted from a stored procedure using EF Core 【发布时间】:2021-10-06 08:06:48 【问题描述】:我正在使用 EF Core 5,但无法取回我插入表中的记录的 userId。我尝试了不同的方法来做到这一点,但我没有运气。我得到的只是一个-1,我知道它是针对受影响的行。我似乎无法检索声明为存储过程的 OUTPUT
参数的 UserID。
存储过程:
ALTER PROCEDURE [dbo].[User.Save]
(@Return_Code INT OUTPUT,
@Error_Description_Code NVARCHAR(50) OUTPUT,
@idCallerSite INT = 0, --default if not specified
@idCaller INT = 0,
@idUser INT OUTPUT,
@firstName NVARCHAR(50),
@middleName NVARCHAR(50),
@lastName NVARCHAR(50),
@preferredName NVARCHAR(50),
@username NVARCHAR(512),
@password NVARCHAR(512),
@isActive BIT,
@isSalesperson BIT,
@userPreferences NVARCHAR(512),
@phoneNumber NVARCHAR(25),
@phoneFax NVARCHAR(25),
@email NVARCHAR(255),
@photo NVARCHAR(255),
@lastEditedBy INT)
AS
BEGIN
SET NOCOUNT ON
DECLARE @idPermission INT -- define the permission required to perform this function
SET @idPermission = 141
/*
validate uniqueness
*/
IF (SELECT COUNT(1)
FROM Users
WHERE SiteID = @idCallerSite
AND (username = @username)
AND (@idUser IS NULL OR @idUser <> UserID)) > 0
BEGIN
SET @Return_Code = 2
SET @Error_Description_Code = 'UserSave_UserNotUnique'
RETURN 1
END
/*
insert/update the user
*/
IF (@idUser = 0 OR @idUser IS NULL)
BEGIN
-- insert the new user
INSERT INTO Users (SiteID, firstName, middleName, lastName,
preferredName, username,
HashedPassword, isActive, IsSalesperson,
UserPreferences, PhoneNumber, FaxNumber,
EmailAddress, Photo, LastEditedBy)
VALUES (@idCallerSite, @firstName, @middleName, @lastName,
@preferredName, @username,
dbo.GetHashedString('SHA1', @password), @isActive, @isSalesperson,
@userPreferences, @phoneNumber, @phoneFax,
@email, @photo, @lastEditedBy)
/*
get the new user's id
*/
SET @idUser = SCOPE_IDENTITY()
SELECT @idUser
-- return successfully
SET @Return_Code = 0 --(0 is 'success')
SET @Error_Description_Code = ''
END
控制器调用
var retCode = new SqlParameter ParameterName = "@Return_Code", Value = null, SqlDbType = (SqlDbType.Int), Direction = System.Data.ParameterDirection.Output ;
var errorDesc = new SqlParameter ParameterName = "@Error_Description_Code", Value = null, SqlDbType = SqlDbType.NVarChar, Size = 50, Direction = System.Data.ParameterDirection.Output ;
var idCallerSite = new SqlParameter ParameterName = "@idCallerSite", Value = 1, SqlDbType = (SqlDbType.Int), Direction = System.Data.ParameterDirection.Input ;
var idCaller = new SqlParameter ParameterName = "@idCaller", Value = 0, SqlDbType = (SqlDbType.Int), Direction = System.Data.ParameterDirection.Input ;
var idUser = new SqlParameter ParameterName = "@idUser", Value = user.UserID, SqlDbType = (SqlDbType.Int), Direction = System.Data.ParameterDirection.Output ;
var fName = new SqlParameter ParameterName = "@firstName", Value = user.FirstName.ToString(), SqlDbType = SqlDbType.NVarChar, Size = 50, Direction = System.Data.ParameterDirection.Input ;
var mName = new SqlParameter ParameterName = "@middleName", Value = user.MiddleName.ToString(), SqlDbType = SqlDbType.NVarChar, Size = 50, Direction = System.Data.ParameterDirection.Input ;
var lName = new SqlParameter ParameterName = "@lastName", Value = user.LastName.ToString(), SqlDbType = SqlDbType.NVarChar, Size = 50, Direction = System.Data.ParameterDirection.Input ;
var pName = new SqlParameter ParameterName = "@preferredName", Value = user.PreferredName.ToString(), SqlDbType = SqlDbType.NVarChar, Size = 50, Direction = System.Data.ParameterDirection.Input ;
var userName = new SqlParameter ParameterName = "@username", Value = user.UserName.ToString(), SqlDbType = SqlDbType.NVarChar, Size = 512, Direction = System.Data.ParameterDirection.Input ;
var password = new SqlParameter ParameterName = "@password", Value = user.HashedPassword.ToString(), SqlDbType = SqlDbType.NVarChar, Size = 512, Direction = System.Data.ParameterDirection.Input ;
var isActive = new SqlParameter ParameterName = "@isActive", Value = user.IsActive, SqlDbType = (SqlDbType.Bit), Direction = System.Data.ParameterDirection.Input ;
var isSalesperson = new SqlParameter ParameterName = "@isSalesperson", Value = user.IsSalesperson, SqlDbType = (SqlDbType.Bit), Direction = System.Data.ParameterDirection.Input ;
var userPref = new SqlParameter ParameterName = "@userPreferences", Value = user.UserPreferences.ToString(), SqlDbType = SqlDbType.NVarChar, Size = 512, Direction = System.Data.ParameterDirection.Input ;
var phone = new SqlParameter ParameterName = "@phoneNumber", Value = user.PhoneNumber.ToString(), SqlDbType = SqlDbType.NVarChar, Size = 25, Direction = System.Data.ParameterDirection.Input ;
var fax = new SqlParameter ParameterName = "@phoneFax", Value = user.FaxNumber.ToString(), SqlDbType = SqlDbType.NVarChar, Size = 25, Direction = System.Data.ParameterDirection.Input ;
var email = new SqlParameter ParameterName = "@email", Value = user.EmailAddress.ToString(), SqlDbType = SqlDbType.NVarChar, Size = 255, Direction = System.Data.ParameterDirection.Input ;
var photo = new SqlParameter ParameterName = "@photo", Value = user.Photo.ToString(), SqlDbType = SqlDbType.NVarChar, Size = 255, Direction = System.Data.ParameterDirection.Input ;
var lastEditedBy = new SqlParameter ParameterName = "@lastEditedBy", Value = user.LastEditedBy, SqlDbType = (SqlDbType.Int), Direction = System.Data.ParameterDirection.Input ;
var _user = await _db.Database.ExecuteSqlRawAsync("EXEC [dbo].[User.Save] @Return_Code, @Error_Description_Code, @idCallerSite, @idCaller, @idUser OUT, @firstName, @middleName, @lastName, @preferredName, @username, @password, @isActive, @isSalesperson, @userPreferences, @phoneNumber, @phoneFax, @email, @photo, @lastEditedBy", retCode, errorDesc, idCallerSite, idCaller, idUser, fName, mName, lName, pName, userName, password, isActive, isSalesperson, userPref, phone, fax, email, photo, lastEditedBy);
return Ok(_user);
我使用.FromSql
得到以下错误。
var _user = _db.Users.FromSqlRaw("EXEC [dbo].[User.Save] @Return_Code, @Error_Description_Code, @idCallerSite, @idCaller, @idUser OUT, @firstName, @middleName, @lastName, @preferredName, @username, @password, @isActive, @isSalesperson, @userPreferences, @phoneNumber, @phoneFax, @email, @photo, @lastEditedBy", retCode, errorDesc, idCallerSite, idCaller, idUser, fName, mName, lName, pName, userName, password, isActive, isSalesperson, userPref, phone, fax, email, photo, lastEditedBy);
System.InvalidOperationException:“FromSql”操作的结果中不存在所需的列“UserID”。
在 Microsoft.EntityFrameworkCore.Query.Internal.FromSqlQueryingEnumerable
1.BuildIndexMap(IReadOnlyList
1 columnNames, DbDataReader dataReader) 在 Microsoft.EntityFrameworkCore.Query.Internal.FromSqlQueryingEnumerable1.AsyncEnumerator.InitializeReaderAsync(DbContext _, Boolean result, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func
4 操作,Func4 verifySucceeded, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.FromSqlQueryingEnumerable
1.AsyncEnumerator.MoveNextAsync() 在 Microsoft.AspNetCore.Mvc.Infrastructure.AsyncEnumerableReader.ReadInternal[T](对象值) 在 Microsoft.AspNetCore.Mvc.Infrastructure.AsyncEnumerableReader.ReadInternal[T](对象值) 在 Microsoft.AspNetCore.Mvc.Infrastructure.ObjectResultExecutor.ExecuteAsyncEnumerable(ActionContext 上下文,ObjectResult 结果,对象 asyncEnumerable,Func`2 阅读器) 在 Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker 调用程序,Task lastTask,State next,Scope 范围,Object state,Boolean isCompleted) 在 Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed 上下文) 在 Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted) 在 Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|27_0(ResourceInvoker 调用程序,任务 lastTask,下一个状态,作用域范围,对象状态,布尔 isCompleted) 在 Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|19_0(ResourceInvoker 调用程序,任务 lastTask,下一个状态,作用域范围,对象状态,布尔 isCompleted) 在 Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker 调用程序,任务任务,IDisposable 范围) 在 Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(端点端点、任务 requestTask、ILogger 记录器) 在 Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext 上下文) 在 Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) 在 Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) 在 Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext 上下文)
标题:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Content-Length: 301
Content-Type: application/json
Cookie: fblo_329420117573382=y
Host: localhost:62374
Referer: http://localhost:62374/swagger/index.html
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
sec-ch-ua: " Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"
sec-ch-ua-mobile: ?0
Origin: http://localhost:62374
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
【问题讨论】:
改用FromSql()
会有什么结果?
我收到以下错误 System.InvalidOperationException:“FromSql”操作的结果中不存在所需的列“UserID”。
请edit 您的问题包括您尝试使用FromSql()
以及您收到的完整错误消息。看起来您想将结果保存在具有属性/列UserID
的对象中,但是您有一个SELECT @idUser
语句,它将只返回一个整数值(并且您无论如何都想使用输出参数)。跨度>
【参考方案1】:
实体框架要求至少存在主键,所以你需要这样做
SET @idUser = SCOPE_IDENTITY();
SELECT UserID = @idUser;
但是,这只会为您提供一个字段,而不会为您提供任何其他列。要获取其他列,您只需 OUTPUT
一切(无需连接)
INSERT INTO Users (SiteID, firstName,
.........
OUTPUT inserted.ID, inserted.SiteID, inserted.firstName
.........
VALUES (@idCallerSite, @firstName
.........
请注意,用于调用存储过程的语法并不理想,因为它依赖于参数位置而不是名称。改为使用
EXEC [dbo].[User.Save] @Return_Code = @Return_Code OUTPUT, @Error_Description_Code = @Error_Description_Code, @idCallerSite = @idCallerSite ...
,您还需要在输出参数上指定OUTPUT
,如图所示。
【讨论】:
【参考方案2】:只能使用输出参数idUser来获取记录id
_db.Database.ExecuteSqlRaw(....);
var id=0;
if (idUser.Value != DBNull.Value)
id = (int) idUser.Value;
return Ok(id);
并修复存储过程。删除 SELECT @idUser,只留下
SET @idUser = SCOPE_IDENTITY()
如果 sp 包含 SELECT Database.ExecuteSqlRaw 总是返回 -1;
【讨论】:
【参考方案3】:我通过对所有字段执行 select 语句解决了这个问题,然后从结果集中提取了 UserID。感谢@Progman 的帮助。是你的建议帮助了我。
【讨论】:
以上是关于如何获取使用 EF Core 从存储过程中插入的记录的 id 值的主要内容,如果未能解决你的问题,请参考以下文章
如何通过 FromSqlRaw 在 EF Core 3.0 中调用存储过程
无法使用 EF Core 或 LinqToDb EF Core Tools 或 ADO.NET 运行任何存储过程创建脚本