How can I secure SQL parameters with entity framework SqlQuery? - c#

I have this method that should take an unknown amount of id's.
I got this method almost done but it isnt secure yet for obvious reasons, i know i could write my own method to strip the parameters but i would be more comfortable by using some build in method for this.
Here is the method
public static List<LocationModel> FetchCitiesByAreas(IEnumerable<string> areas)
{
using (var db = new BoligEnt())
{
var sqlQuery = new StringBuilder();
var first = true;
sqlQuery.Append("SELECT DISTINCT a.city AS City, a.zip AS Zip ");
sqlQuery.Append("FROM zip_city AS a ");
sqlQuery.Append("WHERE country = 1 ");
foreach (var d in areas)
{
if (first)
{
sqlQuery.Append("AND a.area_id = '" + d + "'");
first = false;
}
else
{
sqlQuery.Append("OR a.area_id = '" + d + "'");
}
}
return db.Database.SqlQuery<LocationModel>(sqlQuery.ToString()).ToList();
}
}
i know it have this function built in but as i stated earlier i dont know the exact amount of ids that will come in
db.Database.SqlQuery<LocationModel>("SELECT * FROM table WHERE id = #p0 ;", id).ToList();
Thanks

While I completely agree with paqogomez, in that you should just use LINQ to do the query, the .SqlQuery has the ability to take a parameter array. You could change your statement to look like this:
var sqlQuery = new StringBuilder();
sqlQuery.Append("SELECT DISTINCT a.city AS City, a.zip AS Zip ");
sqlQuery.Append("FROM zip_city AS a ");
sqlQuery.Append("WHERE country = 1 ");
for (int i = 0; i < areas.Count; i++)
{
if (i == 0)
{
sqlQuery.Append("AND (a.area_id = #p" + i.ToString());
}
else
{
sqlQuery.Append(" OR a.area_id = #p" + i.ToString());
}
}
sqlQuery.Append(")");
var results = db.Database.SqlQuery<LocationModel>(sqlQuery.ToString(), areas.ToArray()).ToList();
I added the missing parenthesis needed to your query to correctly filter out the OR results as well. I've also taken the assumption that areas is something like a List, or at least something you can easily get the count from.

