My C# Console Applcation:
1:
string CE_ParentName_ = CEReader5[0].ToString(); // get string with lithuanian letters for example "KonsolÄ—s".
if I use Console.WriteLine() , I get correct output in console.
2:
readname.CommandText = "SELECT [ID] FROM [Net7].[dbo].[GroupFilter] WHERE
[GroupFilterName]='" + CE_ParentName_ + "'"; // I need to find records in my DB with name of that string (1 possible option)
3:
if (NameReader.Read()) { idd = NameReader[0].ToString(); } // if i get any results ar no.2 i need to read them
The point is that no.2 returns zero results if string contains lithuanian letters. If string is w/o lithuanian letters - everything works perfect. Tried everything, you are my last hope folks.
Are Lithuanian characters multibyte? (like Japanese for example)
Is the GroupFilterName NVARCHAR or VARCHAR? If it's NVARCHAR, perhaps you need to put N in front of the string.
Eg.
readname.CommandText = "SELECT [ID] FROM [Net7].[dbo].[GroupFilter] WHERE
[GroupFilterName]=N'" + CE_ParentName_ + "'";
And see asawyer's comment regarding avoiding injection attacks.
Related
I have a nice piece of C# code which allows me to import data into a table with less columns than in the SQL table (as the file format is consistently bad).
My problem comes when I have a blank entry in a column. The values statement does not pickup an empty column from the csv. And so I receive the error
You have more insert columns than values
Here is the query printed to a message box...
As you can see there is nothing for Crew members 4 to 11, below is the file...
Please see my code:
SqlConnection ADO_DB_Connection = new SqlConnection();
ADO_DB_Connection = (SqlConnection)
(Dts.Connections["ADO_DB_Connection"].AcquireConnection(Dts.Transaction) as SqlConnection);
// Inserting data of file into table
int counter = 0;
string line;
string ColumnList = "";
// MessageBox.Show(fileName);
System.IO.StreamReader SourceFile =
new System.IO.StreamReader(fileName);
while ((line = SourceFile.ReadLine()) != null)
{
if (counter == 0)
{
ColumnList = "[" + line.Replace(FileDelimiter, "],[") + "]";
}
else
{
string query = "Insert into " + TableName + " (" + ColumnList + ") ";
query += "VALUES('" + line.Replace(FileDelimiter, "','") + "')";
// MessageBox.Show(query.ToString());
SqlCommand myCommand1 = new SqlCommand(query, ADO_DB_Connection);
myCommand1.ExecuteNonQuery();
}
counter++;
}
If you could advise how to include those fields in the insert that would be great.
Here is the same file but opened with a text editor and not given in picture format...
Date,Flight_Number,Origin,Destination,STD_Local,STA_Local,STD_UTC,STA_UTC,BLOC,AC_Reg,AC_Type,AdultsPAX,ChildrenPAX,InfantsPAX,TotalPAX,AOC,Crew 1,Crew 2,Crew 3,Crew 4,Crew 5,Crew 6,Crew 7,Crew 8,Crew 9,Crew 10,Crew 11
05/11/2022,241,BOG,SCL,15:34,22:47,20:34,02:47,06:13,N726AV,"AIRBUS A-319 ",0,0,0,36,AV,100612,161910,323227
Not touching the potential for sql injection as I'm free handing this code. If this a system generated file (Mainframe extract, dump from Dynamics or LoB app) the probability for sql injection is awfully low.
// Char required
char FileDelimiterChar = FileDelimiter.ToChar()[0];
int columnCount = 0;
while ((line = SourceFile.ReadLine()) != null)
{
if (counter == 0)
{
ColumnList = "[" + line.Replace(FileDelimiterChar, "],[") + "]";
// How many columns in line 1. Assumes no embedded commas
// The following assumes FileDelimiter is of type char
// Add 1 as we will have one fewer delimiters than columns
columnCount = line.Count(x => x == FileDelimiterChar) +1;
}
else
{
string query = "Insert into " + TableName + " (" + ColumnList + ") ";
// HACK: this fails if there are embedded delimiters
int foundDelimiters = line.Count(x => x == FileDelimiter) +1;
// at this point, we know how many delimiters we have
// and how many we should have.
string csv = line.Replace(FileDelimiterChar, "','");
// Pad out the current line with empty strings aka ','
// Note: I may be off by one here
// Probably a classier linq way of doing this or string.Concat approach
for (int index = foundDelimiters; index <= columnCount; index++)
{
csv += "','";
}
query += "VALUES('" + csv + "')";
// MessageBox.Show(query.ToString());
SqlCommand myCommand1 = new SqlCommand(query, ADO_DB_Connection);
myCommand1.ExecuteNonQuery();
}
counter++;
}
Something like that should get you a solid shove in the right direction. The concept is that you need to inspect the first line and see how many columns you should have. Then for each line of data, how many columns do you actually have and then stub in the empty string.
If you change this up to use SqlCommand objects and parameters, the approximate logic is still the same. You'll add all the expected parameters by figuring out columns in the first line and then for each line you will add your values and if you have a short row, you just send the empty string (or dbnull or whatever your system expects).
The big take away IMO is that CSV parsing libraries exist for a reason and there are so many cases not addressed in the above psuedocode that you'll likely want to trash the current approach in favor of a standard parsing library and then while you're at it, address the potential security flaws.
I see your updated comment that you'll take the formatting concerns back to the source party. If they can't address them, I would envision your SSIS package being
Script Task -> Data Flow task.
Script Task is going to wrangle the unruly data into a strict CSV dialect that a Data Flow task can handle. Preprocessing the data into a new file instead of trying to modify the existing in place.
The Data Flow then becomes a chip shot of Flat File Source -> OLE DB Destination
Here's how you can process this file... I would still ask for Json or XML though.
You need two outputs set up. Flight Info (the 1st 16 columns) and Flight Crew (a business key [flight number and date maybe] and CrewID).
Seems to me the problem is how the crew is handled in the CSV.
So basic steps are Read the file, use regex to split it, write out first 16 col to output1 and the rest (with key) to flight crew. And skip the header row on your read.
var lines = System.File.IO.ReadAllLines("filepath");
for(int i =1; i<lines.length; i++)
{
var = new System.Text.RegularExpressions.Regex("new Regex("(?:^|,)(?=[^\"]|(\")?)\"?((?(1)(?:[^\"]|\"\")*|[^,\"]*))\"?(?=,|$)"); //Some code I stole to split quoted CSVs
var m = r.Matches(line[i]); //Gives you all matches in a MatchCollection
//first 16 columns are always correct
OutputBuffer0.AddRow();
OutputBuffer0.Date = m[0].Groups[2].Value;
OutputBuffer0.FlightNumber = m[1].Groups[2].Value;
[And so on until m[15]]
for(int j=16; j<m.Length; j++)
{
OutputBuffer1.AddRow(); //This is a new output that you need to set up
OutputBuffer1.FlightNumber = m[1].Groups[2].Value;
[Keep adding to make a business key here]
OutputBuffer1.CrewID = m[j].Groups[2].Value;
}
}
Be careful as I just typed all this out to give you a general plan without any testing. For example m[0] might actually be m[0].Value and all of the data types will be strings that will need to be converted.
To check out how regex processes your rows, please visit https://regex101.com/r/y8Ayag/1 for explanation. You can even paste in your row data.
UPDATE:
I just tested this and it works now. Needed to escape the regex function. And specify that you wanted the value of group 2. Also needed to hit IO in the File.ReadAllLines.
The solution that I implemented in the end avoided the script task completely. Also meaning no SQL Injection possibilities.
I've done a flat file import. Everything into one column then using split_string and a pivot in SQL then inserted into a staging table before tidy up and off into main.
Flat File Import to single column table -> SQL transform -> Load
This also allowed me to iterate through the files better using a foreach loop container.
ELT on this occasion.
Thanks for all the help and guidance.
I wanted to build code to iterate properties of any object and build dynamic where clause in LINQ based on fields that have a valid value. I have it working.
However, in the code below you will see commented lines that use string interpolation and they simply do not work. The comment explains, but examining the generated SQL it is because the WHERE clause is completely wrong. This only happens for decimal fields and the simply string is the one that works.
It is not the end of the world but I am going to lose sleep as to why LINQ generates this . . . any ideas or help? Thanks much!
// TODO: why does the string interpolation not work?
// it outputs BudgetApproved.Equals(115500.00) which for some reason when query is
// constructed becomes FROM [Tactic] AS [t] WHERE CAST(0 AS bit) = CAST(1 AS bit)
string whereClause = fieldName + " = " + fieldValue;
//string whereClause = string.Format("{0}.Equals({1})", fieldName, fieldValue);
//string whereClause = $"{fieldName}.Equals({fieldValue})";
query = query.Where(whereClause);
UPDATED
Looks like #NetMage suggestion works but it's odd because in immediate window two outputs seem to be the same.
?fieldName + ".Equals(" + fieldValue + ")";
"ExpenseCategoryItemId.Equals(1)"
?string.Format("{0}.Equals({1})", fieldName, fieldValue);
"ExpenseCategoryItemId.Equals(1)"
code for Inserting into database
query = "INSERT INTO Question(Image, AnswerA, AnswerB, AnswerC, AnswerD, CorrectAnswer)"
+ $"VALUES(\""{name}\",\""{answerList[0]}\",\"{answerList[1]}\",\""{answerList[2]}\",\"{answerList[3]}\",\"{name}\"};";
I am getting error in this line as "; expected":
+ $"VALUES(\""{name}\",\""{answerList[0]}\",\"{answerList[1]}\",\""{answerList[2]}\",\"{answerList[3]}\",\"{name}\");";
In three places, you have an unescaped second double quote, which ends the quoted string right there:
\""{name
and
\""{answerList[0]
and
\""{answerList[2]
Those break your C#, and if you escaped them, they'd break your SQL. So don't do that. Almost certainly, you should be using single quotes rather than double quotes as well (thanks Icarus):
query = "INSERT INTO Question(Image, AnswerA, AnswerB, AnswerC, AnswerD, CorrectAnswer)"
+ $"VALUES('{name}','{answerList[0]}','{answerList[1]}','{answerList[2]}','{answerList[3]}','{name}'};";
However, that's very bad coding style. It's vulnerable to SQL injection attacks, it'll crash if one of your answers happens to have an apostrophe in it, and putting quoted or even just matched quotes in a string is highly error-prone, as you've discovered.
So start over and rewrite the code using parameters, which resolve all of these issues cleanly and simply:
SqlCommand cmd = new SqlCommand();
// ...etc.
cmd.Parameters.Add("#name", SqlDbType.NVarChar);
cmd.Parameters.Add("#answerList0", SqlDbType.NVarChar);
// ...etc.
cmd.Parameters["#name"].Value = name;
cmd.Parameters["#answerList0"].Value = answerList[0];
// ...etc.
query = "INSERT INTO Question(Image, AnswerA, AnswerB, AnswerC, AnswerD, CorrectAnswer)"
+ "VALUES(#name,#answerList0,#answerList1,#answerList2,#answerList3,#name};";
try to build the string using like this:
$#"query = ""INSERT INTO Question(Image, AnswerA, AnswerB, AnswerC, AnswerD, CorrectAnswer)""
""VALUES(""{name}"",""{ answerList[0]}"",""{answerList[1]}"",\""{
answerList[2]}"",""{answerList[3]}"",""{name}""};"";";
never use a + when building strings, because it will evaluate both and then append them to a third instead of creating just 1 string.
I am trying to insert New Line after word car but it is not working with folowing solution
Char(13) - not working
Environment.NewLine - when i use this it works but appends '(' this char in sql rows like 'Car ( Rate:2CR'
\n\r - not working
Code:
cmd.Parameters.AddWithValue("#ColumnCar", Car + "char(13)" + "Rate:2CR";
//cmd.Parameters.AddWithValue("#ColumnCar", Car + "\n\r" + "Rate:2CR";
//cmd.Parameters.AddWithValue("#ColumnCar", Car + Environment.NewLine + "Rate:2CR";
cmd.ExecuteNonQuery();
Need output in sql table ColumnCar row value as follows:
Car
Rate:2cr
Note : here after Car there will be a newline and then Rate:2Cr will be added
With the LoC Car + "char(13)" + "Rate:2CR"; you will get a literal string "char(13)" between your 2 values, not a new line. If you want only a new line you can append "\n" or you can append the character equivalent (char)10 of new line.
Now what character or string actually represents a new line might depend on your environment including the collation you are using. In simple ascii/ansi this will work. It might not be the same for another collation. As #mhasan pointed out it could also be different depending on the O/S.
Using characters
const char carriageReturn = (char) 13; // see https://en.wikipedia.org/wiki/Carriage_return
const char newLine = (char) 10;
var car = "some car";
var toInsert = car + newLine + "Rate:2CR";
cmd.Parameters.AddWithValue("#ColumnCar", toInsert);
This would also work and produce the same result:
var toInsert = car + "\n" + "Rate:2CR";
Use combination of newline and carriage return characters i.e. char(13) + char(10) for inserting new line in windows OS system.
For MAC its \r char(13) , for Linux its \n i.e. char(10) but for windows its combination of both.
Try this code hope its working...
Make a string variable and store all value in variable..
ex: string abc=textbox1.text+" "+"Rate:2cr";
#ColumnCar=abc.tostring();
now put your code
cmd.Parameters.AddWithValue("#ColumnCar",datatype);
cmd.executenonquery();
The following code works fine with unicode fields in a MS SQL-Server 2016 DB :
string carString = $"Volvo{Environment.NewLine}Rate: 2CR";
SqlParameter parameter = new SqlParameter("#ColumnCar", carString);
command.Parameters.Add(parameter);
The '(' when you use Environment.NewLine must be another error somewhere else. What is Car in your code? A class instance? What does its ToString() expand to?
Don't use string1 + " " + string2 concatenation.
Use string.Format(), $"" - inline syntax (like above) or StringBuilder to build your strings.
I use my double in a select statement:
code:
SqlCommand command = new SqlCommand("SELECT min(Score) FROM "+ table +" WHERE [" + sportEvent + "] < (#result);", connect);
command.Parameters.AddWithValue("#result", result);
everything works fine if double result is an integer but not if result is a comma number (example 11,34) --> it should be 11.34 to work (point instead of comma)
How can I change a double 11,34 into 11.34 ?
It appears that your code sets a string parameter as a constraint for a DB value of numeric type, letting the database do the conversion. This is not a good idea, because it takes control away from your program: should DBA decide to reconfigure your backend database to "understand" commas instead of dots, your program will stop working!
Currently, your double is in a locale-specific format. You need to parse it using the locale-specific format provider, and then set the value that you get back from the parser as the parameter of your SQL query. Assuming that the current culture is one that is using commas as decimal separator, you can do this:
command.Parameters.AddWithValue(
"#result"
, double.Parse(s, CultureInfo.CurrentCulture)
);
You can use this
result.ToString(CultureInfo.InvariantCulture)
You could try changing the variable into string:
result.ToString().Replace(',','.');
This will replace the comma with a dot.
If result is Double then:
command.Parameters.Add("#result", SqlDbType.Float).Value = result