如何从存储过程中读取值?
How to read values from stored procedure?
我正在尝试从我的 .NET Core Web 上的存储过程中读取值 API。
这是得到的响应:
System.InvalidOperationException: Invalid attempt to call FieldCount when reader is closed.
at Microsoft.Data.SqlClient.SqlDataReader.get_FieldCount()
at System.Data.Common.DbEnumerator.BuildSchemaInfo()
at System.Data.Common.DbEnumerator.MoveNext()
at System.Text.Json.JsonSerializer.HandleEnumerable(JsonClassInfo elementClassInfo, JsonSerializerOptions options, Utf8JsonWriter writer, WriteStack& state)
at System.Text.Json.JsonSerializer.Write(Utf8JsonWriter writer, Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json, Object value, Type inputType, JsonSerializerOptions options, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
这是我的代码:
try
{
using (SqlConnection con = new SqlConnection(myConnString))
{
using (SqlCommand cmd = new SqlCommand("GetUsers", con)) // Simple proc which returning all 'child' users
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@parentUserId", SqlDbType.UniqueIdentifier).Value = parentUserId;
// open connection to database
con.Open();
//set the SqlCommand type to stored procedure and execute
cmd.CommandType = CommandType.StoredProcedure;
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
message.Data = reader;
}
}
return message;
}
catch (Exception ex)
{
message.IsValid = false;
}
当我调试这个时,我意识到数据存在于结果中,但它嵌套在另一个对象中,就像它在此处的图像上看起来的那样:
尝试在两个 using
语句中移动 return message;
。
try
{
using (SqlConnection con = new SqlConnection(myConnString))
{
using (SqlCommand cmd = new SqlCommand("GetUsers", con)) // Simple proc which returning all 'child' users
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@parentUserId", SqlDbType.UniqueIdentifier).Value = parentUserId;
// open connection to database
con.Open();
//set the SqlCommand type to stored procedure and execute
cmd.CommandType = CommandType.StoredProcedure;
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (reader.Read())
{
// add each line to message.Data
}
return message;
}
}
}
catch (Exception ex)
{
message.IsValid = false;
}
您一离开 using
,您的 reader
就丢失了。
你如何处理这个 // add each line to message.Data
将取决于 message.Data
的样子,我们无法判断。
此外,还有一些其他事情:
cmd.CommandType = CommandType.StoredProcedure;
-- 你有两次。没有伤害任何东西,但也没有帮助。
您真的应该研究一种更优雅的数据访问方式。 Straight ADO.NET 笨重且难以使用。探索 Entity Framework 和 Dapper 等选项。
当代码从包含连接的 using 块中退出时,连接及其关联的 reader 都将关闭。
这意味着 DataReader 通常不适合在方法之间传递。存在一些解决方法,see here for example but I have found more useful to totally abandon the SqlDataReader and SqlCommand and replacing them with a simple ORM like Dapper
现在,假设您有一个用户 class,其某些属性与数据表字段的名称完全匹配。
public class User
{
public Guid ID {get;set;}
public string Name {get;set;}
public string EMail {get;set;}
public Guid ParentID {get;set;}
... etc ...
}
此时你的代码,使用 Dapper 将是
using (SqlConnection con = new SqlConnection(myConnString))
{
con.Open();
List<User> users = con.Query<User>("GetUsers",
new {parentUserId=parentUserId},
commandType: CommandType.StoredProcedure).ToList();
message.Data = users;
return message;
}
当然消息中的字段数据 class 应该是用户类型,或者如果您使用许多不同的类型,您甚至可以将列表序列化为 Json 字符串和 return
我认为使用 Dapper 之类的东西你会更开心,但是,如果你想走直线 SqlConnection/DataReader路径,考虑像这样的简单包装器 class:
public class WrappedReader : IDisposable
{
public SqlDataReader Reader { get; }
public SqlConnection Connection { get; }
public WrappedReader(SqlConnection connection, SqlDataReader reader)
{
Reader = reader;
Connection = connection;
}
public void Dispose()
{
Reader?.Dispose();
Connection?.Dispose();
}
您构造一个 WrappedReader
和 return 而不是 return DataReader,类似于:
public static WrappedReader TestWrappedReader()
{
var connection = new SqlConnection(connectionString);
using (var command = new SqlCommand("Select * from SomeTable", connection))
{
var reader = command.ExecuteReader();
return new WrappedReader(connection, reader);
}
}
那么你可以这样称呼它:
using (var wrappedReader = TestWrappedReader())
{
var reader = wrappedReader.Reader;
if (reader.HasRows)
{
while (reader.Read())
{
DoSomethingWith(reader);
}
}
}
我正在尝试从我的 .NET Core Web 上的存储过程中读取值 API。
这是得到的响应:
System.InvalidOperationException: Invalid attempt to call FieldCount when reader is closed.
at Microsoft.Data.SqlClient.SqlDataReader.get_FieldCount()
at System.Data.Common.DbEnumerator.BuildSchemaInfo()
at System.Data.Common.DbEnumerator.MoveNext()
at System.Text.Json.JsonSerializer.HandleEnumerable(JsonClassInfo elementClassInfo, JsonSerializerOptions options, Utf8JsonWriter writer, WriteStack& state)
at System.Text.Json.JsonSerializer.Write(Utf8JsonWriter writer, Int32 originalWriterDepth, Int32 flushThreshold, JsonSerializerOptions options, WriteStack& state)
at System.Text.Json.JsonSerializer.WriteAsyncCore(Stream utf8Json, Object value, Type inputType, JsonSerializerOptions options, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter.WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|29_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
这是我的代码:
try
{
using (SqlConnection con = new SqlConnection(myConnString))
{
using (SqlCommand cmd = new SqlCommand("GetUsers", con)) // Simple proc which returning all 'child' users
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@parentUserId", SqlDbType.UniqueIdentifier).Value = parentUserId;
// open connection to database
con.Open();
//set the SqlCommand type to stored procedure and execute
cmd.CommandType = CommandType.StoredProcedure;
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
message.Data = reader;
}
}
return message;
}
catch (Exception ex)
{
message.IsValid = false;
}
当我调试这个时,我意识到数据存在于结果中,但它嵌套在另一个对象中,就像它在此处的图像上看起来的那样:
尝试在两个 using
语句中移动 return message;
。
try
{
using (SqlConnection con = new SqlConnection(myConnString))
{
using (SqlCommand cmd = new SqlCommand("GetUsers", con)) // Simple proc which returning all 'child' users
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@parentUserId", SqlDbType.UniqueIdentifier).Value = parentUserId;
// open connection to database
con.Open();
//set the SqlCommand type to stored procedure and execute
cmd.CommandType = CommandType.StoredProcedure;
SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
while (reader.Read())
{
// add each line to message.Data
}
return message;
}
}
}
catch (Exception ex)
{
message.IsValid = false;
}
您一离开 using
,您的 reader
就丢失了。
你如何处理这个 // add each line to message.Data
将取决于 message.Data
的样子,我们无法判断。
此外,还有一些其他事情:
cmd.CommandType = CommandType.StoredProcedure;
-- 你有两次。没有伤害任何东西,但也没有帮助。
您真的应该研究一种更优雅的数据访问方式。 Straight ADO.NET 笨重且难以使用。探索 Entity Framework 和 Dapper 等选项。
当代码从包含连接的 using 块中退出时,连接及其关联的 reader 都将关闭。
这意味着 DataReader 通常不适合在方法之间传递。存在一些解决方法,see here for example but I have found more useful to totally abandon the SqlDataReader and SqlCommand and replacing them with a simple ORM like Dapper
现在,假设您有一个用户 class,其某些属性与数据表字段的名称完全匹配。
public class User
{
public Guid ID {get;set;}
public string Name {get;set;}
public string EMail {get;set;}
public Guid ParentID {get;set;}
... etc ...
}
此时你的代码,使用 Dapper 将是
using (SqlConnection con = new SqlConnection(myConnString))
{
con.Open();
List<User> users = con.Query<User>("GetUsers",
new {parentUserId=parentUserId},
commandType: CommandType.StoredProcedure).ToList();
message.Data = users;
return message;
}
当然消息中的字段数据 class 应该是用户类型,或者如果您使用许多不同的类型,您甚至可以将列表序列化为 Json 字符串和 return
我认为使用 Dapper 之类的东西你会更开心,但是,如果你想走直线 SqlConnection/DataReader路径,考虑像这样的简单包装器 class:
public class WrappedReader : IDisposable
{
public SqlDataReader Reader { get; }
public SqlConnection Connection { get; }
public WrappedReader(SqlConnection connection, SqlDataReader reader)
{
Reader = reader;
Connection = connection;
}
public void Dispose()
{
Reader?.Dispose();
Connection?.Dispose();
}
您构造一个 WrappedReader
和 return 而不是 return DataReader,类似于:
public static WrappedReader TestWrappedReader()
{
var connection = new SqlConnection(connectionString);
using (var command = new SqlCommand("Select * from SomeTable", connection))
{
var reader = command.ExecuteReader();
return new WrappedReader(connection, reader);
}
}
那么你可以这样称呼它:
using (var wrappedReader = TestWrappedReader())
{
var reader = wrappedReader.Reader;
if (reader.HasRows)
{
while (reader.Read())
{
DoSomethingWith(reader);
}
}
}