how to search with star (*) in LINQ - c#

I want to search with *.
I have
<asp:TextBox runat="server" Width="500" Height="20" ID="tbInfo"></asp:TextBox>
search method: when I search without star* is normal search, but when I set a star* LINQ query like tbInfo.Contains()
Example:
I set in textbox: Michael - 1 result,
I set in textbox: Michael* - 20 results
I hope someone understand me. 10x

You will have to do some coding yourself.
A sample query:
var q = (from c in db.Customers
where c.CompanyName.Contains(name)
select c)
.ToList();
The above example will always search anywhere in CompanyName for a
match. But you need to give your users a little more control over the
match method by allowing them to supply wild-card characters at either
the start or end of the text to match. This means you are left to
dynamically build your query based on the presence and location of the
wild-card characters.
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
public static class LinqExtensions
{
public static IQueryable<TSource> WhereLike<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, string>> valueSelector,
string value,
char wildcard)
{
return source.Where(BuildLikeExpression(valueSelector, value, wildcard));
}
public static Expression<Func<TElement, bool>> BuildLikeExpression<TElement>(
Expression<Func<TElement, string>> valueSelector,
string value,
char wildcard)
{
if (valueSelector == null)
throw new ArgumentNullException("valueSelector");
var method = GetLikeMethod(value, wildcard);
value = value.Trim(wildcard);
var body = Expression.Call(valueSelector.Body, method, Expression.Constant(value));
var parameter = valueSelector.Parameters.Single();
return Expression.Lambda<Func<TElement, bool>>(body, parameter);
}
private static MethodInfo GetLikeMethod(string value, char wildcard)
{
var methodName = "Contains";
var textLength = value.Length;
value = value.TrimEnd(wildcard);
if (textLength > value.Length)
{
methodName = "StartsWith";
textLength = value.Length;
}
value = value.TrimStart(wildcard);
if (textLength > value.Length)
{
methodName = (methodName == "StartsWith") ? "Contains" : "EndsWith";
textLength = value.Length;
}
var stringType = typeof(string);
return stringType.GetMethod(methodName, new Type[] { stringType });
}
}
Usage of the WhereLike extension method is as follows:
var searchTerm = "*Inc";
var q = db.Customers
.WhereLike(c => c.CompanyName, searchTerm, '*')
.ToList();
source of this is found here.

Related

linq orderby using property name string

I have a list which I need to order eg.
var list = someelements;
I also have a parameter which says by which property and what direction I should order list data eg.
var sortby = "Name";
var sortdirection = "desc";
I was wondering if I can match string property name with the right property so I get:
var list = someelements.OrderBy(x => x.Name).ToList();
when sortby == "Name" without the need of using switch loop to check property name and assign a correct property to OrderBy.
Same I would like to achieve with select either OrderBy or OrderByDescending depending whether I get sortdirection = "asc" or sortdirection = "desc"
Is it possible and if yes, how?
You can get Dynamic Linq through Nuget. Then you can use .OrderBy(sortby + " " + sortdirection).
Here is the link: Dynamic Linq.
Here is one solution that does not use Dynamic Linq:
public class Helper
{
public static IEnumerable<T> OrderByDynamic<T>(IEnumerable<T> items, string sortby, string sort_direction)
{
var property = typeof (T).GetProperty(sortby);
var result = typeof(Helper)
.GetMethod("OrderByDynamic_Private", BindingFlags.NonPublic | BindingFlags.Static)
.MakeGenericMethod(typeof (T), property.PropertyType)
.Invoke(null, new object[]{ items, sortby, sort_direction});
return (IEnumerable<T>)result;
}
private static IEnumerable<T> OrderByDynamic_Private<T,TKey>(IEnumerable<T> items, string sortby, string sort_direction)
{
var parameter = Expression.Parameter(typeof (T), "x");
Expression<Func<T, TKey>> property_access_expression =
Expression.Lambda<Func<T, TKey>>(
Expression.Property(parameter, sortby),
parameter);
if (sort_direction == "asc")
{
return items.OrderBy(property_access_expression.Compile());
}
if (sort_direction == "desc")
{
return items.OrderByDescending(property_access_expression.Compile());
}
throw new Exception("Invalid Sort Direction");
}
}
You can use it like this:
var result = Helper.OrderByDynamic(list, "Name", "desc").ToList();

How to apply an expression tree for IQueryable?

