How do I create a lambda expression with unknown fields? - c#

I want to dynamically build the lambda expression so that I can build a query with an unknown number of fields...how do I accomplish this?
I am looping through an object that contains all of the fields and values adding to the Where clause for each field...
searcher = searcher.Where(f => f.fieldName.Contains(fieldValue));
i.e. pseudo-code:
foreach(var field in fields){
searcher = searcher.Where(f => field.name.Contains(field.value));
}
If I were living in the stone-age I'd pseudo-code it like this:
var first = true;
string query = " SELECT * FROM TABLE WHERE ";
foreach(var field in fields){
if(first){
query += field.name + " LIKE '%" + field.value + "%' ";
}else{
query += " AND " + field.name + " LIKE '%" + field.value + "%' ";
}
first = false;
}
Please tell me the stone-age isn't more powerful than current technology! ;-)

I hope this will be helpful.
searcher = fields.Aggregate(searcher, (current, field) => current.Where(f => f.Name.Contains(f.Value)));

Related

c# issues with filtering sql query from checkboxlist

Here's my problem. I have a textBox in which can be realised research with keyword. I have a checklistbox with different topic (ADV, Logistic, Finance, Administration) to filter the sql queries. If I search for a key word and I checked "logistic" the return result will only be related to "Logistic". This works well, the problem is that, if I check 2 checkbox, "logistic" and"finance" for example I will only have result related to "logistic" but I would like to have the 2 results.. I made it worked like 20 minutes ago and suddenly doesn't work anymore I don't undersand why. Can anyone tell me what am I missing ?
Here's my code :
string word = tbSearch.Text;
string strSql = #"SELECT CAST(ID as VarChar(50)) ID, Aggregation, DateDerniereSolution, DateDescription, DerniereSolution, DescriptionDemande, FileDeTraitement, NomContact, Numero, SousRubrique, TitreDemande
FROM cfao_DigiHelp_index.DigiHelpData WHERE ( 1 = 1 )";
string selectedValue = "";
bool IsFirst = false;
strSql += #" AND (";
foreach (ListItem item in CheckboxID.Items)
{
if (item.Selected)
{
selectedValue += item.Value ;
if (IsFirst)
{
strSql += " OR ";
}
strSql += " SousRubrique Like '%" + selectedValue + "%' ";
IsFirst = true;
}
if (CheckboxID.SelectedIndex == -1)
{
Label2.Visible = true;
Label2.Text = "Veuillez cocher au moins une rubrique";
}
}
strSql += #" )";
This line
selectedValue += item.Value ;
wreak havoc your query, because at every loop you keep concatenating to the selectedValue the value of the checked item. Just use the item.Value
strSql += " SousRubrique Like '%" + item.Value + "%' ";
Also, if your checked items match exactly with the SousRubrique contents you could avoid using the LIKE and the wild search pattern "%" but use just the equal operator
Keep in mind that you need to have a strict control on the contents of your checkedlistbox item because if your user is able to write the value for SousRubrique your code is exposed to a Sql Injection attacks.

dynamic type can't return from web api always different set of columns

My issue is this.
I have an ever changing set of columns due to pivoting on a list of items in a table. Basically each column that you see in the above picture is a line in the database table. I want to display a group of lines as one line on the front end. How do I return this dynamic type from a controller or api?
I have been attempting to use code such as this.
List<dynamic> items = repository.GetAll();
foreach(var item in items){
var properties = item.GetType().GetProperties(BindingFlags.Public);
foreach (var property in properties) {
var PropertyName = property.Name;
var PropertyValue = item.GetType().GetProperty(property.Name).GetValue(item, null);
model.Add(PropertyName, PropertyValue);
}
}
But GetProperties() returns nothing from the dynamic "item". I've also tried using item.GetType().GetFields() to no avail.
How can i go about getting the column name and value from a dynamic data type in c# when i don't know the name of that column? Or just returning the dynamic object from the api? Thanks.
Here's the repository code.
public IEnumerable<dynamic> GetAll()
{
var items = context.Database.SqlQuery<dynamic>(
"DECLARE #cols NVARCHAR(MAX), #sql NVARCHAR(MAX)" +
"SET #cols = STUFF((SELECT DISTINCT ',' + QUOTENAME(FormDetails.Name)" +
" from Value" +
" left join ValueDetails" +
" on Value.Id = ValueDetails.ValueId" +
" left" +
" join FormDetails" +
" on ValueDetails.FormDetailsId = FormDetails.Id" +
" ORDER BY 1" +
" FOR XML PATH(''), TYPE" +
" ).value('.', 'NVARCHAR(MAX)'), 1, 1, '')" +
" SET #sql = 'SELECT Id, FormId, CreatedDateTime, ' + #cols + '" +
" FROM" +
" (" +
" select Value.Id, Value.FormId, FormDetails.Name, ValueDetails.Value, Value.CreatedDateTime" +
" from Value" +
" left join ValueDetails" +
" on Value.Id = ValueDetails.ValueId" +
" left" +
" join FormDetails" +
" on ValueDetails.FormDetailsId = FormDetails.Id" +
" ) s" +
" PIVOT" +
"(" +
"MAX(Value) FOR Name IN(' + #cols + ')" +
") p order by CreatedDateTime desc '" +
" EXECUTE(#sql)").ToList();
return items;
}
Here's a bit more info. Basically my repository returns the data from the first image at the top of this page. But when i return that data to the front end this is what it looks like. The object is there but no properties...
The problem with your code is not in getting an empty list of properties, because they are genuinely not there. SqlQuery<T> will not populate a property if it's not there on type T; you are passing dynamic, which translates to System.Object.
One trick to solving this is to retrieve column names dynamically, and build an ExpandoObject out of it. The technique is described here.
static IList<dynamic> Read(DbDataReader reader) {
var res= new List<Dictionary<string,object>>();
foreach (var item in reader) {
IDictionary<string,object> expando = new ExpandoObject();
foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(item)) {
var obj = propertyDescriptor.GetValue(item);
expando.Add(propertyDescriptor.Name, obj);
}
res.Add(expando);
}
return res;
}
With this method in place, call
using (var cmd = ctx.Database.Connection.CreateCommand()) {
ctx.Database.Connection.Open();
cmd.CommandText = ...; // Your big SQL goes here
using (var reader = cmd.ExecuteReader()) {
return Read(reader).ToList();
}
}
Once your repository start returning ExpandoObjects, your code for retrieving properties can be replaced with code querying IDictionary<string,object>, an interface implemented by ExpandoObject:
IDictionary<string,object> properties = item as IDictionary<string,object>;
if (properties != null) {
foreach (var p in properties) {
model.Add(p.Key, p.Value);
}
}

