I'm getting a strange error in Visual Studio, and of course this great software is unable to tell me where the error is, just that I'm getting an error. I guess the best I can do is paste my code.
using (SQLiteCommand cmd = new SQLiteCommand(query, con))
{
using (SQLiteDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
//Console.WriteLine("{0} ", rdr["logLnNum"]);
ulong start, end, delta = 0;
string contentStr;
string contentMarkup;
String group;
start = (ulong)rdr["startTime"];
end = (ulong)rdr["endTime"];
convertTimes(start, end, 2728232, delta);
contentStr = String.Format("{0}, {1}, {2}, {3}, {4} (ms)",
rdr["offsetOfData"], rdr["amountOfData"], rdr["filename"],
rdr["logLnNum"], (delta * .001));
contentMarkup = "<div title=\"" + contentStr + "\">" + contentStr + "</div>";
group = String.Format("{0:X}", rdr["threadId"]);
group = group + ", " + rdr["threadName"];
TimelineData inputData = new TimelineData(contentMarkup, end, group, start);
Console.WriteLine("Data processed");
dataSet.Add(inputData);
}
}
}
Again, the only error I get is "System.InvalidCastException" occurred in .exe.
Direct casting from an object only works when that object inherits from the type you're casting to (somewhere along the line, anyways).
A simple way to get the type you need out of a DataReader is to call
[type].Parse(reader[index].ToString())
where [type] is what you want to cast to, i.e.
ulong.Parse(rdr["startTime"].ToString())
The DataReader objects typically have a .GetInt32(int), .GetDecimal(int), etc. that simply requires you to pass in the index of the column to parse. If you only have the name of the column, you can use Reader.GetOrdinal("yourColumnName").
I would like to recommend you using an extra method to delimit this kind of errors.
For instance, consider this code:
protected T getDataValue<T>(object value)
{
if (value != null && DBNull.Value != value)
return (T)value;
else
return default(T);
}
Then call it inside your datareader iteration for each value retrieved, this will help you during debugging to detect which field is producing the exception.
Example:
start = getDataValue<ulong>(rdr["startTime"]);
end = getDataValue<ulong>(rdr["endTime"]);
In a nutshell, I usually follow these guidelines when working with data access to avoid exceptions:
If you have access to database, check your data using a db client before executing queries from ADO, this can be done executing the same query.
Consider using the same types in both layers (DB and App), in order to avoid cast exceptions --> it's not the same to convert from varchar to int, that making a simple cast from int to int (of course it comes as an object but for sure it has a type expected value), you reduce a lot of validation logic when using the same types.
If your table's fields accept nulls, consider using a strategy of conversion (like the method I gave you) to assign a valid value when null.
Try to avoid writing too much "logic" in DataAccess methods, just keep it simple, you can use a DTO or a business class in order to store all the values you've got from db, then use it/modify/create a logic in the business layer.
Hope it helps
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 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>";
}
}
}
}
I need to perform an update to a View that has multiple underlying tables using the ExecuteCommand method of a DataContext. I am using this method because of the known restriction of linqToSQL when performing this type of operation on Views having multiple underlying tables.
My existing SQL statement is similar to the following where I am setting newFieldID to a null value simply for this post to illustrate the issue. In the application, newFieldID is assigned a passed parameter and could actually be an integer value; but my question is specific to the case where the value being provided is a null type:
using (var _ctx = new MyDataContext())
{
int? newFieldID = null;
var updateCmd = "UPDATE [SomeTable] SET fieldID = " + newFieldID + "
WHERE keyID = " + someKeyID;
try
{
_ctx.ExecuteCommand(updateCmd);
_ctx.SubmitChanges();
}
catch (Exception exc)
{
// Handle the error...
}
}
This code will fail for the obvious reason that the updateCmd won't be completed in the case of a null value for the newFieldID. So how can I replace or translate the CLR null value with an SQL null to complete the statement?
I know I could move all of this to a Stored Procedure but I am looking for an answer to the existing scenario. I've tried experimenting with DBNull.Value but aside from the challenge of substituting it for the newFieldID to use in the statement, simply placing it into the string breaks the validity of the statement.
Also, enclosing it within a single quotes:
var updateCmd = "UPDATE [SomeTable] SET fieldID = '" + DBNull.Value + "'
WHERE keyID = " + someKeyID;
Will complete the statement but the value of the field is translated to an integer 0 instead of an SQL null.
So How does one go about converting a CLR null or nullable int to an SQL Null value given this situation?
Correct way to do it: use override of ExecuteCommand accepting not only command text, but also array of parameters and use parameterized query instead of command string concatenation:
var updateCmd = "UPDATE [SomeTable] SET fieldID = {0} WHERE keyID = {1}";
_ctx.ExecuteCommand(updateCmd, new [] {newFieldID, someKeyID});
It will not only prevent you from sql injection, but also it will do following for you (from MSDN description):
If any one of the parameters is null, it is converted to DBNull.Value.
Try checking newFieldID == null and change the statement accordingly.
Something like below or using separate if / else statement.
var updateCmd = "UPDATE [SomeTable] SET fieldID =" + (newFieldID == null ? "null" : Convert.ToString(newFieldID)) + " WHERE keyID = " + someKeyID;
Normally, when using Stored Procedures or Prepared Statements, you use Parameters to assign values. When you have a DbParameter, you can assign null or DBNull.Value to the Value-Property or your parameter.
If you want to have the null as text in the statement, simply use the SQL-keyword NULL
var updateCmd = "UPDATE [SomeTable] SET fieldID = NULL WHERE keyID = " + someKeyID;
As pointed out by Andy Korneyev and others, a parameterized array approach is the best and probably the more appropriate method when using Prepared statements. Since I am using LinqToSQL, the ExecuteCommand method with the second argument which takes an array of parameters would be advised but it has the following caveats to its usage.
A query parameter cannot be of type System.Nullable`1[System.Int32][] (The main issue I'm trying to resolve in this case)
All parameters must be of the same type
Shankar's answer works although it can quickly become very verbose as the number of parameters could potentially increase.
So my workaround for the problem involve somewhat of a hybrid between the use of parameters as recommended by Andy and Shankar's suggestion by creating a helper method to handle the null values which would take an SQL statement with parameter mappings and the actual parameters.
My helper method is:
private static string PrepareExecuteCommand (string sqlParameterizedCommand, object [] parameterArray)
{
int arrayIndex = 0;
foreach (var obj in parameterArray)
{
if (obj == null || Convert.IsDBNull(obj))
{
sqlParameterizedCommand = sqlParameterizedCommand.Replace("{" + arrayIndex + "}", "NULL");
}
else
sqlParameterizedCommand = sqlParameterizedCommand.Replace("{" + arrayIndex + "}", obj.ToString());
++arrayIndex;
}
return sqlParameterizedCommand;
}
So I can now execute my statement with parameters having potential null values like this:
int? newFieldID = null;
int someKeyID = 12;
var paramCmd = "UPDATE [SomeTable] SET fieldID = {0} WHERE keyID = {1}";
var newCmd = PrepareExecuteCommand(paramCmd, new object[] { newFieldID });
_ctx.ExecuteCommand(newCmd);
_ctx.SubmitChanges();
This alleviates the two previously referenced limitations of the ExecuteCommand with a parameter array. Null values get translated appropriately and the object array may varied .NET types.
I am marking Shankar's proposal as the answer since it sparked this idea but posting my solution because I think it adds a bit more flexibility.
Using C# on VS13 with a connected Access database and am receiving the error "No value given for one or more required parameters" when executing certain SQL.
Here is my code. Thanks for your time!
// ID accessors for an itemLine object
public void setID(string Value) { ID = Value; }
public string getID() { return ID; }
...
// Code snippet where error originates
foreach (CartItem itemLine in parBasket)
{
cmd.CommandText = "SELECT Instock FROM tblProducts WHERE ProductID = " + itemLine.getID() + "";
OleDbDataReader reader = cmd.ExecuteReader();
reader.Read();
int stock = Convert.ToInt32(reader["Instock"]);
stock = stock - itemLine.getQuanity();
reader.Close(); //Close the reader
cmd.CommandText = "UPDATE tblProducts SET InStock =" + stock + " WHERE ProductID = " + itemLine.getID() + "";
updated = updated + cmd.ExecuteNonQuery();
}
cn.Close();
return updated;
}
If CartItem.getID() returns an integer, not a string, then you need to remove the single quotes around it in the SELECT statement you are building.
Even better - read up on using SqlParameter and use this when building queries like this, as it helps avoid this sort of error, and also prevents SQL injection attacks, if any of the parameter data comes directly from user input.
To fix those errors yourself, you should:
Run with the debugger;
When the SQL command throws an exception the debugger should break (at least if it's unhandled. If you catch it, the debugger may still break but you have to tweak its config to do so);
Use a Watch or something to look at the CommandText of your SqlCommand (i.e. the SQL text that actually gets executed).
This should make pretty obvious what is wrong.
Now using my Crystal ball rather than a debugger, I think your problem is that getId() returns a string (per your comment on the question) and you end up with something like: WHERE ProductID = FortyTwo in both the first and second SQL queries.
The bad solution to this would be to enclose the string in quotes: WHERE ProductID = 'FortyTwo' but you should be careful that your ID doesn't contain a quote itself (which you should escape).
The good solution is to use a SQL parameter. Assuming SQL Server syntax: WHERE ProductID = #id and cmd.Parameters.AddWithValue("id", item.GetId()). (Note: you use the same command repeatedly, you should not add the parameter repeatedly. Rather, add it once and then change its value at each iteration.)
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)