How to get column value from Ado.Net reader - c#

I am trying to get value of a given table column from Ado.Net as follows
var query = "select transmission_time, actual_transaction_amount as sum_amount FROM table_name";
using (NpgsqlConnection conn = new NpgsqlConnection("connectionString"))
{
conn.Open();
NpgsqlCommand command = new NpgsqlCommand(query, conn);
using (NpgsqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
var amount = reader.GetDecimal(Convert.ToInt32(reader["sum_amount"]));
result.Add(amount.ToString());//= amount;
}
}
But I get an error
System.OverflowException: Value was either too large or too small for an Int32.
Blockquote
But when I change the line to this
var amount = reader.GetDecimal(Convert.ToInt64(reader["sum_amount"]));
I get the following error
Argument 1 cannot convert from 'long' to 'int'
Please I need to convert the figures in that column to numbers and return it in my result.
The values in the column I am trying to get are like these examples
10000, 5000, 20000, etc. in a postgreSql table
The application is a Asp.Net 6 application running on Windows
Any help will be appreciated.
Thak you

This line doesn't make sense:
var amount = reader.GetDecimal(Convert.ToInt32(reader["sum_amount"]));
I assume that what you're actually trying to do there is get an int value from the sum_amount column and convert it to a decimal. What that code actually does is get a boxed int (i.e. an object reference) from the sum_amount column, unbox it and then use that int value as an index to get a decimal from a different column that probably doesn't exist. I think that what you actually meant to do was this:
var amount = Convert.ToDecimal(reader.GetInt64("sum_amount"));

Related

SQLDataReader not returning rows/populating list

I'm trying to return all rows of a single column in my database to populate a list. When I execute the stored procedure in SQL, it works fine, but nothing gets returned when I try to do it in C#.
public static List<string> GetRows(string filter_one, string filter_two)
{
var retrievedRows = new List<string>();
var storedProc = "dbo.MyStoredProc";
using (SqlConnection connection = new SqlConnection(MY_CONNECTION_STRING))
using (SqlCommand command = new SqlCommand(storedProc, connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("#FilterOne", SqlDbType.VarChar).Value = filter_one;
command.Parameters.Add("#FilterTwo", SqlDbType.VarChar).Value = filter_two;
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
retrievedRows.Add(reader.GetString(0));
}
}
}
return retrievedRows;
}
Any ideas? I get no errors in the console or when I run it on IIS Express either. When I watch retrievedRows, the size stays at 0 even though when I run the same query in SQL with the same passed parameters, it returns results for me.
EDIT: Please excuse me, my brain must be running a bit slow today. One of the parameters I was passing was pointed at the (empty) value of the wrong webcontrol. I don't know how I missed this.
There is only one issue with your posted snippet that I can see, which could pose a problem:
command.Parameters.Add("#FilterOne", SqlDbType.VarChar).Value = filter_one;
command.Parameters.Add("#FilterTwo", SqlDbType.VarChar).Value = filter_two;
In this section, you're adding two VARCHAR parameters but not specifying a length for them. Try changing your code to add a length specification:
var filterOne = new SqlParameter("FilterOne", System.Data.SqlDbType.VarChar, 50);
The constructor in use here is SqlParameter(string, SqlDbType, int):
Parameters
-
parameterName (String): The name of the parameter to map.
dbType (SqlDbType): One of the SqlDbType values.
size (Int32): The length of the parameter.
When working with VARCHAR you must specify a length or anything outside of the default length (which is 1 byte for definitions and variables, and 30 bytes for CAST and CONVERT) will be truncated:
When n isn't specified in a data definition or variable declaration statement, the default length is 1. If n isn't specified when using the CAST and CONVERT functions, the default length is 30.

Access column name with ExecuteScalar in C#?

