I'm struggling to understand why when I add an item to my string array "internalDives", the method exits and there is no debug error shown.
Please, what have I done wrong here?
private void GridDataConnection()
{
using (SqlCeConnection conn = new SqlCeConnection(ConnectionString))
{
conn.Open();
using (SqlCeCommand command = new SqlCeCommand("SELECT id,divelocation,divedate,diveduration FROM loggeddives", conn))
{
SqlCeDataReader readDiveResult = command.ExecuteReader();
var diveList = new List<string[]>();
while (readDiveResult.Read())
{
string[] internalDives = new string[4];
internalDives[0] = readDiveResult.GetString(0);
internalDives[1] = readDiveResult.GetString(1);
internalDives[2] = readDiveResult.GetString(2);
internalDives[3] = readDiveResult.GetString(3);
diveList.Add(internalDives);
i++;
}
}
conn.Close();
}
}
Are you sure all columns types are string? GetString doesn't make any conversion and suppose all columns are of type string. I suggest you to use a try catch statement for catching any InvalidCastException.
Check also that all values are instanced. You can do it with IsDBNull method.
The first thing you should do is absolutely to catch the exception that is being thrown, as mservidio mentioned in comments.
In all likelihood what you will discover when you do is that one of your calls to GetString is throwing an IndexOutOfBoundsException, so be prepared to step through your code carefully and look at what is coming down the Read pipeline. It might not be what you think it is.
I guess that you have null values in your database so, when you try to read one of the columns you get the error: "Data is Null. This method or property cannot be called on Null values". You must call the IsDBNull method to check if the value is null or not and after that read the value.
try this code
private void GridDataConnection()
{
using (SqlCeConnection conn = new SqlCeConnection(ConnectionString))
{
conn.Open();
using (SqlCeCommand command = new SqlCeCommand("SELECT id,divelocation,divedate,diveduration FROM loggeddives", conn))
{
SqlCeDataReader readDiveResult = command.ExecuteReader();
var diveList = new List<string[]>();
while (readDiveResult.Read())
{
string[] internalDives = new string[4];
internalDives[0] = readDiveResult.IsDBNull(0) ? "": readDiveResult.GetString(0);
internalDives[1] = readDiveResult.IsDBNull(1) ? "": readDiveResult.GetString(1);
internalDives[2] = readDiveResult.IsDBNull(2) ? "": readDiveResult.GetString(2);
internalDives[3] = readDiveResult.IsDBNull(3) ? "": readDiveResult.GetString(3);
diveList.Add(internalDives);
i++;
}
}
conn.Close();
}
}
I usually validate information coming back from a query before attempting to add it to an array or a list. Do a quick check to verify that all of the values are strings and perhaps even get a count to verify there are 4 values. As others have mentioned a try catch is quite helpful. You can also create a quick error log to dump any exceptions into a temp file.
First answer be gentle!
Related
In a C# program with .NET framework to make a windows form I have added the following code to make a connection and to get the value of result and store in a list for later usage
public List<Info> GetInfo(string Col)
{
string connectionString = "Server=VIS-12\\TESTSQLSERVER;Database=SPCC;User Id=sa;Password=admin1234;";
string comand = $"select {Col} from Tbl_Reading where [LogTime] between '2017-07-06 14:30:26.000' and '2017-07-06 14:30:26.000' ";
using (SqlConnection conn = new SqlConnection())
{
double[] val1 = new double [100] ;
conn.ConnectionString = connectionString;
conn.Open();
SqlCommand c1 = new SqlCommand(comand, conn);
SqlDataReader reader = c1.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
int x = 0;
val1[x] = Convert.ToDouble(reader.GetDouble(0)); //this line is throwing the error
x++;
}
}
reader.Close();
List<Info> d1 = new List<Info>();
d1 = (List<Info>)val1.Cast<Info>();//ToList();
conn.Close();
return d1;
//return val1;
}
}
I still keep getting an invalid cast Exception even though the return type and the variable type is same
GetDouble does indeed return type double but there has to be a double value to get. Internally, that method will get an object reference from the specified column and cast it as type double. It is that cast that is invalid, not one that you're performing. If you look at the stack trace of the exception then you should be able to see that.
Either the column you're retrieving is the wrong data type or at least one row contains NULL in that column. If the data type is correct then you need to either filter out NULL values in your query or else account for them in your reading code.
GetDouble will throw an exception if the value internally is not a double. This will happen obviously if the column is a different type, but also if the value is a null. So you need to handle that case.
Other notes:
if (reader.HasRows) is not necessary as you can use reader.Read() to tell you the same thing.
Use a multi-line string with # to make your query more readable.
I hope Col is not coming from user-input, otherwise you have a major injection vulnerability.
You are missing using blocks to dispose your objects.
There seems to be no need for the array, just convert each value as you go along and insert into a list.
between doesn't seem to make sense here, also you should probably use a parameter for the date. If there is really a start and end time, use >= AND < rather than BETWEEN.
Consider putting the connection string in a settings file, rather than hard-coding it.
public List<Info> GetInfo(string Col)
{
string connectionString = "Server=VIS-12\\TESTSQLSERVER;Database=SPCC;User Id=sa;Password=admin1234;";
string comand = #$"
select {Col}
from Tbl_Reading
where LogTime = #time;
";
using (SqlConnection conn = new SqlConnection(connectionString))
using (SqlCommand c1 = new SqlCommand(comand, conn))
{
c1.Parameters.Add("#time", SqlDbType.DateTime).Value = DateTime.Parse("2017-07-06 14:30:26.000");
List<Info> d1 = new List<Info>();
conn.Open();
using (SqlDataReader reader = c1.ExecuteReader())
{
while (reader.Read())
{
d1.Add((reader[0] as double) as Info);
}
return d1;
}
}
Hi I am trying to create CRUD functions in C# but am stuck on my first one which is FetchALL, as so far it says not all code path returns a value.
Heres my code so far
public SqlDataReader FetchAll(string tableName)
{
using (SqlConnection conn = new SqlConnection(_ConnectionString,))
{
string query = "SELECT * FROM " + tableName;
SqlCommand command = new SqlCommand(query, conn);
using (SqlDataReader reader = command.ExecuteReader())
conn.Open();
conn.Close();
}
}
}
}
I can give you more information, thanks
You have a return type of SqlDataReader, but you aren't returning anything anywhere in your code. At the very least you should declare your data reader and return it like this:
public SqlDataReader FetchAll(string tableName)
{
SqlDataReader reader;
using (SqlConnection conn = new SqlConnection(_ConnectionString))
{
string query = "SELECT * FROM " + tableName;
// added using block for your command (thanks for pointing that out Alex K.)
using (SqlCommand command = new SqlCommand(query, conn))
{
conn.Open(); // <-- moved this ABOVE the execute line.
reader = command.ExecuteReader(); // <-- using the reader declared above.
//conn.Close(); <-- not needed. using block handles this for you.
}
}
return reader;
}
Note, I've noted a few other problems I saw as well, which you can see by my comments.
Also, I want to point out something very important: you should always avoid string concatenation in queries as this opens you up to the risk of a SQL injection attack (as gmiley has duly pointed out). In this case, you should create an enum which contains values associated with all the possible table names, and then use a dictionary to look up the table names based on their enum values. If a user provides an invalid/unknown value, you would then thrown an argument exception.
This isn't the end of your problems, though (as Default has pointed out). You can't create the connection in a using block, which disposes and closes as soon as it exits the block, and then use the SqlDataReader that is returned from the method. If I were you, I'd return a DataSet instead of a SqlDataReader. Here's how I'd do it:
First, create your enum of possible table values:
public enum Table
{
FirstTable,
SecondTable
}
And a dictionary that maps table enum values to the table names (which you will populate in your static constructor):
private static Dictionary<Table, string> _tableNames = new Dictionary<Table, string>(); // populate this in your static constructor.
And then here is your method to fetch the data:
public static System.Data.DataSet FetchAll(Table fromTable)
{
var ret = new System.Data.DataSet();
using (var conn = new System.Data.SqlClient.SqlConnection(_connectionString))
{
string tableName = "";
if (!_tableNames.TryGetValue(fromTable, out tableName)) throw new ArgumentException(string.Format(#"The table value ""{0}"" is not known.", fromTable.ToString()));
string query = string.Format("SELECT * FROM {0}", tableName);
using (var command = new System.Data.SqlClient.SqlCommand(query, conn))
{
using (var adapter = new System.Data.SqlClient.SqlDataAdapter(command))
{
adapter.Fill(ret);
}
}
}
return ret;
}
One final note, I'd advise you name your class-level variables with lower camel case per convention, e.g. _connectionString.
Firstly you aren't returning anything from the method. I'd add, are you sure you want to return a SqlDataReader? It is declared within a using block, so it will be closed by the time you return it anyway. I think you should re-evaluate what this function should return.
You need a return statment for the method to return a value.
How would I take info stored in a Select method and transfer it to a string? I'm trying to get the max value from the match_id column and get its value from command.CommandText into the matchCode string. Where would I go from here?
string connectString = "Server=myServer;Database=myDB;Uid=myUser;Pwd=myPass;";
string matchCode = "";
MySqlConnection connect = new MySqlConnection(connectString);
MySqlCommand command = connect.CreateCommand();
command.CommandText = "SELECT MAX(VAL(match_id)) FROM `data`";
try
{
connect.Open();
command.ExecuteNonQuery();
matchCode = "??";
connect.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
I'm new to C#, as it's like my fourth day trying it out. Thanks for the help!
The ExecuteNonQuery() method is for INSERT/UPDATE/DELETE queries. If you're just getting a single value back, use ExecuteScalar(). If you're getting a whole result set back, use ExecuteReader() or Fill() a DataSet object.
Also, there are some things that are idiomatic to C# that you should be doing:
public int GetMatchCode()
{
//this could be loaded from config file or other source
string connectString = "Server=myServer;Database=myDB;Uid=myUser;Pwd=myPass;";
string sql = "SELECT MAX(VAL(match_id)) FROM `data`";
using (var connect = new MySqlConnection(connectString))
using (var command = new MySqlCommand(sql, connect))
{
connect.Open();
var result = command.ExecuteScalar();
if (result == DBNull.Value)
{
//what you do here depends on your application
// if it's impossible for the query to return NULL, you can even skip this
}
return (int)result;
}
}
Some of the changes need explanation:
I don't ever call .Close(). The using block takes care of that for me, even if an exception was thrown. The old code would have left the connection hanging if an exception occured.
.Net developers tend to believe in very small methods. More than that, this method ought to be part of a class that has nothing but other simple public data access methods and maybe a few private helper methods or properties for abstracting common code in the class.
There is no exception handling code here. If you have small methods that are part of a generic database access class, exception handling should be at higher level, where you are better positioned to make decisions about how to proceed.
I am trying to retrieve list of records from one table , and write to another table. I've used a simple query to retrieve the values to SqlDataReader,then load them to a DataTable. Using the DataTableReader , I am going through the entire data set which is Saved in DataTable. The problem is, while reading each and every record I am trying to insert those values to another table using a Stored Procedure.But it only insert the first row of values,and for the second row onward giving some Exception saying."procedure or function has too many arguments specified".
string ConStr = ConfigurationManager.ConnectionStrings["ConString"].ConnectionString;
SqlConnection NewCon = new SqlConnection(ConStr);
NewCon.Open();
SqlCommand NewCmd3 = NewCon.CreateCommand();
NewCmd3.CommandType = CommandType.Text;
NewCmd3.CommandText ="select * from dbo.Request_List where group_no ='" +group_no+ "'";
NewCon.Close();
NewCon.Open();
SqlDataReader dr = (SqlDataReader)NewCmd3.ExecuteReader();
DataTable dt = new DataTable();
dt.Load(dr);
DataTableReader reader = new DataTableReader(dt);
NewCmd.Dispose();
NewCon.Close();
NewCon.Open();
SqlCommand NewCmdGrpReqSer = NewCon.CreateCommand();
NewCmdGrpReqSer.CommandType = CommandType.StoredProcedure;
NewCmdGrpReqSer.CommandText = "Voucher_Request_Connection";
if (reader.HasRows)
{
int request_no = 0;
while (reader.Read())
{
request_no = (int)reader["request_no"];
NewCmdGrpReqSer.Parameters.Add("#serial_no", serial_no);
NewCmdGrpReqSer.Parameters.Add("#request_no", request_no);
try
{
NewCmdGrpReqSer.ExecuteNonQuery();
MessageBox.Show("Connection Updated");//just to check the status.tempory
}
catch (Exception xcep)
{
MessageBox.Show(xcep.Message);
}
MessageBox.Show(request_no.ToString());//
}
NewCmdGrpReqSer.Dispose();
NewCon.Close();
}
Any Solutions ?
As #Sparky suggests, the problem is that you continue to add parameters to the insertion command. There are several other ways in which the code could be improved, however. These improvements would remove the need to clear the parameters and would help to make sure you don't leave disposable resources undisposed.
First - use the using statement for your disposable objects. This removes the need for the explicit Close (btw, only one of Close/Dispose is needed for the connection as I believe Dispose calls Close). Second, simply create a new command for each insertion. This will prevent complex logic around resetting the parameters and, possibly, handling error states for the command. Third, check the results of the insertion to make sure it succeeds. Fourth, explicitly catch a SqlException - you don't want to accidentally hide unexpected errors in your code. If it's necessary to make sure all exceptions don't bubble up, consider using multiple exception handlers and "doing the right thing" for each case - say logging with different error levels or categories, aborting the entire operation rather than just this insert, etc. Lastly, I would use better variable names. In particular, avoid appending numeric identifiers to generic variable names. This makes the code harder to understand, both for others and for yourself after you've let the code sit for awhile.
Here's my version. Note there are several other things that I might do such as make the string literals into appropriately named constants. Introduce a strongly-typed wrapper around the ConfigurationManager object to make testing easier. Remove the underscores from the variable names and use camelCase instead. Though those are more stylistic in nature, you might want to consider them as well.
var connectionString = ConfigurationManager.ConnectionStrings["ConString"].ConnectionString;
using (var newConnection = new SqlConnection(connectionString))
{
newConnection.Open();
using (var selectCommand = newConnection.CreateCommand())
{
selectCommand.CommandType = CommandType.Text;
select.CommandText ="select request_no from dbo.Request_List where group_no = #groupNumber";
selectCommand.Parameters.AddWithValue("groupNumber", group_no);
using (dataReader = (SqlDataReader)newCommand.ExecuteReader())
{
while (reader.HasRows && reader.Read())
{
using (var insertCommand = newConnection.CreateCommand())
{
insertCommand.CommandType = CommandType.StoredProcedure;
insertCommand.CommandText = "Voucher_Request_Connection";
var request_no = (int)reader["request_no"];
insertCommand.Parameters.Add("#serial_no", serial_no);
insertCommand.Parameters.Add("#request_no", request_no);
try
{
if (insertCommand.ExecuteNonQuery() == 1)
{
MessageBox.Show("Connection Updated");//just to check the status.tempory
}
else
{
MessageBox.Show("Connection was not updated " + request_no);
}
}
catch (SqlException xcep)
{
MessageBox.Show(xcep.Message);
}
MessageBox.Show(request_no.ToString());//
}
}
}
}
}
Try clearing your parameters each time...
while (reader.Read())
{
request_no = (int)reader["request_no"];
// Add this line
NewCmdGrpReqSer.Parameters.Clear();
NewCmdGrpReqSer.Parameters.Add("#serial_no", serial_no);
NewCmdGrpReqSer.Parameters.Add("#request_no", request_no);
try
{
I need syntax help with the following code logic:
I have a code block that gets email address from the database. The email addresses need to be assigned to a string variable strEmailAddress with a comma seperation
My code is:
SqlConnection conn = new SqlConnection(strConn);
string sqlEmailAddress = "usp_Get_Email_Address";
SqlCommand cmdEmailAddr = new SqlCommand(sqlEmailAddress, conn);
cmdEmailAddr.CommandType = CommandType.StoredProcedure;
con.Open();
SqlDataReader sqlDREmailAddr = cmdEmailAddr.ExecuteReader();
How can I loop through the records and store the results in strEmailAddress seperated by comma?
while (sqlDREmailAddr.Read())
{
//...process each row here
}
I would also wrap the reader in a using statement to make sure it is closed properly:
using (SqlDataReader sqlDREmailAddr = cmdEmailAddr.ExecuteReader())
{
}
Depending on what the columns in your dataset is named, reading values from each record will look something like this (update: now with all addresses merged):
var emailAddress = new StringBuilder();
var emailAddressOrdinal = sqlDREmailAddr.GetOrdinal("EmailAddress");
while (sqlDREmailAddr.Read())
{
if (emailAddress.Length > 0)
emailAddress.Append(',');
emailAddress.Append(sqlDREmailAddr.GetString(emailAddressOrdinal));
}
Use the SqlDataReader.Read method:
while (sqlDREmailAddr.Read())
{
...
// Assumes only one column is returned with the email address
strEmailAddress = sqlDREmailAddr.GetString(0);
}
while (sqlDREmailAddr.Read())
{
// handle row here
}
This is what you're looking for....
using (SqlConnection conn = new SqlConnection(strConn)){
string sqlEmailAddress = "usp_Get_Email_Address";
using (SqlCommand cmdEmailAddr = new SqlCommand(sqlEmailAddress, conn)){
cmdEmailAddr.CommandType = CommandType.StoredProcedure;
conn.Open(); // Typo Glitch!
using (SqlDataReader sqlDREmailAddr = cmdEmailAddr.ExecuteReader()){
while(sqlDREmailAddr.Read()){
if (!sqlDREmailAddr.IsDBNull(sqlDREmailAddr.GetOrdinal("emailAddr"))){
// HANDLE THE DB NULL...
}else{
strEmailAddress = sqlDREmailAddr.GetSqlString(sqlDREmailAddr.GetOrdinal("emailAddr"));
// Do something with strEmailAddr...
}
}
}
}
}
Notice:
A typo glitch on the conn variable...
A check is made to ensure that the Database value returned is not NULL
A call is made to GetOrdinal to return the column based on emailAddr string value that corresponds to the column from the query for SQL Select...which is an int type) as the parameter for GetSqlString..
Edit: Thanks to John Saunders for pointing out a blooper!
Edit#2: Thanks to Peter Lillevold for pointing out a mis-spelling...
Hope this helps,
Best regards,
Tom.