Why dont you just use Linq?
var locations = (from zip in db.zip_city
where areas.Contains(zip.area_id) && zip.Country == 1
select new LocationModel{
City = zip.City,
Zip = zip.Zip
})
.Distinct()
.ToList();
If you still want to parameterize your query, you need to use EntityCommand
Also note that your query will fail because you havent put parenthesis around your OR statements.
I suggest structuring your sql like this:
string sqlQuery =
#"SELECT DISTINCT a.city AS City, a.zip AS Zip
FROM zip_city AS a
WHERE country = 1 AND (1=0 "
for (int i = 0; i < areas.Count; i++)
{
sqlQuery.Append("OR a.area_id = #d" + i.ToString() + " ");
}
sqlQuery.Append(")");

Related

SQL combine rows to be able to filter

I want to combine some rows, so I can filter the customers properly.
SELECT
StammIndex
, sPropSet
, bProp01
FROM [Properties] as properties
RIGHT JOIN [Kunden] as kunden
ON Kunden.StammIndex = properties.KundenIndex
"StammIndex" is the customers ID,
"sPropSet" is the property and
"bProp01" tells you if the property is checked (1) or not checked (0)
The problem is, that customers that have checked the property at least once are saved in the Database.
That means, that the customer IDs will be saved multiple times.
Now I want to get all customers, that didn't check the "Serienbrief" property.
SELECT
StammIndex
, sPropSet
, bProp01
FROM [Properties] as properties
RIGHT JOIN [Kunden] as kunden
ON Kunden.StammIndex = properties.KundenIndex
WHERE NOT (sPropSet = 'Serienbrief' AND bProp01 = 1) OR sPropSet IS NULL
Here you can see that customer '10001' is still there, because he got two properties. I want customer '10001' removed too, because he also got the property "Serienbrief" checked.
First post on stackoverflow, so sorry if I didn't explain my problem properly.
If you got any further questions, please feel free to ask.
EDIT*
My Filter is in C#. The Code behind looks like this
internal class FilterEigenschaften
{
public static List<UndBlock> FilterEigenschaftenListe = new List<UndBlock>();
public List<AdressenListe> Filter(Connection conn)
{
Criteria undBlockCriteria = conn.CreateCriteria();
foreach (UndBlock undBlock in FilterEigenschaften.FilterEigenschaftenListe)
{
Criteria oderBlockCriteria = conn.CreateCriteria();
foreach (OderBlock oderBlock in undBlock.OderBlock)
{
Criteria filterBlockCriteria = conn.CreateCriteria();
foreach (FilterBlock filterBlock in oderBlock.FilterBlock.Where(y => !string.IsNullOrEmpty(y.Wert)))
{
filterBlockCriteria.AddAnd("sPropSet", Operator.Equal, filterBlock.Kriterium);
filterBlockCriteria.AddAnd("bProp" + this.FiterSqlEigenschaftenFelder(conn, filterBlock), Operator.Equal, 1);
if (filterBlock.Operand == "Ist markiert")
{
}
else
{
}
}
oderBlockCriteria.AddOr(filterBlockCriteria);
}
undBlockCriteria.AddAnd(oderBlockCriteria);
}
string cmdText = $"SELECT Distinct(Kunde.StammIndex) " +
$"FROM {conn.Tablenames[4]} AS Kunde " +
$"RIGHT JOIN {conn.Tablenames[12]} AS Properties " +
$"ON Kunde.StammIndex = Properties.KundenIndex " +
$"WHERE {undBlockCriteria}";
return WordSqlAdressen.GetAdressenListe(conn, cmdText);
}
private string FiterSqlEigenschaftenFelder(Connection conn, FilterBlock filterBlock)
{
for (int i = 1; i < 31; i++)
{
using (Command cmd = conn.CreateCommand($"SELECT sProp{i:00} " +
$"FROM {conn.Tablenames[13]} " +
$"WHERE sPropSet = '{filterBlock.Kriterium}'"))
{
using (DataReader dr = cmd.ExecuteReaderEx())
{
while (dr.Read())
{
if (dr.GetString((0)) == filterBlock.Wert)
{
return i.ToString("00");
}
}
}
}
}
return null;
}
}
I can only use one SQL statement to see, if the property is checked or not checked
You just need to move your condition to your join rather than the where clause.
I.e.
SELECT
*
FROM Kuden k
LEFT JOIN Properties p
ON k.StammIndex = p.KudenIndex
AND p.sPropSet = 'Serienbrief'
AND p.bProp01 = 1
WHERE p.KudenIndex IS NULL;

How do I sanitize a dynamic table name using Dapper?

I'm new to Dapper, and writing a query that will pull from a provided schema and table, along with using dynamic ordering and filtering.
Dapper make dynamic parameters very simple, however, I'm not sure how to do this with tables in the order by and where clauses. Here's my method below, and I see the issues with SQL injection:
public GridData GetGridData(string schema, string table, TableDataParameters tableDataParameters)
{
using (var dbConnection = VarConnection)
{
dbConnection.Open();
if (!this.TableExists(dbConnection, schema, table))
{
throw new ItemNotFoundException($"Could not locate table {schema}.{table}.");
}
string orderyByClause = string.Join(",", tableDataParameters.SortModel.Select(s => $"[{s.ColId}] {(s.Sort.ToLower() == "asc" ? "asc" : "desc")}"));
var parameters = new DynamicParameters();
string whereClause;
if (tableDataParameters.FilterModel == null || !tableDataParameters.FilterModel.Any())
{
whereClause = "1=1";
}
else
{
whereClause = string.Join(" AND ", tableDataParameters.FilterModel.Select((fm, i) =>
{
string whereParam = $"whereParam{i}";
parameters.Add(whereParam, fm.Filter);
if (fm.Operation == "startsWith")
{
return $"[{fm.Column}] LIKE #{whereParam} + '%'";
}
throw new InvalidOperationException($"Unsupported filter operation '{fm.Operation}'");
}));
}
var query = $"SELECT COUNT(1) [total] " +
$"FROM [{schema}].[{table}] " +
$"WHERE {whereClause} " +
$"SELECT * " +
$"FROM [{schema}].[{table}] " +
$"WHERE {whereClause} " +
$"ORDER BY {orderyByClause} " +
$"OFFSET {tableDataParameters.StartIndex.Value} ROWS " +
$"FETCH NEXT {tableDataParameters.StopIndex.Value - tableDataParameters.StartIndex.Value} ROWS ONLY";
int total = 0;
using (var reader = dbConnection.ExecuteReader(query, parameters))
{
// First batch, it's the count
if (reader.Read())
{
total = reader.GetInt32(0);
}
var gridColumns = new List<GridColumn>();
var gridRows = new List<string[]>();
if (reader.NextResult() && reader.Read())
{
for (int i = 0; i < reader.FieldCount; i++)
{
string key = reader.GetName(i);
gridColumns.Add(new GridColumn(key, key, null, ""));
}
var items = new object[reader.FieldCount];
reader.GetValues(items);
gridRows.Add(items.Select(i => i.ToString()).ToArray());
}
while (reader.Read())
{
var items = new object[reader.FieldCount];
reader.GetValues(items);
gridRows.Add(items.Select(i => i.ToString()).ToArray());
}
return new GridData(tableDataParameters.StartIndex.Value, tableDataParameters.StopIndex.Value, total, gridRows.Count(), gridColumns.ToArray(), gridRows.ToArray());
}
}
}
Should I use something like DbCommandBuilder.QuoteIdentifier, https://msdn.microsoft.com/en-us/library/system.data.common.dbcommandbuilder.quoteidentifier(v=vs.110).aspx
in this case? That doesn't seem like it would help so much here.
Thanks!
Dynamic parameters is an oxymoron! Dapper makes parameters easy, but you can't paramaterize table and column names. This is a restriction of SQL, not dapper. If you really want to do this, you have to use dynamic sql and string methods, and you're on your own as regards SQL injection.
You will be happier and live longer if you don't do this. It's just a bad road. You're not adding much value, and you're potentially introducing a load of problems and limitations.
It looks like you're writing an app to browse a database. Good tools already exist for this!

C# and SQL Server multiple values

I'm wondering if its possible to make this easier.
So, I have an arraylist of strings called names and an arraylist of strings called last_name.
When I'm constructing select statement, I want to combine every entry in names with everything in last_name and search in database like:
SELECT * FROM DB WHERE (names='NAMES[0]' AND last_name='LAST_NAME[0]')
or (names='NAMES[0]' AND last_name='LAST_NAME[1]')
or (names='NAMES[1]' AND last_name='LAST_NAME[0]')
or (names='NAMES[1]' AND last_name='LAST_NAME[1]')
This is an example, In my project I have 6 Lists, and I need every combination, and the easiest way was to make for in for in for in for...
Thanks a lot
This should work by using a query like this :
SELECT *
FROM DB
WHERE Name IN (#name1,#name2,#name3) AND LastName IN (#lastName1, #lastName2, #lastName3)
You can build this query in a for-loop, like this :
var names = new[] {"John", "Peter"};
var lastnames = new[] { "Doe", "Waylander" };
var nameParams = "";
var lastNameParams = "";
var cnt = 0;
foreach (var name in names)
{
var nameString = "#name" + cnt;
if (cnt!=0)
{
nameParams += ",";
}
nameParams += nameString;
cmd.Parameters.Add(nameString, name);
cnt++;
}
cnt = 0;
foreach (var lastName in lastnames)
{
var lastNameString = "#lastName" + cnt;
if (cnt != 0)
{
lastNameParams += ",";
}
lastNameParams += lastNameString;
cmd.Parameters.Add(lastNameString, lastName);
cnt++;
}
cmd.CommandText = #"SELECT *
FROM DB
WHERE NAME IN (" + nameParams + #")
AND LastName IN (" + lastNameParams + ")";
var result = cmd.ExecuteReader();
The only limitation is the number of parameters (IIRC it is about 1000). Another good alternative would be a stored procedure, like already mentioned in the comments.

How to prevent duplicates in Search Results (ASP.NET Razor Syntax)

I'm creating a search algorithm based on keywords and sometimes phrases. I want to output results based on each word of every phrase in the search bar.
Below is my code which works fine:
var searchWords = Request["searchTerm"].Split(' ');
IEnumerable<dynamic> stories = Enumerable.Empty<string>();
var sqlSelect = "SELECT TOP 10 Feeds.* FROM Feeds WHERE Feeds.AdminPublish=#1 AND Title LIKE #0 ORDER BY UploadDate DESC";
foreach(var word in searchWords)
{
stories = stories.Concat(db.Query(sqlSelect, "%" + word + "%",).ToList());
}
In my View -Page (Result Page)
#foreach (var d in stories){
//Show Title and description
#d.Title
#d.Username
}
Problem
Let's say am searching for "xmas of Annoying", and after searching the db, it splits the phrase into three and returns results based on LIKENESS/SIMILARITIES of the three found words. I get aresults like
The Sociology of Annoying Xmas Songs
Welcome to the future
The Sociology of Annoying Xmas Songs
Welcome to the future
The Sociology of Annoying Xmas Songs
Welcome to the future
How do I condition my result to not show show results that have similar ID's or how do I remove excess duplicates
Yo can use Distinct Keyword To Select Distinct from Table
var sqlSelect = #"SELECT distinct TOP 10 Feeds.*
FROM Feeds WHERE Feeds.AdminPublish=#1 AND
Title LIKE #0 ORDER BY UploadDate DESC";
EDIT:
then You can do like this
foreach(var word in searchWords)
{
stories = stories.Concat(db.Query(sqlSelect, "%" + word + "%",).ToList());
}
stories= stories.Distinct<dynamic>();
Also you can use the Distinct extension method in lambda expression to achive the same in your View (Razor)
#foreach (var d in stories.Distinct()){
//Show Title and description
#d.Title
#d.Username
}
You can also provide a specific property on the basis of which the list can be made distinct, like:
stories.Distinct(x=>x.Title)
//Or
stories.Distinct(x=>x.UserName)
I would say to not add duplicate values to stories in the first place...
Something similar to this:
foreach(var word in searchWords)
{
lst = db.Query(sqlSelect, "%" + word + "%",).ToList()
foreach(var v in lst)
{
if (!stories.Contains(v))
stories.Add(v)
}
}
Mike from #Mikesdotnetting.com came up with a dynamic solution, most credit goes to him, and some to me for spotting out ways to tweek the structure, for future reference; heres the solution
var searchWords = Request["searchTerm"].Split(' ').Select(s => "%" + s + "%");
var sql = "SELECT TOP 10 Feeds.* FROM Feeds WHERE";
for(var i = 0; i < searchWords.Count(); i++)
{
sql += i == 0 ? "" : " OR ";
sql += " Title LIKE #" + i;
}
sql += " ORDER BY UploadDate DESC";
var stories = db.Query(sql, searchWords.array());
IF you want a result that might look something like this:
SearchTerm = "one two three"
SQL = ... where title like '%one%' or title like '%two%' or title like '%three%' or fname like '%one%' or fname like '%two%' or fname like '%three%' WHERE A FINAL CONDITION BINDS THEM ALL etc
If so, try this:
var fields = new List<string>{"Title", "SubTitle", "Fname", "UserName", "Link"};
var searchWords = Request["searchTerm"].Split(' ').Select(s => "%" + s + "%").ToList();
var sql = "SELECT TOP 10 Feeds.* FROM Feeds WHERE (";
System.Text.StringBuilder sb = new StringBuilder();
var i = 0;
foreach(var word in searchWords)
{
foreach(var field in fields)
{
sb.AppendFormat(" OR {0} LIKE #{1}", field, i);
}
i++;
}
sql = sql + sb.ToString().Trim().TrimStart("OR".ToCharArray()) + string.Format(") AND Feeds.AdminPublish=#{0} ORDER BY UploadDate DESC", i);
searchWords.Add(your_parameter_value_for_AdminPublish);
var stories = db.Query(sql, searchWords.ToArray());