I have a stored procedure that returns 0 or 1 depending on certain outcomes. I often execute this procedure manually, so to have a description of the success/failure that's easily viewed in SSMS but still readable as 0/1 in code, I select the 0 or 1 as a different column name, i.e. SELECT 0 AS ThisReason or SELECT 0 AS ThatReason.
There is almost certainly a better way to handle this, but it got me curious - is it possible to read the name of the column you've selected when using ExecuteScalar in C#?
Not with ExecuteScalar but with ExecuteReader and SqlDataReader.GetName:
using (var con = new SqlConnection("connection-string"))
using (var cmd = new SqlCommand("storedprocedurename", con))
{
cmd.CommandType = CommandType.StoredProcedure;
// parameters here...
con.Open();
using (var rd = cmd.ExecuteReader())
{
if (rd.Read())
{
string column = rd.GetName(0); // first column
int value = rd.GetInt16(0); // or rd.GetInt32(0)
}
}
}
What you want to do is to get two bits of information as a result of a query. To use ExecuteScalar you will need to first "pack" those two bits into one. For example you could return a string starting with "+" or "-" indicating the success/failure, and the rest of the string could be a "reason".
There is no other way to do this with ExecuteScalar.

How to difirentiate between integer and decimal fields when using OracleDataReader.GetFieldType(i)