Using an arbitrary number of parameters in ORMLite Query

I am in the process of fixing some of our bad sql queries that are vulnerable to sql injection. Most are straight queries with no inputs, but our search field takes search terms that are not parameterised. A snippet is below:
using (var db = ORMLite.Open())
{
StringBuilder sb = new StringBuilder();
sb.Append("select * from column1, column2");
if (terms.Count() > 0)
{
sb.Append("where (column1 like '%#term0%' or " + column2 + " like '%#term0%') ");
if (terms.Count() > 1)
{
for (int i = 1; i < terms.Count(); i++)
{
sb.Append("and (column1 like '%#term" + i + "%' or " + column2 + " like '%#term" + i + "%') ");
}
}
}
List<POCO> testQuery = db.Select<POCO>(sb.ToString());
}
The #term components are where I intend to use parameters (they used to be of the form '" + term[i] + '", but any term with malicious code would just be inserted. When I move to my select statement, I would like to add the parameters. This is normally done as so:
List testQuery = db.Select(sb.ToString(), new { term0 = "t", term1 = "te", term2 = "ter" });
However I can have any number of terms (term.count() is the number of terms). How can I pass in an anonymous object with any number of terms? Or is there a better method?
I'm looking for almost the same thing in Postgresql. Based on this SO question
the answer looks like "you have to perform multiple queries."
I can get the unique row IDs from my table given the partial parameterized
query, and then directly paste those unique IDs back into the query -- since those
row IDs will be safe.
Here's an example of what I mean, but the c# is probably wrong (sorry):
string query = "SELECT unique_id FROM table WHERE (column1 LIKE '%#term%' OR column2 LIKE '%#term%')";
string safeIDs;
List uniqueRowIDs = db.Select(query, new {term = term[0]});
for (int i = 1; i < terms.Count(); i++) {
// Loop to narrow down the rows by adding the additional conditions.
safeIDs = uniqueRowIDs.Aggregate( (current, next) => current + string.Format(", '{0}'", next) );
uniqueRowIDs = db.Select(
query + string.Format(" AND unique_id IN ({0})", safeIDs),
new {term = term[i]});
}
// And finally make the last query for the chosen rows:
safeIDs = uniqueRowIDs.Aggregate( (current, next) => current + string.Format(", '{0}'", next) );
List testQuery = db.Select(string.Format("SELECT * FROM table WHERE unique_id IN ({0});", safeIDs));
Another option for your case specifically could be to just get all of the values that
are like term0 using a parameterized query and then, within the c# program, compare
all of the results against the remaining terms the user entered.

How can i filter C# dataGridView across all field names?

I see in this video its quite easy to add a textbox and have it drive the filtering of a datagridView. The issue is in this video it seems you have to specific which column to filter based on.
RowFilter = "FirstName like "%' + searchText.Text + '%"
but what if I want it to check all of the fields and show the row if any column has my search string in it
You would want to loop through each column in your row and append an OR comparison
This is really stupid code, but hopefully gives you the gist of it. Something like:
StringBuilder filter = new StringBuilder();
foreach(var column in dataGridView.Columns)
{
if(filter.ToString() == "")
{
filter.Append(column.Name + " like '" + searchText.Text + "'");
}
else
{
filter.Append(" OR ");
filter.Append(column.Name + " like '" + searchText.Text + "'");
}
}
RowFilter = filter.ToString();
You can iterate all reasonable columns and get "like" filter like this:
var search = "search request";
var columnsList = dataGridView.Columns.Cast<DataGridViewColumn>()
.Where(x => x.Visible && x.ValueType == typeof(string))
.Select(x => x.DataPropertyName);
var filter = string.Join(" OR ", columnsList.Select(x => $"{x} like '%{search}%'"));

Search different words together

public string getSearchResult(string qry, string options, string noOfRecords, string strrefine)
{
string Query = "";
if (qry == "") return "";
string[] qrymod = qry.Split(' ');
if (qrymod.Length > 1)
{
for (int i=1; qrymod.Length > i; i++)
{
qry =qrymod[i];
}
}
//qry = qrymod[0];
qry = qry.Replace("\\", "\\\\").Replace("%", "\\%").Replace("_", "\\_");
if (options == "A" || options == "AC")
Query += " Select top " + noOfRecords + " cast(activityid as nvarchar(50)) as 'id',title as 'title',cast(description as varchar(200)) as 'desc' ,'AC' as 'Type' from searchActivity WHERE title like '%" + chkText(qry) + "%' or description like '%" + chkText(qry) + "%' escape '\\' ";
}
I can perform search from this function but the issue is whenever i am entering the text with two words
it searches the word after entering space the function will start searching the 2nd word
i want to compile the search result to search both the words
You want to do a full text query... Have a look here

Categories