简体   繁体   中英

C# return multiple columns and rows from SqlDataReader

I have a scenario to query DB for two columns and return all the rows to a different method for further processing. I am not sure if my approach below is the best. Can you please share any better techniques to accomplish this. The below code works well for a single row returned from SQL.

public (Int32 SiteID, string SiteName) QueryDB(string ConnStr)
{
    Int32 SiteID = 0;
    string SiteName = "";
    using (SqlConnection con = new SqlConnection(ConnStr))
        {
            SqlCommand cmd = new SqlCommand("spGetSiteDetails", con);
            con.Open();
            SqlDataReader reader = cmd.ExecuteReader();
            while (reader.Read())
            {
                SiteID = Convert.ToInt32(reader[0]);
                SiteName = reader[1].ToString();
            }
        }
    return (SiteID, SiteName);
}

In order to address multiple rows result, I am using string concatenated in a List. I believe there must be a better way of doing this because I have an overhead of having to split the string in the list to use the values..

public List<string> QueryDB(string ConnStr)
{
    List<string> SiteDetails = new List<string>();
    using (SqlConnection con = new SqlConnection(ConnStr))
    {
        SqlCommand cmd = new SqlCommand("spGetSiteDetails", con);
        con.Open();
        SqlDataReader reader = cmd.ExecuteReader();
        while (reader.Read())
        {
            SiteDetails.Add(reader[0] + "|" + reader[1]);
        }
    }
    return SiteDetails;
}

You can return a List<(int SiteID, string SiteName)> :

var list = new List<(int, string)>();
while (reader.Read())
{
    var siteID = Convert.ToInt32(reader[0]);
    var siteName = reader[1].ToString();
    list.Add((siteID, siteName));
}
return list;

However, personally I'd recommend not returning value-tuples in public APIs, and returning your own custom type instead - ie a List<SiteInfo> for a class SiteInfo or readonly struct SiteInfo that has an int SiteID and string SiteName as properties; ie

public sealed class SiteInfo
{
    public int SiteID {get;set;}
    public string SiteID {get;set;}
}

with

var list = new List<SiteInfo>();
while (reader.Read())
{
    var siteID = Convert.ToInt32(reader[0]);
    var siteName = reader[1].ToString();
    list.Add(new SiteInfo { SiteID = siteID, SiteName = siteName });
}
return list;

Either way: Dapper could really help you with this!

public List<(int SiteID, string SiteName)> QueryDB(string ConnStr)
{
    using (SqlConnection con = new SqlConnection(ConnStr))
    {
        return con.Query<(int,string)>("spGetSiteDetails",
              commandType: CommandType.StoredProcedure).AsList();
}

or

public List<SiteInfo> QueryDB(string ConnStr)
{
    using (SqlConnection con = new SqlConnection(ConnStr))
    {
        return con.Query<SiteInfo>("spGetSiteDetails",
              commandType: CommandType.StoredProcedure).AsList();
}

To return a collection you can write:

public IEnumerable<(Int32 SiteID, string SiteName)> QueryDB(string ConnStr)
{
    using (SqlConnection con = new SqlConnection(ConnStr))
    {
            SqlCommand cmd = new SqlCommand("spGetSiteDetails", con);
            con.Open();
            SqlDataReader reader = cmd.ExecuteReader();
            while (reader.Read())
            {
                yield return (Convert.ToInt32(reader[0]), reader[1].ToString());
            }
    }
}

I found the DataTable which solves the problem straight away

dataTable.Load(reader);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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