I am using OracleDataReader.GetFieldType(i) to get the returned recordset fields of a stored procedure in order to generate a CS class file that could be used to call this SP (I need to define a property for the class for each field returned by the SP - so I need the data type of the field).
The problem is I cannot determine if the field type should be an integer or decimal, because GetFieldType(i) returns System.Decimal if the field was of type Number, so I could not determine if the field was an integer or a double/decimal value.
Here is the code I am using to get the fields returned by the SP:
#region OpenDBConnection
oConnection.ConnectionString = sDBConnectionString;
OracleCommand oCommand;
oConnection.Open();
#endregion
#region GetTheFieldsOfTheGetProcedure
oCommand = new OracleCommand("SPName", oConnection);
oCommand.CommandType = System.Data.CommandType.StoredProcedure;
foreach (Parameter oParam in oliGetProcParams)
{
OracleParameter oOracleParam = new OracleParameter();
oOracleParam.ParameterName = oParam.ParamName;
oOracleParam.Value = DBNull.Value;
oOracleParam.Direction = oParam.ParamDirection;
oOracleParam.OracleType = oParam.ParamDataType;
oCommand.Parameters.Add(oOracleParam);
}
OracleParameter oCVParam = new OracleParameter("cv_1", OracleType.Cursor);
oCVParam.Direction = System.Data.ParameterDirection.Output;
oCommand.Parameters.Add(oCVParam);
OracleDataReader oReader = oCommand.ExecuteReader();
if (oReader != null)
{
for (int i = 0; i < oReader.FieldCount; i++)
{
CustomField oField = new CustomField();
oField.Name = oReader.GetName(i);
oField.DataType = oReader.GetFieldType(i).ToString();
oliFields.Add(oField);
}
}
#endregion
oCommand.Dispose();
oReader.Dispose();
You can use DbDataReader.GetSchemaTable() to get the metadata DataTable, which contains more information about the types, including the precision and scale.
The schema table has a row for each column in the data reader, and columns like ColumnName and NumericPrecision / NumericScale. So you can walk one row after another and get all the metadata you need.
However, do note that you still need to read the value as decimal - the provider will give it to you as decimal. You should be able to map those manually to more proper types, though. Default number precisions are a bit annoying on Oracle, though, so it's quite likely you'll need to stick with decimal anyway (number(20, 0) is too big for long even though it's an integer).
I think you can do it casting the values to typeof int in a try catch, if it's not a int it should be a double.
That the approach i would try.

Datareader contains seemingly random amount of rows, despite of Select statement criteria

I have searched far and wide, but most Datareader problem/answer pairs concern getting past the first row, not returning anything, getting single values using datareader, etc.. Nothing quite like waht I'm encountering at the moment.
To be clear, this is an assignment for my evening class, albeit just a very small part of it.
the function takes size as int; the table has two colmuns: col1 and col2 of which col1 holds the index value as double and col2 holds a randomly generated double. table is an excel worksheet in a workbook, don't know if that's relevant.
table has been populated using an insert statement carried out by an ADO command object without problems.
Now, instead of supplying me with the amount of rows as specified by size/#size in the query (as it plays the double role of index/UID in this case), the datareader object fetches seemingly random amounts of rows. I say seemingly, because the number does seem to be fixed to the value of "size" (eg. size = 10 -> datareader contains 3 rows after .executeReader(); size = 2 -> datareader contains 113 rows; size = 5 -> 446 rows).
While debugging I kept track of #size parameter for the query remains 10.0
and I can't put my finger on when/why reader.Read() turns False.
I also substituted the parameter in the query string with a literal (5.0); which resulted in a type mismatch in criteria expression exception. But it's all Doubles, or am I missing something?! I'm guessing this is going to be the kicker somehow, but I'm at a loss right now.
As you can probably guess I'm pretty new at programming, so bear with me please.
What causes my code to behave the way it does?
private Double[] getSelection(int size, string table)
{
List<Double> list = new List<Double>();
Double[] toSort;
OleDbConnection connect = new OleDbConnection(cntstring);
connect.Open();
OleDbCommand command = connect.CreateCommand();
command.CommandType = CommandType.Text;
command.Parameters.Add("#size", OleDbType.Double).Value = Convert.ToDouble(size);
command.CommandText = String.Format("SELECT * FROM [{0}$] WHERE col1 < #size;", table);
try
{
OleDbDataReader reader = command.ExecuteReader();
Double outputReader;
while (reader.Read())
{
outputReader = Convert.ToDouble(reader.GetValue(1)); /for some reason (which is not my main concern at the moment) the reader.getDouble() method returned an invalid cast exception
list.Add(outputReader);
}
toSort = new double[list.Count()];
foreach (double d in list)
{
toSort[list.IndexOf(d)] = d;
}
string output = String.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}", toSort[0], toSort[1], toSort[2], toSort[3], toSort[4], toSort[5], toSort[6], toSort[7], toSort[8], toSort[9]);
//to check for values; the String.Format is where i first encountered the index out of bounds exception
MessageBox.Show(output);
reader.Close();
reader.Dispose();
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
finally
{
connect.Close();
connect.Dispose();
return toSort;
}
}
Did you try single quotes around #size in your select statement, i.e.
'#size'

Checking and Saving/Loading from MySQL C#

I am making something that requires MySQL. I have the saving done from in-game, which is simply done by INSERT.
I have a column that will have a password in and I need to check if the inputted password matched any of the rows and then if it is, get all of the contents of the row then save it to variables.
Does anyone have an idea how to do this in C#?
//////////////////////////
I have found how to save and get the string, however it will only get 1 string at a time :(
MySql.Data.MySqlClient.MySqlCommand command = conn.CreateCommand();
command.CommandText = "SELECT * FROM (player) WHERE (pass)";
command.ExecuteNonQuery();
command.CommandType = System.Data.CommandType.Text;
MySql.Data.MySqlClient.MySqlDataReader reader = command.ExecuteReader();
reader.Read();
ayy = reader.GetString(1);
print (ayy);
if(ayy == password){
//something
}
My best practice is to use MySQLDataAdapter to fill a DataTable. You can then iterate through the rows and try to match the password.
Something like this;
DataTable dt = new DataTable();
using(MySQLDataAdapter adapter = new MySQLDataAdaper(query, connection))
{
adapter.Fill(dt);
}
foreach(DataRow row in dt.Rows)
{
//Supposing you stored your password in a stringfield in your database
if((row.Field<String>("columnName").Equals("password"))
{
//Do something with it
}
}
I hope this compiles since I typed this from my phone. You can find a nice explanation and example here.
However, if you are needing data from a specific user, why not specificly ask it from the database? Your query would be like;
SELECT * FROM usercolumn WHERE user_id = input_id AND pass = input_pass
Since I suppose every user is unique, you will now get the data from the specific user, meaning you should not have to check for passwords anymore.
For the SQL statement, you should be able to search your database as follows and get only the entry you need back from it.
"SELECT * FROM table_name WHERE column_name LIKE input_string"
If input_string contains any of the special characters for SQL string comparison (% and _, I believe) you'll just have to escape them which can be done quite simply with regex. As I said in the comments, it's been a while since I've done SQL, but there's plenty of resources online for perfecting that query.
This should then return the entire row, and if I'm thinking correctly you should be able to then put the entire row into an array of objects all at once, or simply read them string by string and convert to values as needed using one of the Convert methods, as found here: http://msdn.microsoft.com/en-us/library/system.convert(v=vs.110).aspx
Edit as per Prix's comment: Data entered into the MySQL table should not need conversion.
Example to get an integer:
string x = [...];
[...]
var y = Convert.ToInt32(x);
If you're able to get them into object arrays, that works as well.
object[] obj = [...];
[...]
var x0 = Convert.To[...](obj[0]);
var x1 = Convert.To[...](obj[1]);
Etcetera.

Categories

Resources