I have created the following sequence that is resulting in an error that states
System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'
as indicated in the comments.
SqlCommand cmd = new SqlCommand(#"SELECT GETDATE() 'Today', DATENAME(weekday, GETDATE()) 'Day Name';
select * from [Schedule] where Working = datename(weekday,GetDate()) and Commencing != Finishing;
select * from [Vacation] where VacStart = GetDate();", con);
con.Open();
SqlDataReader read = cmd.ExecuteReader();
DateTime now = DateTime.Today;
while (read.Read())
{
// from Schedule Table
string Working = read.GetString(1);
DateTime Commencing = read.GetDateTime(2); //Exception Error Here
DateTime Finishing = read.GetDateTime(3); //Exception Error Here
// from Vacation Table
string Reason = read.GetString(1);
if (Reason == null)
{
// if condition is met
lblMsg2.Text = "<p class='closed'>Sorry, we are closed for for a personal holiday.<br />If you are a current client and this is an emergency, we will of course make every effort to accommodate you.</p>";
}
else
{
// if condition is not met
if (Working != null && now >= Commencing && Finishing <= now)
{
// if condition met
lblMsg2.Text = "<p class='open'>Yes, we are currently open and would love to hear from you!</p>";
}
else
{
// if condition is not met
lblMsg2.Text = "<p class='closed'>Sorry, we are currently closed. You can reach us during normal business hours. We would love to have the opportunity to speak with you!</p>";
}
}
}
Based on my research through a lot of posts, I believe that the error is being caused by the differences in dates, i.e. 1900-01-01 versus 2018-07-18. The dates are not relevant since I am only trying to compare times.
I have tried several variations where you change the query to account for the dates but I was not able to get these to work.
It looks like everyone converts the data from DateTime to a string, which I am also having trouble getting to work.
Commencing and Finishing columns are both of type DateTime in SQL Server database.
The query in the SqlCommand contains 3 select statements, so it will return 3 rowsets. The problem you're having is that posted code is only processing the first rowset; but the comment indicates that you think it is processing the second and third. My solution is below; but here is a list of suggested improvements.
SqlCommand, SqlDataReader and SqlConnection are all IDisposable so should each be in a using block. (I accept I can't see the connection in the posted code, I'm just guessing about that.)
Based on the comments, it looks like you don't care about the first of the 3 queries, so don't ask SQL to do it.
Avoid doing select *. If you specifically write which columns you are expecting, then your code is protected against a change being made to the database, like changing the order of the columns, or adding new columns to the table. It will also help you check your code, since you will be able to check which column each ordinal refers to.
Given that you've done select *, I can't tell whether you know that the ordinal for the Get methods on DataReader start at zero, not 1, so I thought I should mention it - if that's wrong, you'll need to fix it.
If a column is returning null, then you can't Get it, you need to use the IsDbNull method for the same ordinal. You can encapsulate the two calls in a single extension method. as shown below this list.
I'd suggest renaming read (verb) as reader (noun).
To advance from one result set to the next, use NextResult()
Here's the extension method, you can do similar methods for other types:
public static class DataRecordExtensions
{
public static string GetNullableString(this IDataRecord dataRecord, int ordinal)
{
return dataRecord.IsDBNull(ordinal) ? null : dataRecord.GetString(ordinal);
}
}
NOTE: I can't tell from the snippet how many rows are returned by the queries, so I've just used while loops. I have shifted the logic to be closer to the relevant code; but it should behave the same. The logic may need fixing anyway, since I can't tell how many rows are returned.
using (SqlCommand cmd = new SqlCommand(#"select * from [Schedule] where Working = datename(weekday,GetDate()) and Commencing != Finishing;
select * from [Vacation] where VacStart = GetDate();", con))
{
con.Open();
using (SqlDataReader reader = cmd.ExecuteReader())
{
DateTime now = DateTime.Today;
// from Schedule Table
while (reader.Read())
{
string working = reader.GetString(1);
DateTime commencing = reader.GetDateTime(2);
DateTime finishing = reader.GetDateTime(3);
// if condition is not met
if (working != null && now >= commencing && finishing <= now)
{
// if condition met
lblMsg2.Text = "<p class='open'>Yes, we are currently open and would love to hear from you!</p>";
}
else
{
// if condition is not met
lblMsg2.Text = "<p class='closed'>Sorry, we are currently closed. You can reach us during normal business hours. We would love to have the opportunity to speak with you!</p>";
}
}
// from Vacation Table
if (reader.NextResult())
{
while (reader.Read())
{
string reason = reader.GetNullableString(1);
if (reason == null)
{
// if condition is met
lblMsg2.Text = "<p class='closed'>Sorry, we are closed for for a personal holiday.<br />If you are a current client and this is an emergency, we will of course make every effort to accommodate you.</p>";
}
}
}
}
Related
This question already has answers here:
What are good ways to prevent SQL injection? [duplicate]
(4 answers)
Closed 1 year ago.
I've been stuck on this issue for a few hours and I can't seem to get over what is causing this issue to populate. When running my application in debug mode, it will run the foreach two times successfully returning "Query Executed!".
However, when it goes for the third time, I get this error:
Incorrect syntax near ']'.Unclosed quotation mark after the character string '')'.
My method that will perform the insert to the SQL Server table Logs:
static String connectionString = "Data Source=.\\SQLExpress;Database=ElasticSearchService;Trusted_Connection=True;";
public static async Task<int> InsertLogData()
{
SqlConnection connection = null;
SqlCommand command = null;
int numrows = 0;
try
{
var response = await _elasticClient.SearchAsync<EsSource>(s => s
.Size(3000)
.Source(src => src.Includes(i => i
.Fields(f => f.timestamp,
fields => fields.messageTemplate,
fields => fields.message)))
.Index("customer-simulation-es-app-logs*")
.Query(q => +q
.DateRange(dr => dr
.Field("#timestamp")
.GreaterThanOrEquals("2021-06-07T17:13:54.414-05:00")
.LessThanOrEquals(DateTime.Now))));
// var json = _elasticClient.RequestResponseSerializer.SerializeToString(response);
connection = new SqlConnection(connectionString);
Console.WriteLine("\nOpening connection...");
connection.Open();
Console.WriteLine("\nConnection successful!");
foreach (var item in response.Hits)
{
var dateCreated = item.Source.timestamp;
var messageTemplate = item.Source.messageTemplate;
var message = item.Source.message;
command = new SqlCommand("insert into Logs (DateCreated, MessageTemplate, Message) values ('" + dateCreated + "', '" + messageTemplate + "', '" + message + "')", connection);
numrows = command.ExecuteNonQuery();
Console.WriteLine("\nQuery Executed!");
}
connection.Close();
Console.WriteLine("\nConnection Closed....");
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
finally
{
command.Dispose();
connection.Dispose();
}
return numrows;
}
Is there something that is causing this that I am not seeing? Is my command = new SqlCommand() incorrect that is causing it to come with that error of:
Incorrect syntax near ']'.Unclosed quotation mark after the character string '')'.
For starters:
command = new SqlCommand("insert into Logs (DateCreated, MessageTemplate, Message) values (#dt, #mt, #m)";
command.Parameters.AddWithValue("#dt", dateCreated);
command.Parameters.AddWithValue("#mt", messageTemplate);
command.Parameters.AddWithValue("#m", message);
Then execute it
After you get comfortable with that, take a read of this blog - AddWithValue is convenient but it can cause performance issues in some contexts. I don't care about it so much for something like an insert statement but I'd recommend you read the blog to see why you should move away from making a habit of it in SQL Server, once you're down with how parameterizing works. Also worth noting that not every database has issues with AWV, and to some great extent it is preferable to use it and suffer occasional performance issues than not use parameterizing at all and get hacked
SQL in the way you've got it (and the way I've got it) adding parameters etc is actually fairly tedious. Take a look at dapper, a library that makes it a lot easier to do this sort of stuff and more, such as running a sql query and turning the result setinto a list of c# objects, a bit like parsing Json
Other things to consider:
use of ExecuteNonQueryAsync
create your command outside of the foreach loop and add dummy parameters of the right types using AddWithValue (or take the fine opportunity to branch out into adding carefully constructed parameters of the right sql db type), then open the connection and run the loop, changing the parameter values on every pass of the loop. In summary: setup once, change the values and execute 1000 times, rather than setting up 1000 times
use using to declare your command, that way it gets disposed later
I have a C# program and I made a code for subtracting the amount of sold products from the amount of these products in the stock (access datatable) so I used this code :
foreach (DataGridViewRow r in dataGridView1.Rows)
{
OleDbCommand command = new OleDbCommand("Update BookInserting set Amount = Amount - '" + Convert.ToInt32(r.Cells[1].Value) + "' where BookName = " + r.Cells[0].Value.ToString() + "", connection);
connection.Open();
command.ExecuteNonQuery();
connection.Close();
}
but when I run it gives me this error :
(No value given for one or more required parameters) .
I tried solving it several times but failed, I hope you can help me solve it.
Thanks in advance.
Your problem is probably caused by Access unable to recognize some part of your query as an object of the underlying table (or the table itself).
This problem and a more serious one called Sql Injection could be avoided using parameters. (And a side benefit your code becomes a lot clearer without all those strings concatenations)
So let's try to change your code in this way:
// String sql with parameters placeholders
string cmdText = #"Update BookInserting
set Amount = Amount - #amt
where BookName = #book";
connection.Open();
// Just build the command just one time outside the loop and
// add the two parameters required (without a value and in the exact order
// expected by the placeholders
OleDbCommand command = new OleDbCommand(cmdText, connection);
command.Parameters.Add("#amt", OleDbType.Integer);
command.Parameters.Add("#book", OleDbType.VarWChar);
// Inside the loop just change the parameters values and execute
foreach (DataGridViewRow r in dataGridView1.Rows)
{
// If the cell with the parameter for the WHERE
// clause is invalid skip the update
if(!r.IsNewRow && r.Cells[0].Value != null
&& r.Cells[0].Value.ToString() != "")
{
cmd.Parameters["#amt"].Value = Convert.ToInt32(r.Cells[1].Value);
cmd.Parameters["#book"].Value = r.Cells[0].Value.ToString();
command.ExecuteNonQuery();
}
}
connection.Close();
Final note. A connection object should be created each time you require it. From your code it is not clear if this is the case. Use the following pattern. (Using Statement)
using(OleDbConnection connection = new OleDbConnection(....))
{
... code that uses the connection ....
} // <- here the connection is closed and disposed
In my app, I am trying to grab a date from the database and compare it against local time. Right now I am getting an error when I am converting to date incorrectly.
I have tried:
Convert.ToDateTime()
DateTime.ParseExact()
My code:
string time = "Select top(1) DATE from SERVICE order by DATE desc";
SqlCommand command = new SqlCommand(time, connection);
connection.Open();
using (SqlDataReader timereader = timecommand.ExecuteReader())
{
while (timereader.Read())
{
if (DateTime.ParseExact(time, "yyyy-MM-dd HH:mm:ss", null).AddMinutes(10) > currenttime)
{
// code
}
}
connection.Close();
}
I am hoping when I retrieve this value from the database, I can convert it into a proper datetime value and compare against local time and run other code after that.
At the moment I am just getting this error:
The string was not recognized as a valid DateTime. There is an unknown word starting at index 0.
I'm probably just dumb and missing something obvious..
Your query selects a single value. Use ExecuteScalar and cast it to a DateTime (it's already a DateTime, but boxed inside an object):
string time = "Select top(1) DATE from SERVICE order by DATE desc";
SqlCommand command = new SqlCommand(time, connection);
connection.Open();
DateTime d = (DateTime)command.ExecuteScalar();
connection.Close();
After you do this, and before you embark on some long mission of this protracted way of getting data out of a database, converting it to objects for use in your app etc, take a look at least at an ORM called Dapper, if not Entity Framework. Dapper's basically what you're doing now, but it auto converts your queries to objects and back and saves a lot of tedious code. With Dapper it'd look more like:
using (var connection = new SqlConnection("connstr"))
{
var d = connection.QuerySingle<DateTime>("SELECT TOP 1 Date FROM ...");
}
Yes, it's not much of a saving over what you have now, right? But what if you have a list of Order that themselves have 20 properties:
using (var connection = new SqlConnection("connstr"))
{
var orders = connection.Query<Order>("SELECT * FROM order WHERE customerID = #custid", new {custid = 123}).ToList();
}
Orders is now a list of Order objects for customer 123, parameterized, injection safe, quick and a one liner to read and populate your orders; doing that in a datareader is going to take at least 25 lines of boring code
http://dapper-tutorial.net and spend 2 minutes reading; I'll lay bets you'll be glad you did
Just try to read the value as a proper, native DateTime type like this (assuming that the DATE column in SQL Server is in fact a DATETIME or similar datatype - not a string - hopefully!):
using (SqlDataReader timereader = timecommand.ExecuteReader())
{
while (timereader.Read())
{
// just read the DateTime value as such
DateTime dt = timereader.GetDateTime(0);
// then do whatever you need to do with this DateTime value .....
}
connection.Close();
}
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'
I have a datatable which gives me the columnname,datatype of the column and the maximum character length. returntype should be a list and how can i link the column name and the datatype in just one list. Please Help.
try
{
List<string> tablelist = new List<string>();
DataTable dt;
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT column_name,data_type,character_maximum_length FROM "+ dbPropertiesobj.DBName +".information_schema.columns WHERE table_name = '"+ TableName+"' ", conn);
SqlDataAdapter sqlda = new SqlDataAdapter(cmd);
dt = new DataTable();
sqlda.Fill(dt);
for (int i = 0; i < dt.Rows.Count; i++)
{
string dtrow = dt.Rows[i].ItemArray[0].ToString(); //gets only the columnname
tablelist.Add(dtrow);
}
return tablelist; // need the datatype and maximum character length along with the name.
}
catch (Exception ex)
{
return null;
}
You've got a lot of stuff going on with this code. I rewrote it completely, this is not the shortest code or even the best way to do it, but it is written to be illustrative of areas to learn more or work on.
Study up on exception handling especially, when to use it and what to use it for. Never write try blocks that swallow errors. What were you planning on doing if this method returned null? Was the table name bad? Did the connection fail? A user may or may not could fix the error, but you swallowed it. Now no one knows what it was.
Even worse possibly no one knows an error even occurred here depending on what you did with the null. NEVER swallow errors like this. Applications should fail at the points where errors occur. Be careful about returning nulls like this as well. When you return nulls and then do something else the application may fail somewhere later instead and the original error is now much harder to find and fix. You may occasionally return nulls in this style when writing frameworks. For ordinary applications it is usually not needed and almost never a good idea.
Good quality production code typically contains very little exception handling because you should use conditionals to handle anything you can anticipate. Anything you cannot anticipate you usually also cannot handle. You may have a lot of try...finally blocks that exist to clean up resources, but an application should contain very few actual try..catch blacks. Normally you let errors propagate back up the call stack to a final handler that notifies a user before the app shuts down.
The following is still not the best code you could write. I kept it as close to your original as was reasonable and eliminated some shortcuts to make it clearer. Study the differences and go from there
public class SomeClass
{
//Use parameters rather than accessing module level properties
private IList<ColumnInformation> GetColumnInformationForTable(string dbName, string tableName)
{
// Favor object oriented styles and meaningful names. Your method does not return a list of tables
// it returns a list of column meta data
List<ColumnInformation> columnInformations = new List<ColumnInformation>();
// Avoid SQL conncatenation if at all possible. NEVER concatenate where parameters into SQL commands and NEVER EVER with single quotes.
// Here table name requires concatenation but the select parameter TableName does not.
string selectCmdString = "SELECT column_name,data_type,character_maximum_length FROM " + dbName + ".information_schema.columns WHERE table_name = #TableName";
// Use parameters. Get everything ready first, don't open connections prematurely and only wrap error prone code in try blocks.
SqlCommand cmd = new SqlCommand(selectCmdString, conn);
SqlParameter tableNameParameter = new SqlParameter("#TableName", tableName);
cmd.Parameters.Add(tableNameParameter);
// Use a DataReader since you cannot modify this data anyway.
// This also shows an appropriate use of a try block to ensure a connection gets closed,
// but better yet, open your reader with the CommandBehavior set to close
// and get rid of this try block altogether
try
{
//Reconsider use of a module or global level connection. May be better to create a new here.
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
//Favor OOP styles rather than indexes and arrays and repeated calls to determine things like Rows.Count in a loop
while(reader.Read())
{
// Favor explicit member access rather than index acess.
//YOUR HOMEWORK! Study DataReader access and rewrite the code below to handle possible nulls in length field. Use a method based on evaluating conditionals, DO NOT use a method based on a try block.
ColumnInformation columnInformation = new ColumnInformation(reader["column_name"].ToString(), reader["data_type"].ToString(), (int)reader["character_maximum_length"].ToString());
columnInformations.Add(columnInformation);
}
reader.Close();
}
finally
{
// The only reason to use the try is to make sure the connection gets closed here. A better approach
// is to use the CommandBehavior.CloseConnection option and get rid of the try finally block completely.
// But NEVER just wrap a bunch of code in try blocks arbitrarily, swallow any errors and return a null.
conn.Close();
}
return columnInformations;
}
}
public class ColumnInformation
{
private string _columnName;
private string _dataType;
private int _columnLength;
public string ColumnName
{
get { return _columnName; }
}
public string DataType
{
get { return _dataType; }
}
public int ColumnLength
{
get { return _columnLength; }
}
public ColumnInformation(string columnName, string dataType, int columnLength)
{
_columnName = columnName;
_dataType = dataType;
_columnLength = columnLength;
}
}
Why not a List of 3-Tuple objects (ie Tuple with 3 values)? Not sure the exact syntax for declaring the List but each entry would be something like:
Tuple.Create(column_name, datatype, max_length)