WebMatrix internal search engine

I've wrriten a code which searchs the database but i don't know why when i search for some specific keywords it'll show other links which are unrelated. here's the code and the result.
Page.Title = "Catalog Search";
var db = Database.Open("Shopping");
var searchWords = Request["searchTerm"].Split(' ');
IEnumerable<dynamic> result = Enumerable.Empty<string>();
var sqlSelect = "SELECT ProductId, ProductTitle FROM Products WHERE " +
"ProductTitle LIKE #0";
foreach(var word in searchWords)
{
result = result.Concat(db.Query(sqlSelect, "%" + word + "%").ToList());
}
so i searched for "Samsung LCD" and here's the result.
Samsung - 15" Series 9 Ultrabook Laptop
Samsung - Galaxy Tab 2 7.0
Samsung - 32" Class - LCD
Samsung - 32" Class - LCD
i've seen a php code which is exactly what i want but unfortunately i don't know how to convert it. here's the php code.
$searchTerms = explode(' ', $bucketsearch);
$searchTermBits = array();
foreach ($searchTerms as $term) {
$term = trim($term);
if (!empty($term)) {
$searchTermBits[] = "bucketname LIKE '%$term%'";
}
}
...
$result = mysql_query("SELECT * FROM buckets WHERE ".implode(' AND ', $searchTermBits).");
and the result of the php search code.
SELECT * FROM buckets WHERE bucketname LIKE '%apple%' AND bucketname LIKE '%and%' AND bucketname LIKE '%pear%'
I took the liberty of adding a few null checks and such, but the code for C# would pretty much look like this:
// make sure search terms are passed in, and remove blank entries
var searchTerms = Request["searchTerms"] == null ?
new string[] {} :
Request["searchTerms"].Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
// build the list of query items using parameterization
var searchTermBits = new List<string>();
for (var i=0; i<searchTerms.Length; i++) {
searchTermBits.Add("bucketname LIKE #" + i);
}
// create your sql command using a join over the array
var query = "SELECT * FROM buckets";
if (searchTerms.Length > 0) {
query += " WHERE " + string.Join(" AND ", searchTermBits);
}
// ask the database using a lambda to add the %
var db = Database.Open("StarterSite");
var results = db.Query(query, searchTerms.Select(x => "%" + x + "%").ToArray());
// enjoy!
Response.Write(results.Count());
Let me know if you run into any more trouble!

Categories

Resources