Is there any way to apply a manually created expression tree from one IQueryable to another? For example:
IQueryable<string> p = Enumerable.Empty<string>().AsQueryable();
p = p.Where(pp => pp[0] == 'A');
p = p.Skip(2).Take(4);
p = p.OrderBy(pp => pp.Length);
var vv = new[] {"Afss", "Acv", "Adfv", "Bcvx", "Ng"}.AsQueryable();
// Filtering of the vv collection with expression from p
var filteredResult = vv.Filter(p.Expression);
Or we have to separately apply the Where and OrderBy expressions?
Simple CreateQuery call does not work, because provider uses data source of the passed expression.
Before executing expression you need to replace Enumerable.Empty<string>() by a collection with your data:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
public class Program
{
public static void Main()
{
IQueryable<string> p = Enumerable.Empty<string>().AsQueryable();
p = p.Where(pp => pp[0] == 'A');
p = p.Skip(2).Take(4);
p = p.OrderBy(pp => pp.Length);
var vv = new[] {"Afss", "Acv", "Adfv", "Bcvx", "Ng"}.AsQueryable();
// Filtering of the vv collection with expression from p
var expr = ExpressionTreeConstantReplacer.CopyAndReplace(p.Expression, typeof(EnumerableQuery<string>), vv);
var filteredResult = vv.Provider.CreateQuery<string>(expr);
Console.Write("Source: \t");
foreach(var temp in vv)
Console.Write("{0} ", temp);
Console.WriteLine();
Console.Write("Filtered: ");
foreach(var temp in filteredResult)
Console.WriteLine(temp);
}
class ExpressionTreeConstantReplacer<T> : ExpressionVisitor
{
Type originalType;
T replacementConstant;
internal ExpressionTreeConstantReplacer(Type originalType, T replacementConstant)
{
this.originalType = originalType;
this.replacementConstant = replacementConstant;
}
protected override Expression VisitConstant(ConstantExpression c)
{
return c.Type == originalType ? Expression.Constant(replacementConstant) : c;
}
}
class ExpressionTreeConstantReplacer
{
internal static Expression CopyAndReplace<T>(Expression expression, Type originalType, T replacementConstant)
{
var modifier = new ExpressionTreeConstantReplacer<T>(originalType, replacementConstant);
return modifier.Visit(expression);
}
}
}
You can access the provider of the query and use CreateQuery to create a query based on an expression, and you can get the expression from the other query to pass to it:
var filteredResult = vv.Provider.CreateQuery<string>(p.Expression);
I had a similar case in which I had to send filter/sort expressions over WCF. I could not find a way to use a single instance for both the filter and the sort expression. I ended up using SerializableExpression from InterLinq. Doesn't solve your particular question but I hope it helps.

dynamic orderBy using linq dynamic

I am trying to convert this Func to use string values using linq.dynamic.
Currently I have
Func<IQueryable<Customer>, IOrderedQueryable<Customer>> orderBy = o => o.OrderBy(c => c.Postcode);
But I want to do
string sortItem = "customer";
string order = "ASC"
Func<IQueryable<Customer>, IOrderedQueryable<Customer>> orderBy = o => o.OrderBy(sortItem + " " + order);
I am using the Linq.Dynamic library but I am unable to get it to work with the function.
Any help...
Like the other answer suggests, this may not be possible. However, I wanted to post some code which I wrote recently to do something similar:
// single column sorting support
var sortColumnIndex = Convert.ToInt32(Request["iSortCol_0"]);
Func<LegalComplianceDatatable, string> orderingFunction = (c => sortColumnIndex == 0 ? c.HomeCountry :
sortColumnIndex == 1 ? c.HostCountry :
sortColumnIndex == 2 ? c.YearOneRate :
sortColumnIndex == 3 ? c.YearOtherRate :
sortColumnIndex == 4 ? c.RateType :
c.HostCountry);
if (Request["sSortDir_0"] == "desc")
{
filteredResults = filteredResults.OrderByDescending(orderingFunction);
}
else
{
filteredResults = filteredResults.OrderBy(orderingFunction);
}
I haven't used Linq.Dynamic but you can achieve this if you are comfortable building your own expression tree. For example:
public static class IQueryableExtension
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, string propertyName)
{
var memberProp = typeof(T).GetProperty(propertyName);
var method = typeof(IQueryableExtension).GetMethod("OrderByInternal")
.MakeGenericMethod(typeof(T), memberProp.PropertyType);
return (IOrderedQueryable<T>)method.Invoke(null, new object[] { query, memberProp });
}
public static IOrderedQueryable<T> OrderByInternal<T, TProp>(IQueryable<T> query, PropertyInfo memberProperty)
{
if (memberProperty.PropertyType != typeof(TProp)) throw new Exception();
var thisArg = Expression.Parameter(typeof(T));
var lamba = Expression.Lambda<Func<T, TProp>>(Expression.Property(thisArg, memberProperty), thisArg);
return query.OrderBy(lamba);
}
}
And you can use this like:
IQueryable<Customer> query; // Some query
query = query.OrderBy("Name"); // Will return an IOrderedQueryable<Customer>
This is the logic for ascending sort, without checking (you will need to make sure the property exists, and so on) and some things can be optimized (the reflected method invocation for example). It should get you started.

