I am developing a C# .Net MVC application and trying to implement a generic search method for entity fields. As our pages are growing i don't want to code a search method each time a new page is added.
For that, i am using Dynamic.Core LINQ Queries : Check it at : https://dynamic-linq.net/basic-simple-query
the way i implemented it works the following way : at user input in the view, the app sends an ajax request to the controller telling it to search that specific value and then display the new list where the previous one.
The problem is : i could make it case insensitive but not accent insensitive and was wondering if anyone could help me with that.
Here is my code :
public static List<T> SearchEntityList<T>(this IQueryable<T> entityList, string searchBy, List<string> fieldsToCheck)
{
if (searchBy == null)
return entityList.ToList();
searchBy = searchBy.ToLower().RemoveDiacriticsUtil();
// Dynamic LINQ Library
string query = "";
foreach (string str in fieldsToCheck)
{
query += str + ".ToString().ToLower().Contains(#0) ||";
}
if (query != null)
{
// Removes the last "OR" inserted on the foreach here on top
query = query.Substring(0,query.Length - 3);
try
{
entityList = entityList.Where(query, searchBy);
}
catch (Exception e)
{
// query is wrong, list wont be filtered.
return entityList.ToList();
}
}
List<T> filteredList = entityList.ToList(); ;
return filteredList;
}
the method receives a list of string representing the fields to check, for example : "Username"
then a string query is built and checked with the database.
This code works as expected and is case insensitive, now i want to add accent insensitive to it.
i modify this line
query += str + ".ToString().ToLower().Contains(#0) ||";
with this one
query += str + "Collate(" + str + ".toString(), \"SQL_Latin1_General_CP1_CI_AI\").Contains(#0) ||";
and now i cannot make it work.
Got this error :
"No applicable method 'Collate' exists in type '...'"
I tested a lot of other stuff such as RemoveDiacritics, etc.. but they dont work with dynamic string linq queries...
Was wondering if anyone already had the same problem. Thanks !
You're on the right track.
You need to register your custom extension methods for dynamic linq before us use it:
// Registration for the default custom type handler
[DynamicLinqType]
public static class MyExtensions
{
// taken from: https://stackoverflow.com/questions/359827/ignoring-accented-letters-in-string-comparison/368850
public static string RemoveDiacritics(this string text)
{
return string.Concat(
text.Normalize(NormalizationForm.FormD)
.Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) !=
UnicodeCategory.NonSpacingMark)
).Normalize(NormalizationForm.FormC);
}
}
public static class Entry
{
public static void Main(string[] args)
{
// your input
var query = "éè";
// clean it using your extension method
var cleanQuery = query.RemoveDiacritics();
// a test set for this demo
IQueryable<string> testSet = new EnumerableQuery<string>(new List<string>()
{
"soméè", "tèèst", "séét", "BBeeBB", "NoMatchHere"
});
var results = testSet.Where("it.RemoveDiacritics().Contains(#0)", cleanQuery);
Debug.Assert(results.Count() == 4);
}
}
Related
I've been building an email generator function. It started as a regex function, but quickly moved over to reflection to make it as generic as possible. The idea is to have the email generator function pull in information from a messagedata class. It started as a simple task, as the function only had to change out a few static items, but as the function got more complex and the templates being sent out needed tables of information, then the function I had built was not enough.
I've extended the function to use a foreach loop to run through the template and replace text based on a list from the messageData class. I've been trying to get the list created in messageData to implement it into the emailGenerator function.
I've got:
string value = match.Groups["value"].Value;
// Code removed to shorten length
var items = (IEnumerable) value;
But it's not gathering the information from the messageData class. I'm thinking that maybe I need to get the value into a list?
Here is the EmailGenerator function:
public class EmailGenerator : IEmailGenerator
{
private string mergeTemplate(string template, object obj)
{
var operationMatches = operationParser.Matches(template).Cast<Match>().Reverse().ToList();
foreach (var match in operationMatches)
{
string operation = match.Groups["operation"].Value;
string value = match.Groups["value"].Value;
var propertyInfo = obj.GetType().GetProperty(value);
object dataValue = propertyInfo.GetValue(obj, null);
if (operation == "endforeach")
{
string foreachToken = "$foreach " + value + "$";
var startIndex = template.LastIndexOf(foreachToken, match.Index);
var templateBlock = template.Substring(startIndex + foreachToken.Length, match.Index - startIndex - foreachToken.Length);
var items = (IEnumerable) value;
string blockResult = "";
foreach (object item in items)
{
blockResult += this.mergeTemplate(templateBlock, item);
}
template = template.Remove(startIndex, match.Index - startIndex).Insert(startIndex, blockResult);
}
}
}
And here is the messageData class. It gets the information from a DTO.
** EDIT: Removed unnecessary code.
public class messageData : IMailObject
{
public List<messageItemData> Items
{
get
{
var items = new List<messageItemData>();
foreach (var docDTO in this.recipientDTO.InfoList)
{
items.Add(new messageItemData(docDTO));
}
}
}
}
public class messageItemData
{
// Properties
}
What I'm trying to accomplish is that the emailGenerator function is made generic enough to be reusable for other email templates later down the road, gathering the replacement information from the messageData class and the list it contains.
So, I finally found the answer. The code was 99% working. It was as simple as changing var items = (IEnumerable) value; for var items = (IEnumerable) dataValue;
I am trying to enumerate the registry to fetch a list of installed applications and return them via Linq to a DataTable in C#.
I have tried various things including sanitizing it as XML (which worked), however it seemed rather inefficient as ultimately, i require this as DataTable object.
Here is where i am at currently:
//Warning
public static DataRow DataRowInstalledApplication (this RegistryKey rgkey, string keyName)
{
RegistryKey key = rgkey.OpenSubKey(keyName, false);
try
{
//Application Name is mandetory for a given key.
if (key == null|| key.RegToString("DisplayName", false) == null )return null;
//Build a sanitised data row
var rowBuilder = new DataTable().NewRow();
rowBuilder["DisplayName"] = key.RegToString("DisplayName");
rowBuilder["UninstallString"] = key.RegToString("UninstallString");
rowBuilder["InstallLocation"] = key.RegToString("InstallLocation");
rowBuilder["Publisher"] = key.RegToString("Publisher");
rowBuilder["DisplayIcon"] = key.RegToString("DisplayIcon");
return rowBuilder;
}
finally
{
if (key != null) key.Close();
}
}
Here is the method that contains the Linq:
public DataTable GetRegistryApplicationDataTable(RegistryKey registryKey, string tableName)
{
if (registryKey != null)
{
try
{
//change to throw non critical error
var installedListXml = new DataTable(tableName, from name in registryKey.GetSubKeyNames()
let app = registryKey.DataRowInstalledApplication(name)
select app);
return installedListXml;
}
catch
{
return new DataTable(tableName);
}
finally
{
registryKey.Close();
}
}
return null;
}
The main problem i have is that i do not really understand Linq very well. Mainly how individual values are used during the iteration to call other things similar to
foreach (string value in collection)
{
Somefunction(value);
}
, in the case of the registry values i am trying to retrieve, i do not understand how to make a Linq query pass each key name to function, which will generate a row and the Linq Query return as a data table.
I would be grateful of any pointers! thanks
ps i call the above with
private static readonly RegistryKey HKLMUninstallKey = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall");
GetRegistryApplicationDataTable(HKLMUninstallKey, "Computer")
and expect back a data table called computer.
Your first problem is your linq statement. You are trying to pass an IEnumerable into a constructor that is expecting either a (string) or a (string, string). If you modify your logic to this:
var query = from name in registryKey.GetSubKeyNames()
let app = registryKey.DataRowInstalledApplication(name)
select app;
foreach(var result in query)
{
var installedListXml = new DataTable(tableName, result);
}
That would work IF result was actually a string, but it's a DataRow, which is not a valid parameter for a DataTable constructor. Instead, there is an extension method you can call on the resulting query to copy to a data table, like so:
var query = from name in registryKey.GetSubKeyNames()
let app = registryKey.DataRowInstalledApplication(name)
select app;
var installedListXml = query.CopyToDataTable();
When you write a LINQ query statement, it is not executed until you try to enumerate over the results. CopyToDataTable does this, so if you were to step through your code you will notice that your function DataRowInstalledApplication will not be called until you call CopyToDataTable and not when you first assign query. The result you receive is an Enumerator that you can treat just like any other, whether that's using in a foreach loop or calling ToList or so forth. Inside the linq query itself, you are actually iterating over other values, in this case GetSubKeyNames. It would be functionally equivalent if you were to do this instead:
var dataRows = new List<DataRow>();
foreach(var name in registryKey.GetSubKeyNames())
{
dataRows.Add(registryKey.DataRowInstalledApplication(name));
}
return dataRows.CopyToDataTable();
I have the following JSON which has to be converted to URL parameters for a GET request.
An example is given here, however due to the complexity of this object, there can be multiple line_items_attributes each with the given values as shown, I'm having difficulties passing on the correct one.
I've also tried to just serialize the JSON object and pass on that value but that did not solve the issue either.
{
"purchase_invoice":
{
"date":"14/04/2015",
"due_date":"14/04/2015",
"contact_id":500,
"contact_name":"TestContact",
"reference":"TestReference",
"line_items_attributes":[
{
"unit_price":10.00,
"quantity":1,
"description":"TestLineItemAttDesc",
"tax_code_id":1,
"ledger_account_id":501,
"tax_rate_percentage":19.0,
"tax_amount":1.60
}]
}
}
I've been searching for a while now but without much luck. Any insights are appreciated and most welcome!
This is calling an API which does not support the incoming data in JSON format, so doing this server-side or changing the web service to support data in JSON format is not possible.
x-www-form-urlencoded content is, essentially, a flat sequence of key/value tuples, and as explained in this answer to How do I use FormUrlEncodedContent for complex data types? by Tomalak, there is no canonical way to transform a hierarchical, nested key/value structure into a flat one.
Nevertheless, from the accepted answer to this question, this example from the Stripe API, and the question mentioned above, it seems that it is common to flatten parameters inside complex nested objects by surrounding their keys in brackets and appending them to the topmost key like so:
{
{ "purchase_invoice[date]", "14/04/2015" }
{ "purchase_invoice[due_date]", "14/04/2015" }
{ "purchase_invoice[contact_id]", "500" }
{ "purchase_invoice[contact_name]", "TestContact" }
{ "purchase_invoice[reference]", "TestReference" }
{ "purchase_invoice[line_items_attributes][0][unit_price]", "10" }
{ "purchase_invoice[line_items_attributes][0][quantity]", "1" }
{ "purchase_invoice[line_items_attributes][0][description]", "TestLineItemAttDesc" }
{ "purchase_invoice[line_items_attributes][0][tax_code_id]", "1" }
{ "purchase_invoice[line_items_attributes][0][ledger_account_id]", "501" }
{ "purchase_invoice[line_items_attributes][0][tax_rate_percentage]", "19" }
{ "purchase_invoice[line_items_attributes][0][tax_amount]", "1.6" }
}
If this is what you want, you can generate such key/value pairs with json.net using the following extension methods:
public static partial class JsonExtensions
{
public static string ToUrlEncodedQueryString(this JContainer container)
{
return container.ToQueryStringKeyValuePairs().ToUrlEncodedQueryString();
}
public static IEnumerable<KeyValuePair<string, string>> ToQueryStringKeyValuePairs(this JContainer container)
{
return container.Descendants()
.OfType<JValue>()
.Select(v => new KeyValuePair<string, string>(v.ToQueryStringParameterName(), (string)v));
}
public static string ToUrlEncodedQueryString(this IEnumerable<KeyValuePair<string, string>> pairs)
{
return string.Join("&", pairs.Select(p => HttpUtility.UrlEncode(p.Key) + "=" + HttpUtility.UrlEncode(p.Value)));
//The following works but it seems heavy to construct and await a task just to built a string:
//return new System.Net.Http.FormUrlEncodedContent(pairs).ReadAsStringAsync().Result;
//The following works and eliminates allocation of one intermediate string per pair, but requires more code:
//return pairs.Aggregate(new StringBuilder(), (sb, p) => (sb.Length > 0 ? sb.Append("&") : sb).Append(HttpUtility.UrlEncode(p.Key)).Append("=").Append(HttpUtility.UrlEncode(p.Value))).ToString();
//Answers from https://stackoverflow.com/questions/3865975/namevaluecollection-to-url-query that use HttpUtility.ParseQueryString() are wrong because that class doesn't correctly escape the keys names.
}
public static string ToQueryStringParameterName(this JToken token)
{
// Loosely modeled on JToken.Path
// https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Linq/JToken.cs#L184
// By https://github.com/JamesNK
if (token == null || token.Parent == null)
return string.Empty;
var positions = new List<string>();
for (JToken previous = null, current = token; current != null; previous = current, current = current.Parent)
{
switch (current)
{
case JProperty property:
positions.Add(property.Name);
break;
case JArray array:
case JConstructor constructor:
if (previous != null)
positions.Add(((IList<JToken>)current).IndexOf(previous).ToString(CultureInfo.InvariantCulture)); // Don't localize the indices!
break;
}
}
var sb = new StringBuilder();
for (var i = positions.Count - 1; i >= 0; i--)
{
var name = positions[i];
// TODO: decide what should happen if the name contains the characters `[` or `]`.
if (sb.Length == 0)
sb.Append(name);
else
sb.Append('[').Append(name).Append(']');
}
return sb.ToString();
}
}
Then if you have a JSON string, you can parse it into a LINQ-to-JSON JObject and generate the query string like so:
var obj = JObject.Parse(jsonString);
var queryString = obj.ToUrlEncodedQueryString();
Alternatively, if you have some hierarchical data model POCO, you can generate your JObject from the model using JObject.FromObject():
var obj = JObject.FromObject(myModel);
var queryString = obj.ToUrlEncodedQueryString();
Demo fiddle here.
So the final URL would be easy to compute using any URL Encoding mechanism. In C#, we could do the following:
string json = "...";
string baseUrl = "http://bla.com/somepage?myJson="
string urlWithJson = baseUrl + System.Net.WebUtility.UrlEncode(json)
Is there any way you can POST the data or otherwise send a request body instead? It would seem slightly easier/cleaner.
Sounds like you need something which is x-www-form-urlencoded.
From your example, it would look like this:
purchase_invoice%5Bdate%5D=14%2F04%2F2015&purchase_invoice%5Bdue_date%5D=14%2F04%2F2015&purchase_invoice%5Bcontact_id%5D=500&purchase_invoice%5Bcontact_name%5D=TestContact&purchase_invoice%5Breference%5D=TestReference&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Bunit_price%5D=10&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Bquantity%5D=1&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Bdescription%5D=TestLineItemAttDesc&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Btax_code_id%5D=1&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Bledger_account_id%5D=501&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Btax_rate_percentage%5D=19&purchase_invoice%5Bline_items_attributes%5D%5B0%5D%5Btax_amount%5D=1.6
The best reference for this encoding that I'm aware of is the undocumented jQuery.param method on the jQuery JavaScript library.
I want the query to give me back a NotificationConfiguration and also an IEnumerable<string> (which I will later transform to a single string using SB). Once the query gives me back those two items I will transform it using a constructor in my view model so I can properly display all the data using a DataTable. The answer I am looking for maybe very specific but I need to understand why I'm getting the subquery error when I want it to return an IEnumerable<string> and how to fix it. Also please note... according to the .net docs the code should be handing my back an IEnumerable<string> but for some reason its still crashing. Here is the relevant code samples again:
[HttpPost]
public ActionResult Index(DataTableRequest requestedData)
{
using (this.dataStore.Session.BeginTransaction())
{
return this.dataStore.Query<NotificationConfiguration>()
.TableRange(requestedData, p => new NotificationConfigurationViewModel(p, p.Events.Select(x => x.Code) ?? null));
}
}
.
public NotificationConfigurationViewModel(NotificationConfiguration notification , IEnumerable<string> events)
{
Contract.Requires(notification != null);
this.notification = notification;
this.events = events;
}
.
[Display(Name = "Events")]
public virtual string EventTypeCodes
{
get
{
var codes = new StringBuilder();
foreach (var item in this.events)
{
codes.Append(item + ",");
}
return codes.ToString().TrimEnd(',');
}
}
I am trying to create a method that converts a regular sql statement to c# objects, So i decided to use Irony to parse the sql statement then i return the statement as an Action that contains the type of the statement and the values of it depending on the type
Here is my non completed code [ Because i got frustrated as i don't know what to do then ]
private List<Action> ParseStatement(string statement)
{
var parser = new Parser(new SqlGrammar());
var parsed = parser.Parse(statement);
var status = parsed.Status;
while (parsed.Status == ParseTreeStatus.Parsing)
{
Task.Yield();
}
if (status == ParseTreeStatus.Error)
throw new ArgumentException("The statement cannot be parsed.");
ParseTreeNode parsedStmt = parsed.Root.ChildNodes[0];
switch (parsedStmt.Term.Name)
{
case "insertStmt":
var table = parsedStmt.ChildNodes.Find(x => x.Term.Name == "Id").ChildNodes[0].Token.ValueString;
var valuesCount =
parsedStmt.ChildNodes.Find(x => x.Term.Name == "insertData").ChildNodes.Find(
x => x.Term.Name == "exprList").ChildNodes.Count;
var values = parsedStmt.ChildNodes.Find(x => x.Term.Name == "insertData").ChildNodes.Find(
x => x.Term.Name == "exprList").ChildNodes;
foreach (var value in values)
{
string type = value.Token.Terminal.Name;
}
break;
}
return null;
}
private Type ParseType(string type)
{
switch (type)
{
case "number":
return typeof (int);
case "string":
return typeof (string);
}
return null;
}
So the Question Here is : How could i make use of Irony to convert a string SQL Statement to a c# objects ?
Here is an example of what i want to achieve :
INSERT INTO Persons VALUES (4,'Nilsen', 'Johan', 'Bakken 2',
'Stavanger')
And get it converted to
return new Action<string type, string table, int val1, string val2, string val3, string val4, string val5>;
Dynamically depending on what the method have read from the statement.
I hope i have well explained my idea so you can help me guys, And if there is something unclear please tell me and i will try to explain it.
I was trying to parse SQL with Irony as well. I gave up because the sample SQL parser in Irony don't handle: CTEs, Order by column number, half the special statements like
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
While I had a great time learning about Irony, I don't have the coding chops to implement all the aforementioned parts correctly.
I ended up using the Microsoft-provided SQL parsing library. Sample code for LINQPad 5 below:
// Add a reference to
// C:\Program Files (x86)\Microsoft SQL Server\130\SDK\Assemblies\
// Microsoft.SqlServer.TransactSql.ScriptDom.dll
//
// https://blogs.msdn.microsoft.com/gertd/2008/08/21/getting-to-the-crown-jewels/
public void Main()
{
var sqlFilePath = #"C:\Users\Colin\Documents\Vonigo\database-scripts\Client\Estimate\spClient_EstimateAddNew.sql";
bool fQuotedIdenfifiers = false;
var parser = new TSql100Parser(fQuotedIdenfifiers);
string inputScript = File.ReadAllText(sqlFilePath);
IList<ParseError> errors;
using (StringReader sr = new StringReader(inputScript))
{
var fragment = parser.Parse(sr, out errors);
fragment.Dump();
}
}
If you are not doing this as a fun exercise I would recommend using Linq to SQL to generate your stub classes or Entity Framework as Drunken Code Monkey mentioned in the comments.
Here's a good article to get you started: Generating EF code from existing DB