以正确的方式从 SQL 服务器获取单条记录
Get a single record from SQL Server the correct way
我正在使用 Ado 按 ID 检索单个记录。观察:
public async Task<Image> GetImage(int id)
{
var image = new Image();
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
conn.Open();
string sql = @" SELECT * FROM Images where id = @id";
using (SqlCommand comm = new SqlCommand(sql, conn))
{
comm.Parameters.AddWithValue("@id", id);
var reader = await comm.ExecuteReaderAsync();
int ordId = reader.GetOrdinal("id");
int ordName = reader.GetOrdinal("name");
int ordPath = reader.GetOrdinal("path");
while (reader.Read())
{
image.Id = reader.GetInt32(ordId);
image.Name = reader.GetString(ordName);
image.Path = reader.GetString(ordPath);
}
return image;
}
}
}
如您所见,我正在使用 While 循环访问记录。因为 while 表示可能有多个记录要迭代,所以我相信这可能是获取单个记录的错误方法。考虑到 ADO 对一行一个字段有 ExecuteScalar,也许他们对一行多个字段有指定的方法。有没有指定的方法在ADO中获取单条记录?
我会采用您当前的方法,但我会消除 while 循环。如果要确保只返回一条记录,请执行额外的 Read
以确保它 returns false。这类似于 LINQ Single
运算符的语义。
if (!reader.Read())
throw new InvalidOperationException("No records were returned.");
image.Id = reader.GetInt32(ordId);
image.Name = reader.GetString(ordName);
image.Path = reader.GetString(ordPath);
if (reader.Read())
throw new InvalidOperationException("Multiple records were returned.");
假设你数据库中的id
列是一个主键(唯一),那么在SQL查询中不需要指定TOP
子句; SQL 服务器查询优化器会推断由于 WHERE
子句最多只返回一条记录。但是,如果您在 id
列上没有主键或唯一 index/constraint,那么您应该发出一个 TOP (2)
子句来限制返回的行数。您应该避免使用 TOP (1)
,因为您将无法检测(并引发错误)额外的匹配项。
string sql = @"SELECT TOP (2) * FROM Images WHERE id = @id"
在这种情况下,您可以在查询中使用 Top(1)
从数据库中仅获取一条记录:
SELECT Top(1) * FROM Images
where id = @id
order by id desc -- will get the latest record
如果你只读一次怎么办:
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
conn.Open();
string sql = @" SELECT id, name, path FROM Images where id = @id";
using (SqlCommand comm = new SqlCommand(sql, conn))
{
comm.Parameters.AddWithValue("@id", id);
using (var reader = await comm.ExecuteReaderAsync())
{
if (!reader.Read())
throw new Exception("Something is very wrong");
int ordId = reader.GetOrdinal("id");
int ordName = reader.GetOrdinal("name");
int ordPath = reader.GetOrdinal("path");
image.Id = reader.GetInt32(ordId);
image.Name = reader.GetString(ordName);
image.Path = reader.GetString(ordPath);
return image;
}
}
}
P.S.: 我也将 select 语句更改为 select 只有必填字段并在使用中包装 reader声明。
我正在使用 Ado 按 ID 检索单个记录。观察:
public async Task<Image> GetImage(int id)
{
var image = new Image();
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
conn.Open();
string sql = @" SELECT * FROM Images where id = @id";
using (SqlCommand comm = new SqlCommand(sql, conn))
{
comm.Parameters.AddWithValue("@id", id);
var reader = await comm.ExecuteReaderAsync();
int ordId = reader.GetOrdinal("id");
int ordName = reader.GetOrdinal("name");
int ordPath = reader.GetOrdinal("path");
while (reader.Read())
{
image.Id = reader.GetInt32(ordId);
image.Name = reader.GetString(ordName);
image.Path = reader.GetString(ordPath);
}
return image;
}
}
}
如您所见,我正在使用 While 循环访问记录。因为 while 表示可能有多个记录要迭代,所以我相信这可能是获取单个记录的错误方法。考虑到 ADO 对一行一个字段有 ExecuteScalar,也许他们对一行多个字段有指定的方法。有没有指定的方法在ADO中获取单条记录?
我会采用您当前的方法,但我会消除 while 循环。如果要确保只返回一条记录,请执行额外的 Read
以确保它 returns false。这类似于 LINQ Single
运算符的语义。
if (!reader.Read())
throw new InvalidOperationException("No records were returned.");
image.Id = reader.GetInt32(ordId);
image.Name = reader.GetString(ordName);
image.Path = reader.GetString(ordPath);
if (reader.Read())
throw new InvalidOperationException("Multiple records were returned.");
假设你数据库中的id
列是一个主键(唯一),那么在SQL查询中不需要指定TOP
子句; SQL 服务器查询优化器会推断由于 WHERE
子句最多只返回一条记录。但是,如果您在 id
列上没有主键或唯一 index/constraint,那么您应该发出一个 TOP (2)
子句来限制返回的行数。您应该避免使用 TOP (1)
,因为您将无法检测(并引发错误)额外的匹配项。
string sql = @"SELECT TOP (2) * FROM Images WHERE id = @id"
在这种情况下,您可以在查询中使用 Top(1)
从数据库中仅获取一条记录:
SELECT Top(1) * FROM Images
where id = @id
order by id desc -- will get the latest record
如果你只读一次怎么办:
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
conn.Open();
string sql = @" SELECT id, name, path FROM Images where id = @id";
using (SqlCommand comm = new SqlCommand(sql, conn))
{
comm.Parameters.AddWithValue("@id", id);
using (var reader = await comm.ExecuteReaderAsync())
{
if (!reader.Read())
throw new Exception("Something is very wrong");
int ordId = reader.GetOrdinal("id");
int ordName = reader.GetOrdinal("name");
int ordPath = reader.GetOrdinal("path");
image.Id = reader.GetInt32(ordId);
image.Name = reader.GetString(ordName);
image.Path = reader.GetString(ordPath);
return image;
}
}
}
P.S.: 我也将 select 语句更改为 select 只有必填字段并在使用中包装 reader声明。