I'm wondering how to return the result from a SELECT COUNT statement in C#.
I have a sql statement that returns the count of 15.
Currently, I'm returning the datareader. Can I somehow return the result of that as a string?
static public SqlDataReader FillDataReader(string sql, SqlParameter[] parms)
{
SqlConnection conn = new SqlConnection(ConnectionString);
SqlCommand cmd = new SqlCommand(sql, conn);
SqlDataReader dr = null;
conn.Open();
cmd.CommandTimeout = 120; //120 seconds for the query to finish executing
foreach (SqlParameter p in parms)
{
cmd.Parameters.Add(p);
}
try
{
dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
catch (SqlException ex)
{
if (dr != null)
{
dr.Close();
}
conn.Close();
//DBUtilExceptionHandler(ex, sql);
throw ex;
}
finally
{
}
return dr; //This could be null...be sure to test for that when you use it
}
Or I could use a开发者_开发知识库 different method. I just don't know what it should be.
Any help is appreciated.
This is my select statement:
select count(LeadListID) from LeadLists WHERE SalesPersonID = 1
AND LeadListDateCreated BETWEEN '9/1/11' AND '10/1/11 23:59:59'
Sure - just use:
int count = (int) query.ExecuteScalar();
// TODO: Decide the right culture to use etc
return count.ToString();
Notes:
- Use
using
statements instead of manual try/catch/finally blocks - You should close the connection whether or not there was an error
- Given that the natural result of the query is an integer, I would change it to return an
int
, not astring
. Let the caller make that conversion if they want to - If there's an error, you should almost certainly let the exception bubble up, rather than returning
null
I would write the code as:
public static int ExecuteScalarInt32(string sql, SqlParameter[] parms)
{
using (SqlConnection conn = new SqlConnection(ConnectionString))
using (SqlCommand command = new SqlCommand(sql, conn) { Parameters = parms })
{
conn.Open();
command.CommandTimeout = 120;
return (int) command.ExecuteScalar();
}
}
If you really needed a version to work on an arbitrary data reader, you could write it as:
public static T ExecuteQuery<T>(string sql, SqlParameter[] parms,
Func<SqlDataReader, T> projection)
{
using (SqlConnection conn = new SqlConnection(ConnectionString))
using (SqlCommand command = new SqlCommand(sql, conn) { Parameters = parms })
{
conn.Open();
command.CommandTimeout = 120;
return projection(command.ExecuteReader());
}
}
And then call it with:
int count = ExecuteQuery<int>(sql, parms, reader => {
if (!reader.MoveNext()) {
throw new SomeGoodExceptionType("No data");
}
return reader.GetInt32(0);
});
Sure, you can always call the ToString()
method in .NET on the single field when reading it:
dr[0].ToString()
Are there many records that you want to concat as a string? Then you loop through each row, grab the value as a string, and create a master string in a for loop fashion.
Since you're expecting only a single value, a better alternative to ExecuteReader
is the ExecuteScalar
method:
try
{
var count = cmd.ExecuteScalar().ToString();
}
Either cast it as a varchar in your sql:
select cast(count(LeadListID) as varchar(10))
from LeadLists
WHERE SalesPersonID = 1
AND LeadListDateCreated BETWEEN '9/1/11' AND '10/1/11 23:59:59'
or just call .ToString() on the result, as shown in other answers.
Additionally, I'm not a fan of relying on CommandBehavior.CloseConnection
for DataReaders. I much prefer code like this:
static public IEnumerable<IDataRecord> GetDataReader(string sql, SqlParameter[] parms)
{
using (var conn = new SqlConnection(ConnectionString))
using (var cmd = new SqlCommand(sql, conn))
{
cmd.CommandTimeout = 120; //120 seconds for the query to finish executing
foreach (SqlParameter p in parms)
{
cmd.Parameters.Add(p);
}
conn.Open();
using (var dr= cmd.ExecuteReader())
{
while (dr.Read())
{
yield return dr;
}
}
}
}
Use ExecuteScalar instead, that will return the first field from the first record of the returned recordset, which is what you want. That will return to you an object that is really an integer. Add a ToString to that and you should be good.
精彩评论