Like operator or using wildcards in LINQ to Entities

I'm using LINQ 2 Entities.
Following is the problem:
string str = '%test%.doc%'
.Contains(str) // converts this into LIKE '%~%test~%.doc~%%'
Expected Conversion: LIKE '%test%.doc%'
If it was LINQ 2 SQL, I could have used SqlMethods.Like as somebody answered it in my previous question. But now as I'm using L2E not L2S, I need other solution.
The SQL method PATINDEX provides the same functionality as LIKE. Therefore, you can use the SqlFunctions.PatIndex method:
.Where(x => SqlFunctions.PatIndex("%test%.doc%", x.MySearchField) > 0)
Following on from Magnus' correct answer, here is an extension method that can be re-used, as I needed in my project.
public static class LinqExtensions
{
public static Expression<Func<T, bool>> WildCardWhere<T>(this Expression<Func<T, bool>> source, Expression<Func<T, string>> selector, string terms, char separator)
{
if (terms == null || selector == null)
return source;
foreach (string term in terms.Split(new[] { separator }, StringSplitOptions.RemoveEmptyEntries))
{
string current = term;
source = source.And(
Expression.Lambda<Func<T, bool>>(
Expression.Call(selector.Body, "Contains", null, Expression.Constant(current)),
selector.Parameters[0]
)
);
}
return source;
}
}
Usage:
var terms = "%test%.doc%";
Expression<Func<Doc, bool>> whereClause = d => d;
whereClause = whereClause.WildCardWhere(d => d.docName, terms, '%');
whereClause = whereClause.WildCardWhere(d => d.someOtherProperty, "another%string%of%terms", '%');
var result = ListOfDocs.Where(whereClause).ToList();
The extension makes use of the predicate builder at http://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/. The resulting sql does a single table scan of the table, no matter how many terms are in there. Jo Vdb has an example you could start from if you wanted an extension of iQueryable instead.
You can try use this article, where author describes how to build a LIKE statement with wildcard characters in LINQ to Entities.
EDIT: Since the original link is now dead, here is the original extension class (as per Jon Koeter in the comments) and usage example.
Extension:
public static class LinqHelper
{
//Support IQueryable (Linq to Entities)
public static IQueryable<TSource> WhereLike<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, string>> valueSelector, string value, char wildcard)
{
return source.Where(BuildLikeExpression(valueSelector, value, wildcard));
}
//Support IEnumerable (Linq to objects)
public static IEnumerable<TSource> WhereLike<TSource>(this IEnumerable<TSource> sequence, Func<TSource, string> expression, string value, char wildcard)
{
var regEx = WildcardToRegex(value, wildcard);
//Prevent multiple enumeration:
var arraySequence = sequence as TSource[] ?? sequence.ToArray();
try
{
return arraySequence.Where(item => Regex.IsMatch(expression(item), regEx));
}
catch (ArgumentNullException)
{
return arraySequence;
}
}
//Used for the IEnumerable support
private static string WildcardToRegex(string value, char wildcard)
{
return "(?i:^" + Regex.Escape(value).Replace("\\" + wildcard, "." + wildcard) + "$)";
}
//Used for the IQueryable support
private static Expression<Func<TElement, bool>> BuildLikeExpression<TElement>(Expression<Func<TElement, string>> valueSelector, string value, char wildcard)
{
if (valueSelector == null) throw new ArgumentNullException("valueSelector");
var method = GetLikeMethod(value, wildcard);
value = value.Trim(wildcard);
var body = Expression.Call(valueSelector.Body, method, Expression.Constant(value));
var parameter = valueSelector.Parameters.Single();
return Expression.Lambda<Func<TElement, bool>>(body, parameter);
}
private static MethodInfo GetLikeMethod(string value, char wildcard)
{
var methodName = "Equals";
var textLength = value.Length;
value = value.TrimEnd(wildcard);
if (textLength > value.Length)
{
methodName = "StartsWith";
textLength = value.Length;
}
value = value.TrimStart(wildcard);
if (textLength > value.Length)
{
methodName = (methodName == "StartsWith") ? "Contains" : "EndsWith";
}
var stringType = typeof(string);
return stringType.GetMethod(methodName, new[] { stringType });
}
}
Usage Example:
string strEmailToFind = "%#yahoo.com"
IQueryable<User> myUsers = entities.Users.WhereLike(u => u.EmailAddress, strEmailToFind, '%');
or, if you expect your users to be more accustomed to Windows Explorer-styled wildcards:
string strEmailToFind = "*#yahoo.com"
IQueryable<User> myUsers = entities.Users.WhereLike(u => u.EmailAddress, strEmailToFind, '*');
Use a regular expression...
The following will print out all of the files in the current directory that match test.doc* (dos wildcard style - which I believe is what you're asking for)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.IO;
namespace RegexFileTester
{
class Program
{
static void Main(string[] args)
{
string[] _files = Directory.GetFiles(".");
var _fileMatches = from i in _files
where Regex.IsMatch(i, ".*test*.doc.*")
//where Regex.IsMatch(i, ".*cs")
select i;
foreach(var _file in _fileMatches)
{
Console.WriteLine(_file);
}
}
}
}
Split the String
var str = "%test%.doc%";
var arr = str.Split(new[]{'%'} ,StringSplitOptions.RemoveEmptyEntries);
var q = tblUsers.Select (u => u);
foreach (var item in arr)
{
var localItem = item;
q = q.Where (x => x.userName.Contains(localItem));
}
So I was trying the same thing - trying to pair down a List to return all candidates that matched a SearchTerm. I wanted it so that if a user typed "Arizona" it would return everything regardless of case that had Arizona. Also, if the user typed "Arizona Cha", it would return items like "Arizona License Change". The following worked:
private List<Certification> GetCertListBySearchString()
{
string[] searchTerms = SearchString.Split(' ');
List<Certification> allCerts = _context.Certifications.ToList();
allCerts = searchTerms.Aggregate(allCerts, (current, thisSearchString) => (from ac in current
where ac.Name.ToUpper().Contains(thisSearchString.ToUpper())
select ac).ToList());
return allCerts;
}

Parse string into int32 using expressions in C#

Basically I've wrote my own parser and I'm parsing a string into and expression and then compiling and storing to be reused later on.
For (an odd) example the type of string I'm parsing is this:-
if #name == 'max' and #legs > 5 and #ears > 5 then shoot()
The hash parts in the string are telling my parser to look at the properties on the object of type T that I pass in, if not found to look in a Dictionary that also gets passed in as extra.
I parse up the string into parts create an expression and join the expressions using methods from the PredicateBuilder.
I get the left value from where ever it may be and then the right value and turn it into an int32 or a string based on if it's wrapped in single quotes.. for now, then pass both into Expression.Equals/Expression.GreaterThan etc. dependant on the operator.
So far this works fine for equals and strings but take for example #ears which isn't a property on the object but is in the dictionary I end up with "does string equal int32" and it throws an exception.
I need to be able to parse the string from the dictionary into and int32 and then try the equal/greater/less than method.
Hopefully someone could shed some light on this as I haven't found anything yet that will do it and its driving me mad.
Here is the CreateExpression method I'm using to create an expression from the string.
private Expression<Func<T, IDictionary<string, string>, bool>> CreateExpression<T>(string condition)
{
string[] parts = condition.Split(new char[] { ' ' }, 3, StringSplitOptions.RemoveEmptyEntries);
if (parts.Length == 3)
{
var typeCache = _cache[typeof(T).FullName];
var param = Expression.Parameter(typeCache.T, "o");
var param2 = Expression.Parameter(typeof(IDictionary<string, string>), "d");
/* Convert right hand side into correct value type
*/
Expression right = null;
if (parts[2][0] == '\'' && parts[2][parts[2].Length - 1] == '\'')
right = Expression.Constant(parts[2].Trim(new char[] { '\'' }), typeof(string));
else
{
int x = 0;
right = (int.TryParse(parts[2].Trim(), out x))
? Expression.Constant(x)
: Expression.Constant(parts[2].Trim(new char[] { '\'' }), typeof(string));
}
/* Get the left hand side value from object T or IDictionary and then attempt to convert it
* into the right hand side value type
*/
Expression left = null;
var key = (parts[0][0] == '#') ? parts[0].TrimStart('#') : parts[0];
if (_cache[typeCache.T.FullName].Properties.Find(key, true) == null)
{
var m = typeof(ExpressionExtensions).GetMethod("GetValue");
left = Expression.Call(null, m, new Expression[] { param2, Expression.Constant(key) });
}
else
left = Expression.PropertyOrField(param, key);
/* Find the operator and return the correct expression
*/
if (parts[1] == "==")
{
return Expression.Lambda<Func<T, IDictionary<string, string>, bool>>(
Expression.Equal(left, right), new ParameterExpression[] { param, param2 });
}
else if (parts[1] == ">")
{
return Expression.Lambda<Func<T, IDictionary<string, string>, bool>>(
Expression.GreaterThan(left, right), new ParameterExpression[] { param, param2 });
}
}
return null;
}
And here is the method to parse the whole string into parts and build up the expression.
public Func<T, IDictionary<string, string>, bool> Parse<T>(string rule)
{
var type = typeof(T);
if (!_cache.ContainsKey(type.FullName))
_cache[type.FullName] = new TypeCacheItem()
{
T = type,
Properties = TypeDescriptor.GetProperties(type)
};
Expression<Func<T, IDictionary<string, string>, bool>> exp = null;
var actionIndex = rule.IndexOf("then");
if (rule.IndexOf("if") == 0 && actionIndex > 0)
{
var conditionStatement = rule.Substring(2, actionIndex - 2).Trim();
var actionStatement = rule.Substring(actionIndex + 4).Trim();
var startIndex = 0;
var endIndex = 0;
var conditionalOperator = "";
var lastConditionalOperator = "";
do
{
endIndex = FindConditionalOperator(conditionStatement, out conditionalOperator, startIndex);
var condition = (endIndex == -1) ? conditionStatement.Substring(startIndex) : conditionStatement.Substring(startIndex, endIndex - startIndex);
var x = CreateExpression<T>(condition.Trim());
if (x != null)
{
if (exp != null)
{
switch (lastConditionalOperator)
{
case "or":
exp = exp.Or<T>(x);
break;
case "and":
exp = exp.And<T>(x);
break;
default:
exp = x;
break;
}
}
else
exp = x;
}
lastConditionalOperator = conditionalOperator;
startIndex = endIndex + conditionalOperator.Length;
} while (endIndex > -1);
}
else
throw new ArgumentException("Rule must start with 'if' and contain 'then'.");
return (exp == null) ? null : exp.Compile();
}
My eyes are open to suggestions or advice on this but the idea of parsing that string as is to return true or false has to be,
EDIT:
I've now alter my method that retrieves the value from the dictionary to a generic one as Fahad suggested to this:-
public static T GetValue<T>(this IDictionary<string, string> dictionary, string key)
{
string x = string.Empty;
dictionary.TryGetValue(key, out x);
if (typeof(T) == typeof(int))
{
int i = 0;
if (int.TryParse(x, out i))
return (T)(i as object);
}
return default(T);
//throw new ArgumentException("Failed to convert dictionary value to type.");
}
And this works fine but I would rather return null than a default value for the type i've tried using Nullable ... where T : struct and returning null when not found or can't convert but I don't know how to use the null result in my expression.
I think you should redo your problem analysis and try another way. One way I would propose is to have a generic method that would give you the value from a IDictionary or IList etc., and use this method to wrap around your Expression's. The Expression would only want the "Type" of the object to be satisfied, if that is done correctly, then you could easily do it. If you know the "Type" then you can implement it with generic methods, otherwise, you could still use reflection and do generics.
So all you have to think is how to get the value from the dictionary or any other list through expressions.
Hope that helps.
-Fahad
You could use Expression.Call(null, typeof(int), "Parse", Type.EmptyTypes, yourTextExpression)

Categories