I have put together the following method:
public static ArrayList DbQueryToArry()
{
string SqlCString = "connString";
SqlConnection connection = null;
ArrayList valuesList = new ArrayList();
connection = new SqlConnection(SqlCString);
connection.Open();
SqlCommand command = new SqlCommand("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT", connection);
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
valuesList.Add(Convert.ToString(reader[0]));
}
return valuesList;
}
I'd like to be able to run an assertion like this one:
var a = DbQueryToArry();
Assert.IsTrue(a.Contains("some value"));
Given reader [0]
valuesList.Add(Convert.ToString(reader[0]));
I only get the first column (CLIENTINFO) into the array and not the second (ACCOUNT_Purpose). How should I modify the code to get both ?
In addition, the returned values could be either a String or Int so would my current code version should be handling both ?
Thanks in advance.
If we switch from obsolete ArrayList to something like IEnumerable<T>:
public static IEnumerable<IDataRecord> DbQueryToArray(string sql) {
if (null == sql)
throw new ArgumentNullException(nameof(sql));
//TODO: do not hardcode connetcion string but read it (say, from Settings)
string SqlCString = "connString";
//DONE: Wrap IDisposable into using
using (SqlConnection connection = new SqlConnection(SqlCString)) {
connection.Open();
//DONE: Wrap IDisposable into using
using (SqlCommand command = new SqlCommand(sql, connection)) {
//DONE: Wrap IDisposable into using
using (SqlDataReader reader = command.ExecuteReader()) {
while (reader.Read()) {
yield return reader as IDataRecord;
}
}
}
}
}
then you can use Linq in order to query the result:
var a = DbQueryToArray("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT");
Assert.IsTrue(a.Any(record =>
Convert.ToString(record["CLIENTNO"]) == "some value"));
Assert.IsTrue(a.Any(record =>
Convert.ToString(record["ACCOUNT_Purpose"]) == "some other value"));
If you don't want execute query several times, you can materialize the results:
var a = DbQueryToArray("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT")
.ToList();
Assert.IsTrue(a.Any(record => Convert.ToString(record[0]) == "some value"));
Assert.IsTrue(a.Any(record => Convert.ToString(record[1]) == "some other value"));
Finally (see comments below), if we want to test if any field in any record has the value:
var a = DbQueryToArray("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT")
.SelectMany(line => {
// Flatten the cursor into IEnumerable<String>
string[] result = new string[line.FieldCount];
for (int i = 0; i < result.Length; ++i)
result[i] = Convert.ToString(line[i]);
return result;
});
a.Any(item => item == "some value");
It is because you only read the first value of the reader. Reader.Read() read each row one by one and Convert.ToString(reader[0])) means that you want to read the first column as string.
That's cause you are getting only the first column. You can do something like below by specifying the column name
while (reader.Read())
{
valuesList.Add(Convert.ToString(reader["CLIENTNO"]));
valuesList.Add(Convert.ToString(reader["ACCOUNT_Purpose"]));
}
Moreover, since you are converting all the columns to string; I would suggest to use a strongly typed collection like List<string> rather then ArrayList valuesList = new ArrayList();
The other answers are good, However some concerns
Lets stop using ArrayList, and use a List<T> instead
Lets use a using statement where you can
Note : I have used a ValueTuple to return more than 1 field
Example
public static List<(string clientNo, string account)> DbQueryToArray()
{
const string SqlCString = "connString";
var valuesList = new List<(string clientNo, string account)>();
using (var connection = new SqlConnection(SqlCString))
{
connection.Open();
using (var command = new SqlCommand("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT", connection))
{
var reader = command.ExecuteReader();
while (reader.Read())
valuesList.Add(((string)reader[0],(string)reader[1]) );
}
}
return valuesList;
}
Usage
var results = DbQueryToArray();
Assert.IsTrue(results.Any(x => x.clientNo == someValue || x.account == someValue));
best practice is to check first if the reader has rows
reader.HasRows
then close the reader and the connection
your code should look like this:
public static ArrayList DbQueryToArry()
{
string SqlCString = "connString";
SqlConnection connection = null;
ArrayList valuesList = new ArrayList();
connection = new SqlConnection(SqlCString);
using (connection)
{
connection.Open();
SqlCommand command = new SqlCommand("Select CLIENTNO, ACCOUNT_Purpose from audit.ACCOUNTS_AUDIT", connection);
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
valuesList.Add(Convert.ToString(reader[0]));
valuesList.Add(Convert.ToString(reader[1])); // add to valuelist
}
}
reader.Close(); // close reader
} //dispose connection
return valuesList;
}
Use DataTable and SqlDataAdapter to get query result in form of table. Something like this:
string connString = #"your connection string here";
string query = "select * from table";
DataTable dataTable = new DataTable();
SqlConnection conn = new SqlConnection(connString);
SqlCommand cmd = new SqlCommand(query, conn);
conn.Open();
// create data adapter
SqlDataAdapter da = new SqlDataAdapter(cmd);
// this will query your database and return the result to your datatable
da.Fill(dataTable);
conn.Close();
da.Dispose();
Then you can use dataTable object to see if particular values exist.
Related
I have a method that returns a string from a database based on what i pass to it. The method works but i need to display the result in a textbox on a form. But when i run it the result i keep getting is "System.Collections.Generic.List`1[System.String]". I cant work this out. I try looping through the interest variable with for loop but this doesn't work either.
var interest = GetValue2(tvp, connectionString); -- this is my method that returns a string
textBox2.Text = string.Join(", ", interest.ToString());
this returns "System.Collections.Generic.List`1[System.String]" to textbox2. Any ideas what im missing here? thanks
PART 2:
public static List<String> GetValue2(DataTable tvp, String connectionString)
{
List<String> items = new List<String>();
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand("[dbo].[...]", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParameter = new SqlParameter();
tvpParameter.ParameterName = "#..";
tvpParameter.SqlDbType = System.Data.SqlDbType.Structured;
tvpParameter.Value = tvp;
tvpParameter.TypeName = "[dbo].[....]";
cmd.Parameters.Add(tvpParameter);
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
Console.WriteLine((String)rdr["id"]);
}
Console.ReadLine();
}
}
return items;
}
Could you share the GetValue2 method? Probably you are calling ToString() on List in that method and it that case the GetValue2 method returns
"System.Collections.Generic.List1[System.String]"
Example:
var test = new List<string>{"some text"};
test.ToString(); // returns System.Collections.Generic.List`1[System.String]
Update:
Because the method GetValue2 returns List<string> avoid calling .ToString() on interest
var interest = GetValue2(tvp, connectionString); // returns List<string>
textBox2.Text = string.Join(", ", interest);
There's also the issue of the List<string> items inside the GetValue2() not being populated.
Here's the corrected method:
public static List<String> GetValue2(DataTable tvp, String connectionString)
{
List<String> items = new List<String>();
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand("[dbo].[...]", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter tvpParameter = new SqlParameter();
tvpParameter.ParameterName = "#..";
tvpParameter.SqlDbType = System.Data.SqlDbType.Structured;
tvpParameter.Value = tvp;
tvpParameter.TypeName = "[dbo].[....]";
cmd.Parameters.Add(tvpParameter);
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
items.Add((String)rdr["id"]);
}
}
}
return items;
}
I am creating a SQLConnection to access some data. I need to print the entire DB result in log
I can able to print the specific cell value by referring out the cell, but I want to print the query result at single shot
string queryString = "select top 1 column_1, column_2 from master_table";
using (SqlConnection dbConnection = new SqlConnection(envDBConnectionString))
{
SqlCommand dbCommand = new SqlCommand(queryString, dbConnection);
dbConnection.Open();
SqlDataReader dbReader = dbCommand.ExecuteReader();
if (dbReader.HasRows)
{
while (dbReader.Read())
{
string col1Value = dbReader.GetValue(0).ToString());
string col2Value = dbReader.GetValue(1).ToString());
}
}
dbReader.Close();
dbConnection.Close();
}
Here I am getting each column value and stores it to string variable. instead I want to store the entire query result in that string
I suggest method extracting, i.e.
// Given a query, return records
private static IEnumerable<IDataRecord> QueryLines(string query) {
using (SqlConnection con = new SqlConnection(envDBConnectionString)) {
con.Open();
using (var q = new SqlCommand(query, con)) {
using (var reader = q.ExecuteReader()) {
while (reader.Read())
yield return reader as IDataRecord;
}
}
}
}
Then we can easily use it:
string command =
#"select top 1
column_1 as Column1,
column_2 as Column2
from master_table";
Having records, write them into a log in a desired format, e.g. simple CSV
File.AppendAllLines(#"c:\MyLog.txt", QueryLines(command)
.Select(record => $"{record[0]},{record[1]}"));
Or in elaborated XML
File.AppendAllLines(#"c:\MyLog.txt", QueryLines(command)
.Select(record => string.Concat(Enumerable
.Range(0, record.FieldCount)
.Select(i => $"<{record.GetName(i)}>{record[i]}</{record.GetName(i)}>")));
string queryString = "select top 1 column_1, column_2 from master_table";
using (SqlConnection dbConnection = new SqlConnection(envDBConnectionString))
{
SqlCommand dbCommand = new SqlCommand(queryString, dbConnection);
SqlDataAdapter sa = new SqlDataAdapter(dbCommand);
DataTable dt = new DataTable(); //All your data in this datatable
sa.fill(dt);
}
for reference:
Inside loop:
foreach(DataRow row in dt.rows){
row["COLUMN_NAME"].toString(); //or whatever datatype
}
I'm updating some old legacy code and I ran into a problem with the
SqlCommand.ExecuteReader() method. The problem is that it's not returning any
results. However, using SqlDataAdapter.Fill(), I get results back from the
database. What am I doing wrong? How can I get results back using the data
reader?
var connectionString = ConfigurationManager.ConnectionStrings["MyConnectionString"].ToString();
using (var sqlConnection = new SqlConnection(connectionString))
{
using (var sqlCommand = new SqlCommand())
{
sqlCommand.Connection = sqlConnection;
sqlCommand.CommandType = CommandType.Text;
sqlCommand.CommandText = "SELECT * FROM MyTable WHERE ID = 1";
sqlConnection.Open();
// This code works.
//var dataTable = new DataTable();
//using (var sqlDataAdapter = new SqlDataAdapter(sqlCommand))
//{
// sqlDataAdapter.Fill(dataTable);
//}
// This code is not working.
using (var sqlDataReader = sqlCommand.ExecuteReader())
{
while (sqlDataReader.Read())
{
// This fails because the data reader has no results.
var id = sqlDataReader.GetInt32(0);
}
}
}
}
Could it be that there is no Int32 in your results ?
var id = sqlDataReader.GetInt32(0); // <-- this might not be an Int32
Either try:
var id = sqlDataReader.GetValue(0);
Or cast to the correct type (BIGINT for example is Int64), not sure without seeing your data.
Try this..
var id = 0;
using (var sqlDataReader = sqlCommand.ExecuteReader())
{
while (sqlDataReader.Read())
{
id = sqlDataReader.GetInt32(sqlDataReader.GetOrdinal("ColName"));
}
}
I have moved the variable outside of the reader code or the variable will only be accessible inside that scope. I would avoid specifying the ordinal in the code, in case someone altered the columns in the DB.
Also, specify the columns in the SQL statement... SELECT ColName FROM ... and use params in the query
If you got to that line then it has results
Does not mean the value is not null
And you should not use a SELECT *
If may have a problem with an implicit cast
Try Int32
try
{
if(sqlDataReader.IsDBNull(0))
{
// deal with null
}
else
{
Int32 id = sqlDataReader.GetInt32(0);
}
}
catch (SQLexception Ex)
{
Debug.WriteLine(Ex.message);
}
var connectionString = ConfigurationManager.ConnectionStrings["MyConnectionString"].ToString();
using (var sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
string sql = "SELECT * FROM MyTable WHERE ID = 1";
using (var sqlCommand = new SqlCommand(sql, sqlConnection))
{
using (var sqlDataReader = sqlCommand.ExecuteReader())
{
while (sqlDataReader.Read())
{
// This fails because the data reader has no results.
var id = sqlDataReader.GetInt32(0);
}
}
}
}
I am using generic list to store the data that comes by querying the databse.I uses List of classes actually for multiple rows.
But my problem is my classes have almost more than 20 properties and most of the time i uses only its 2 or 3 properties.
So I want to know that what is the best way to keep the data coming from database.
Below is my code
List<ImageGalleryCollection> tempList = new List<ImageGalleryCollection1>();
SqlConnection connection = Dal.GetConnection();
SqlParameter[] paramList = new SqlParameter[1];
paramList[0] = new SqlParameter("#cityId", cityId);
SqlDataReader data = Dal.ExecuteReaderSP(SPNames.GetRegCity, paramList, connection);
while(data.Read())
{
ImageGalleryCollection igc = new ImageGalleryCollection1();
igc.cityPhotoGalleryId = Convert.ToInt32(data["cityPhotoGalleryId"]);
igc.ImagePath = data["imagePath"].ToString();
tempList.Add(igc);
}
data.Close();
connection.Close();
return tempList;
In ImageGalleryCollection I have more that 20 properties and above i only uses two properties.I think it is very inefficient
Can you how your base class implementation? You can create another class with the most using attributes and use an object of that class inside your class.
IEnumerable<ImageGalleryCollection> GetImageGalleryCollection()
{
SqlConnection connection = Dal.GetConnection();
SqlParameter[] paramList = new SqlParameter[1];
paramList[0] = new SqlParameter("#cityId", cityId);
SqlDataReader data = Dal.ExecuteReaderSP(SPNames.GetRegCity, paramList,connection);
while(data.Read())
{
ImageGalleryCollection igc = new ImageGalleryCollection1();
igc.cityPhotoGalleryId = Convert.ToInt32(data["cityPhotoGalleryId"]);
igc.ImagePath = data["imagePath"].ToString();
yield return igc;
}
data.Close();
connection.Close();
}
I would like to suggest you to write a extension method for SqlDataReader and make use of the method in linq to fetch required columns from the returned rows of reader.
Extension method:
public static class DataReaderExtension
{
public static IEnumerable<Object[]> DataRecord(this System.Data.IDataReader source)
{
if (source == null)
throw new ArgumentNullException("source");
while (source.Read())
{
Object[] row = new Object[source.FieldCount];
source.GetValues(row);
yield return row;
}
}
}
using it in linq:
using (SqlConnection cn = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand("Select * from tblUser"))
{
cmd.CommandType = CommandType.Text;
cmd.Connection = cn;
cn.Open();
using (SqlDataReader dr = cmd.ExecuteReader())
{
var result = (from row in dr.DataRecord()
select new
{
UserId = row[0],
UserName = row[1]
}).ToList();
}
}
}
This result list has only the required properties you select and helps to reduce the consumption of memory for unwanted properties.
I have the following code which should be able to get the primary key in a table
public List<string> GetPrimaryKeysForTable(string tableName)
{
List<String> retVal = new List<string>();
SqlCommand command = connector.GetCommand("sp_pkeys");
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.AddWithValue("#table_name", typeof(SqlChars)).Value = tableName;
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
retVal.Add(reader[3].ToString());
}
return retVal;
}
I have a table called Users in my database. When I pass Users in as my parameter, the reader returns no results. Any idea why this might be failing to return my primary keys?
Try this instead:
command.Parameters.Add("#table_name", SqlDbType.NVarChar).Value = tableName;
It looks like you have an issue with your usage of .AddWithValue() which may be causing your problem. See the fix below:
public List<string> GetPrimaryKeysForTable(string tableName)
{
List<string> retVal = new List<string>();
SqlCommand command = connector.GetCommand("sp_pkeys");
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.AddWithValue("#table_name", tableName);
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
retVal.Add(reader[3].ToString());
}
return retVal;
}
In your example you are attempting to add the #table_name as a Type rather than a string.
Any chance it needs to be qualified with the schema? Something like:
command.Parameters.AddWithValue("#table_name", typeof(SqlChars)).Value = "dbo." + tableName;
or:
command.Parameters.AddWithValue("#schema", "dbo");
Or is there some parm you need to add to tell it the database inside the server?
You can simply use as below, would you please try it out:
command.Parameters.AddWithValue("#table_name", ("dbo." + tableName));
The stored procedure 'sp_pkeys' takes three parameters:
command.Parameters.Add("#table_name", SqlDbType.NVarChar).Value = tableName;
command.Parameters.Add("#table_owner", SqlDbType.NVarChar).Value = tableOwner;
command.Parameters.Add("#table_qualifier", SqlDbType.NVarChar).Value = tableQualifier;
It works with just #table_name but could try passing all three to see if it makes a difference.
Question: Does your table have any keys defined?
I have run the slightly modified code below against severa tables in a test DB of my own and it works as expected:
static class Program
{
static void Main(string[] args)
{
var connStr = new SqlConnectionStringBuilder
{
DataSource = "localhost",
InitialCatalog = "RichTest",
IntegratedSecurity = true
};
using (var conn = new SqlConnection(connStr.ToString()))
{
conn.Open();
var parents = GetPrimaryKeysForTable(conn, "People");
Console.WriteLine("Parent Keys:");
foreach (var p in parents)
{
Console.WriteLine(" {0}", p);
}
}
}
static IList<string> GetPrimaryKeysForTable(SqlConnection conn, string tableName)
{
if (conn == null) throw new ArgumentNullException("Value is null", "conn");
if (string.IsNullOrWhiteSpace(tableName)) throw new ArgumentNullException("Value is null or emtpy", "tableName");
List<String> retVal = new List<string>();
using (var command = new SqlCommand
{
Connection = conn,
CommandText = "sp_pkeys",
CommandType = CommandType.StoredProcedure
})
{
command.Parameters.AddWithValue("#table_name", typeof(SqlChars)).Value = tableName;
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
retVal.Add(reader["COLUMN_NAME"].ToString());
}
}
return retVal;
}
}