繁体   English   中英

编辑从 SQL 查询返回的值

[英]Edit the values returned from an SQL Query

对于我正在处理的项目,我有以下 OwnershipRole 记录:

public record OwnershipRole
{

    public OwnershipRole()
    {

    }

    public OwnershipRole(Guid id, string title, bool isWithdrawn)
    {
        Id = id;
        Title = title;
        IsWithdrawn = isWithdrawn;
    }

    public Guid Id { get; set; }
    public string Title { get; set; }
    public bool IsWithdrawn { get; set; }
}
}

为了获取这些详细信息,我使用了这样的 sql 查询:

    public List<OwnershipRole> GetOwnershipRoles()
    {

        var sql = @"
SELECT ItemID, Title, Status
FROM ItemDetail";


        var data = GetTable(sql);

        return data.Select().Select(dr => new OwnershipRole
        {
            Id = dr.Field<Guid>(0),
            Title = dr.Field<string>(1),
            IsWithdrawn = !dr.Field<bool>(2)
        }).ToList();
    }

    private DataTable GetTable(string sql)
    {
        var rv = new DataTable();
        using var cnn = new SqlConnection(_connectionString);
        using var cmd = new SqlCommand(sql, cnn);
        cnn.Open();

        var da = new SqlDataAdapter(cmd);
        da.Fill(rv);
    
        cnn.Close();
        return rv;
    }

我们正在抓取结果并将它们放入数据表中。 然而,我遇到的问题是 Status 列中的值实际上是字符串。 所以我想知道是否可能有这样的条件:

if (Status == "Withdrawn") {
Status == false

}

我知道这行不通,但是有没有一种方法可以操纵我从 Status 列中获得的值,以便适应 OwnershipRole 类中的 bool 参数?

当然,要么:

        var sql = @"
SELECT ItemID, Title, CASE WHEN Status='Withdrawn' then 1 else 0 end IsWithdrawn
FROM ItemDetail";

或者

    return data.Select().Select(dr => new OwnershipRole
    {
        Id = dr.Field<Guid>(0),
        Title = dr.Field<string>(1),
        IsWithdrawn = dr.Field<string>(2)=="Withdrawn"?true:false
    }).ToList();

是的,您可以很好地从 RDBS 读取string ,然后从中获取bool Status (摆脱GetTable方法):

  public List<OwnershipRole> GetOwnershipRoles() {
    using var cnn = new SqlConnection(_connectionString);

    cnn.Open();

    string sql =
      @"SELECT ItemID, 
               Title, 
               Status
          FROM ItemDetail";

    using var cmd = new SqlCommand(sql, cnn);

    var result = new List<OwnershipRole>(); 

    using var reader = cmd.OpenReader();

    while (reader.Read()) { 
      result.Add(new OwnershipRole(
        Guid.Parse(Convert.ToString(reader[0])),
        Convert.ToString(reader[1]),
        Convert.ToString(reader[2]) != "Withdrawn" 
      )); 
    }

    return result; 
  }

在此行

 Convert.ToString(reader[2]) != "Withdrawn" 

我们将最后一个字段( Status )读取为一个string ,然后通过与"Withdrawn"进行比较将其转换为bool

来自评论:

GetTable() 方法真的让我害怕。 它实际上迫使您编写极易受到 sql 注入攻击的代码。

你会如何改进它?

我会做这样的事情:

private DataTable GetTable(string sql, SqlParameter[] paras = null)
{
    var rv = new DataTable();
    using var cnn = new SqlConnection(_connectionString);
    using var cmd = new SqlCommand(sql, cnn);
    using var da = new SqlDataAdapter(cmd);
    if (paras != null && paras.Length > 0)
    {
        cmd.Parameters.AddRange(paras);
    }

    da.Fill(rv);
    return rv;
}

private IEnumerable<IDataRecord> GetRows(string sql, SqlParameter[] paras = null)
{
    var rv = new DataTable();
    using var cnn = new SqlConnection(_connectionString);
    using var cmd = new SqlCommand(sql, cnn);
    if (paras != null && paras.Length > 0)
    {
        cmd.Parameters.AddRange(paras);
    }
    cnn.Open();
    using var rdr = cmd.ExecuteReader();
    while (rdr.Read())
    {
        yield return rdr;
    }
}

private IEnumerable<T> GetItems<T>(string sql, Func<IDataRecord, T> transform, SqlParameter[] paras = null)
{
    var rv = new DataTable();
    using var cnn = new SqlConnection(_connectionString);
    using var cmd = new SqlCommand(sql, cnn);
    if (paras != null && paras.Length > 0)
    {
        cmd.Parameters.AddRange(paras);
    }
    cnn.Open();
    using var rdr = cmd.ExecuteReader();
    while (rdr.Read())
    {
        yield return transform(rdr);
    }
}

第一种方法按原样工作。 如果确实没有参数,您甚至不需要为简单查询使用附加参数。 另外两种方法是这样工作的:

public IEnumerable<OwnershipRole> GetOwnershipRoles()
{
    var sql = @"
SELECT ItemID, Title, Status
FROM ItemDetail";

    var data = GetRows(sql);
    return data.Select(dr => new OwnershipRole
        {
            Id = (Guid)dr[0],
            Title = (string)dr[1],
            IsWithdrawn = ((string)dr[2])!="Withdrawn"
        });
}

像这样:

public IEnumerable<OwnershipRole> GetOwnershipRoles()
{
    var sql = @"
SELECT ItemID, Title, Status
FROM ItemDetail";

    return GetItems<OwnershipRole>(sql, row => new OwnershipRole
       {
           Id = (Guid)dr[0],
           Title = (string)dr[1],
           IsWithdrawn = ((string)dr[2])!="Withdrawn"
       });
}

请注意,返回类型从List更改为IEnumerable 一个好的经验法则是尽可能长时间地将事物作为IEnumerable保留。 这可以减少内存使用并使您设置为流值,因此在某些情况下,您根本不需要内存中的整个结果集。 很快我们就会想要使用IAsyncEnumerable

当您确实需要一个列表(这比您想象的要少)时,您可以调用该方法附加ToList()调用。 尤其要小心在GetRows()函数上调用ToList() ,因为它在每次迭代中产生相同的对象 做错了会给你一堆代表最后一行的记录。

最后,如果您不知道如何使用 SqlParameter 对象,请停止并去谷歌搜索,因为这真的很重要

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM