I know we should never do this:
string select = "SELECT * FROM table1 ";
string where = "WHERE Name = '" + name + "' ";
string sql = select + where;
//execute the sql via ADO.NET
because of sql injection, because name can contain the char ', because of another 100 reasons. But now I have to do something similiar. I have a Dictionary<string, object> whose data look like:
Key(string) Value(object)
"Name" "Bob" //string
"ID" 10092L //long
"Birthday" 1980-05-07 00:00:00 //DateTime
"Salary" 5000.5m //decimal
//some others, whose key is a string, and value is string/long/int/DateTime/decimal
I want an easy way, to get all items in the dictionary collected in a String, just like a where statement:
Name = 'Bob' and ID = 10092 and Birthday = '1980-05-07 00:00:00' and Salary = 5000.5
String and DateTime are quoted with ', but note that the Name can be O'Neal. Is there any easy implementation? Input the dictionary, and return the string as a result.
EDIT Note that what I want is the string, I'm not going to execute it, parameterized command doesn't help. I just want a string that looks like a perfect safe WHERE statement.
The first code is only a problem if name is something entered by the user. Otherwise, it should be fine.
I don't know that it eliminates all problems but you might try experimenting with something like name = name.Replace("'", "''"). By converting all single quotes to double single quotes, you prevent the type of problems you described. Another approach might be to remove any single quotes.
However, the best route is to use query arguments. ADO supports these nicely and that would also eliminate any possibility of injection attacks.
The easy way could be like this:
string results = string.Join(" and ", myDict.Select( x=> x.Key + " = " + x.Value));
This of course wouldn't solve the quotes ' issue depending on different datatypes so you cannot use this as input to a SQL query - for that I would strongly recommend named parameters anyway - but is otherwise correct depending on the ToString() implementation of the values in your dictionary.
I wrote this many years ago, and always use it, and never ever have to think about this again. it is a waste of brain cells to solve this more than once:
// replace things like:
// O'Keefe with
// 'O''Keefe'
// make sure you don't call this twice!
static public string SqlString(string strInputSQL)
{
string strOut;
strOut = strInputSQL;
strOut = strOut.Replace ("'", "''");
strOut = "'" + strOut + "'";
return strOut;
}
Use it like this:
string sql = "SELECT * FROM FOO WHERE Name LIKE " + SqlString(myvalue);
There may be a dozen other ways to do it, but if you can have one and only one function, and use it consistently, you will save alot of time.
Try this link : Creating safe SQL statements as Strings
Some people consider this over-engineered, or just labourious to type. I fall back on a simple argument though...
Someone has already invested time and effort ensuring arguements can be safely and reliably included in SQL statements. Are you 100% certain you have pre-empted every possible scenario? Or is it more likely tried and tested code is more reliable?
But, then, I'm a bit anal ;)
var sb = new StringBuilder();
var isFirst = true;
foreach (var element in dic)
{
if(!isFirst)
sb.Append(" AND ");
else
isFirst = false;
sb.Append(element.Key);
sb.Append(" = ");
if(element.Value is decimal)
sb.Append(CastToSqlDecimalString((decimal)element.Value));
else
sb.Append("'" + String.Format(CultureInfo.InvariantCulture, "{0:G}", element.Value).Replace("'", "''") + "'");
}
You might want to handle decimals using this function
public static string CastToSqlDecimalString(decimal dec)
{
var sqlDecimal = new System.Data.SqlTypes.SqlDecimal(dec);
return string.Format("CAST({0} AS DECIMAL({1}, {2}))",
string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0:G}", dec),
sqlDecimal.Precision,
sqlDecimal.Scale);
}
Related
I have a bunch of string which i loop through . I want to insert an apostrophe when ever an apostrophe in any string that has apostrophe. I simply do something like below.
string strStatus = "l'oreal";
index = strStatus.IndexOf("'");
strStatus.Insert(index, " ' ");
I want to have output like l''oreal. Yet this fails. I tried using escape patter
strStatus.Insert(index, " \' ");
All to no avail. Please how do i achieve this? Any suggestion/help is highly appreciated.
Strings are immutable. Insert returns a new string with the 2 apostrophes, it doesn't modify strStatus in any way. Your code simply discards the result of Insert.
You should try:
string strStatus = "l'oreal";
index = strStatus.IndexOf("'");
string newStatus=strStatus.Insert(index, "'");
Strings are immutable in .NET (and Java), which means Insert does not modify strStatus, instead it will return a new instance which has the modification you're after.
Do this:
String status = "L'Oreal";
status = status.Insert( status.IndexOf('\''), "'" );
Strings are immutable in C#, so all it's methods do not modify the string itself - they return modified copy. This should work:
strStatus = strStatus.Insert(index, " ' ");
This might be a problem with Session and not ToString(), I'm not sure.
I have two .aspx pages and I want to pass an IP address from a datatable from one page to the other. When I do this, spaces get added that I don't want. The simple version of the code is this:
first .aspx page
int num = DropDownList1.SelectedIndex;
DataView tempDV = SqlDataSource2.Select(DataSourceSelectArguments.Empty) as DataView;
Session["camera"] = tempDV.Table.Rows[num].ItemArray[2];
Response.Redirect("test.aspx");
test.aspx page
string ipCamAdd = Session["camera"].ToString();
TextBox1.Text = "http://" + ipCamAdd + "/jpg/image.jpg?resolution=320x240";
what I want to print is
http ://ipadd/jpg/image.jpg?resolution=320x240
but what prints out is
http//ipaddress /jpg/image.jpg?resolution=320x240
how can I fix this?
Also, I asked this question hoping someone could tell me why this is happening as well. Sorry for the mistake.
Try this:
string ipCamAdd = Session["camera"].Trim().ToString();
For the valid concern, Session["camera"] could be null, add function such as the following to your code
static string ToSafeString(string theVal)
{
string theAns;
theAns = (theVal==null ? "" : theVal);
return theAns;
}
Then use:
string ipCamAdd = Session["camera"].ToSafeString().Trim();
You can use string.Replace if you just want to get rid of the spaces:
TextBox1.Text = "http://" + (ipCamAdd ?? "").Replace(" ", "") + "/jpg/image.jpg?resolution=320x240";
Trim the result before setting to session.
Session["camera"] = tempDV.Table.Rows[num].ItemArray[2].Trim();
Seems In SQL your data type is char(*) if you convert the data type to varchar and re enter data, you wont get any additional spaces
I have a comma separated string stored in database.
E.g.: record1 = "1,3,5,7,9,10" and record2 = "4,5,10"
And I have a given information, E.g.: 1.
I have to select the record using LINQ that contains the given info of 1.
The result returned should be record1.
If I were to use .contains() solely, it's not accurate as record2 will be returned as well.
How can I achieve that? Is it possible to achieve that in a single LINQ query?
Thanks for advise !
With a single LINQ-to-objects query:
string[] records = new[] { record1, record2 };
string record = records.FirstOrDefault(r => r.Split(',').Any(s => s == "1"));
Demo
First of all I would like to mention what #Tim Schmelter said -
Have you noticed already that the real problem is your creepy
datamodel? Use a table with real records instead of a column with a
comma separated string.
It is not a good practice to use a datamodel where you need string split match. Because it leads to inefficient systems and not to mention slow queries. But yet, if you really need a solution why not try this -.
There are four occasions where you will get a match,
A prefix match - starting with
Inner Match - contains with
Suffix Match - ends with
The only match - only one item and this is it
considering the scenario I am suggesting the solution below -
s is the value looking for say "1"
string prefixMatch = s + ",";
string suffixMatch = "," + s;
string innerMatch = "," + s + ",";
string record = <dbRecords>.FirstOrDefault(r=> r.StartsWith(prefixMatch) ||
r.Contains(innerMatch) || r.EndsWith(suffixMatch) ||
(!r.Contains(",") && r == s));
The reason for such a detailed query is to keep your memory utilisation less and letting the SQL query do the hard work of finding the results because this query will support LINQ-to-SQL conversion.
If i understand you correctly, you need as result record that contains "1". So you can use:
private bool GerRecord(string record)
{
string[] arr=record.Split(',');
return arr.Contains("1");
}
Instead of searching 1 you can try 1,(1 and comma combined) for searching in Contains in linQ
I am working on a C# project and I am facing an issue. The program allows the user to connect to a MySQL database and retrieve information from each selected table and write the data out to a file. The problem is because I have no idea what the schema is going to be like or what values its going to contain.
If the timestamp column contains the date 0000-00-00 00:00:00 I get the conversion error and no matter what I try it never works. I've tried converting to a string I've tried converting to a DateTime but I always get the error.
Below is how I am currently trying to get the data:
using (ConnectMySQLDB db = new ConnectMySQLDB(databaseSettings))
{
string query = string.Format("SELECT * FROM {0}.{1}", database, table);
Console.WriteLine("Query: {0}", query);
using (MySqlCommand cmd = new MySqlCommand(query, db.conn))
{
using (MySqlDataReader reader = cmd.ExecuteReader())
{
int i = 1;
while (reader.Read())
{
Console.WriteLine("ID: {0}", i);
fieldsAndValues = new Dictionary<string, string>();
foreach (ColumnDataTypes fieldAndType in fieldsAndTypes)
{
Console.WriteLine("Column: {0} Type: {1}", fieldAndType.field, fieldAndType.dataType);
string formattedValue = "";
if (fieldAndType.dataType == "timestamp")
{
DateTime date = DateTime.Parse(reader.GetDateTime(fieldAndType.field).ToString());
formattedValue = date.ToString("yyyyMMdd");
}
else
{
formattedValue = getDBFormattedValue(reader.GetString(fieldAndType.field), fieldAndType.dataType);
fieldsAndValues.Add(fieldAndType.field, formattedValue);
}
}
rows.Add(fieldsAndValues);
i++;
}
}
}
}
I also have added the allow zero date and convertzerodate to null option in the connector string as follows:
connString = "server=" + server + ";uid=" + username + ";pwd=" + password + ";port=" + port + ";Allow Zero Datetime=true;zeroDateTimeBehavior=convertToNull;Convert Zero Datetime=true";
Looking at this documentation, it looks like you're specifying two contradictory options (AllowZeroDateTime=true and ConvertZeroDateTime=true) and one which appears not to be listed (ZeroDateTimeBehavior=ConvertToNull).
I suggest that unless you have actual data which is DateTime.MinValue which you don't want to mix up with the "zero" value, you just specify ConvertZeroDateTime=true and detect if the result is DateTime.MinValue. You definitely shouldn't call reader.GetDateTime(), then convert the result to a string, and then back to a DateTime - you should avoid string conversions as far as you can, as they can mess things up for you pretty easily.
It's not really clear what string value you want for these "zero" values, but you should be able to special-case them with DateTime.MinValue fairly easily. Personally I'd actually try to keep the data in its "native" form as much as possible rather than converting everything to strings, but that's a different battle.
Sometime I want to join two strings with a space in between. But if second string is null, I don't want the space.
Consider following code:
void AssertFoo(bool cond, string message = null) {
...
Assert.Fail("Something is foo.{0}", message != null ? " " + message : "");
...
}
Is there a more elegant way to do that?
Here is one option that I like. It's better if you already have an IEnumerable<string> with your data, but it's easy enough even if you don't. It also clearly scales well to n strings being joined, not just 1 or two.
string[] myStrings = new string[]{"Hello", "World", null};
string result = string.Join(" ", myStrings.Where(str => !string.IsNullOrEmpty(str)));
Here is another option. It's a bit shorter for this one case, but it's uglier, harder to read, and not as extensible, so I would probably avoid it personally:
//note space added before {0}
Assert.Fail("Something is foo. {0}", message ?? "\b");
In this case we add the space to the format string itself, but if message is null we instead use the backspace character to remove the space that we know is before it in the message.
For newer versions of C# you can use the following extension method:
public static string Prepend(this string value, string prepend) => prepend + value;
It can be used like this:
Assert.Fail("Something is foo.{0}", message?.Prepend(" "));
Added in 2020:
Today I use this:
public static string Surround(this object value, string prepend, string append = null) => prepend + value + append;
Try this:
string joinedString = string.IsNullOrEmpty(message2) ? message1 : message1 + " " + message2;
Assert.Fail("Something is foo.{0}", (" " + message).TrimEnd());
Sure, this will result in a few string object creations, but it's unlikely such micro-optimization issues would matter in the vast majority of programs. It might be considered an advantage of this method that it handles not just null message, but a message of all whitespace as well.
Assert.Fail("Something is foo.{0}", message?.PadLeft(message.Lenght + 1, ' '));
Since C#6 you can use string interpolation like this:
$"Something is foo. {mssg}".TrimEnd();
See it in .NET Fiddle
The most elegant way is to use the inbuilt keyword of String class.
String.IsNullOrEmpty
This way you wont have a problem.