|
|
 Prize winner in Competition
"Best ASP.NET article of February 2010"
Comments and Discussions
|
|
 |

|
hi ilya, i am new to the EF and instead of a generated list, i pass the EF database context, I just followed your code here and i have learned a lot. i just wonder why the DBcontext doesn't get sorted after it passed to the LinqExpressions function. But when I debug it, inside the IQueryable OrderBy function, it returns the correct result. This is the repository i have:
public IQueryable<Driver>> GetAllDrivers()
{
return context.Driver.ToList().AsQueryable();
}
Thanks.
|
|
|
|
|

|
Hi,
Great article! Helped me a lot and directed in right direction. However I am struggling in figuring it out as How can I implement the same in multi tier application with repository pattern.
I do not want to implement following repetitive code in each controller action.
var query = _repository.Computers();
//filtring
if (grid.IsSearch)
{
//And
if (grid.Where.groupOp == "AND")
foreach (var rule in grid.Where.rules)
query = query.Where<Computer>(
rule.field, rule.data,
(WhereOperation)StringEnum.Parse(typeof(WhereOperation), rule.op));
else
{
//Or
var temp = (new List<Computer>()).AsQueryable();
foreach (var rule in grid.Where.rules)
{
var t = query.Where<Computer>(
rule.field, rule.data,
(WhereOperation)StringEnum.Parse(typeof(WhereOperation), rule.op));
temp = temp.Concat<Computer>(t);
}
//remove repeating records
query = temp.Distinct<Computer>();
}
}
//sorting
query = query.OrderBy<Computer>(grid.SortColumn,
grid.SortOrder);
//count
var count = query.Count();
//paging
var data = query.Skip((grid.PageIndex - 1) * grid.PageSize).Take(grid.PageSize).ToArray();
Rather want a repository method to handle this code.
Any suggestions and help on this is highly appreciable.
Thanks.
|
|
|
|
|
|

|
Hi,
I reached here while finding solution to one of the issue with jqGrid which is bothering me from quite some time.
Can you please have a look at the question I posted in Q/A forum
Filter jqGrid by multiple conditions[^]
Thanks
|
|
|
|

|
Hi Ilya, your olution realy helped me out - just 1 question,
despite adding a distinct method to the results of the OR operation, my results still have duplicate records
see stack overflow question
http://stackoverflow.com/questions/10892892/how-should-i-make-the-following-linq-generic-method-returns-distinct.lo-records[^] edited : seems the duplicate results were due to using a new list as the start point for the 'or' operation while in my code i was passing in an already prepared list of entities that had an earlier filter applied, solution was to drop the new list and use the result passed in
modified 21 Jun '12 - 17:46.
|
|
|
|

|
Excellent article.
Made it to work in MVC 3.
Also extended 'where' for lt, le, gt, ge operations
|
|
|
|
|
|
|
|

|
Great. This article is well written and saved me a lot of time.
|
|
|
|

|
Hi,
Thanks a lot for this article. It helped me a lot on my learning.
But I have a doubt about searching for items listed in the grid.
When I click the search button, it shows a popup, asking me to enter values for filtering, but, if I don't supply values in textfields, an exception is thrown (System.FormatException) because no filter values were supplied.
Is there some way we could validate the information required from the users to enter in those textboxes?
|
|
|
|
|

|
public ActionResult CustomerGridData(GridSettings grid)
{
int totalRecords;
var data = _customerService.CustomerTable.SearchGrid(grid, out totalRecords);
var result = new
{
total = (int)Math.Ceiling((double)totalRecords / grid.PageSize),
page = grid.PageIndex,
records = totalRecords,
rows = (from c in data
select new
{
id = c.CustomerID,
cell = new string[] { c.CustomerID.ToString(), c.CompanyName, c.ContactName, c.ContactTitle, c.Address, c.City, c.Region, c.PostalCode, c.Country, c.Phone, c.Fax }
}).ToArray()
};
return Json(result, JsonRequestBehavior.AllowGet);
}
using System.Linq.Dynamic;
public static class LinqExtensions
{
public static T[] SearchGrid<T>(this IQueryable<T> query, GridSettings grid, out int totalRecords) where T : class
{
if (grid.IsSearch)
{
StringBuilder sb = new StringBuilder();
int i = 0;
foreach (var rule in grid.Where.rules)
{
string op = null;
switch (rule.op)
{
case "eq": op = rule.field + "={0}";
break;
case "ne": op = rule.field + "!={0}";
break;
case "lt": op = rule.field + "<{0}";
break;
case "le": op = rule.field + "<={0}";
break;
case "gt": op = rule.field + ">{0}";
break;
case "ge": op = rule.field + ">={0}";
break;
case "bw": op = rule.field + ".StartsWith({0})";
break;
case "bn": op = "!" + rule.field + ".StartsWith({0})";
break;
case "in":
break;
case "ni":
break;
case "ew": op = rule.field + ".EndsWith({0})";
break;
case "en": op = "!" + rule.field + ".EndsWith({0})";
break;
case "cn": op = rule.field + ".Contains({0})";
break;
case "nc": op = "!" + rule.field + ".Contains({0})";
break;
case "nu": op = rule.field + "==null";
break;
case "nn": op = rule.field + "!=null";
break;
}
if (op == null)
throw new NotSupportedException("rule.op=" + rule.op);
op = string.Format(op, "@" + i.ToString());
sb.Append(op);
if (rule != grid.Where.rules.Last())
{
sb.Append(grid.Where.groupOp == "AND" ? "&&" : "||");
}
i++;
}
var predicate = sb.ToString();
var values = grid.Where.rules.Select(r => r.data).ToArray();
query = query.Where<T>(predicate, values);
}
totalRecords = query.Count();
query = query.OrderBy<T>(grid.SortColumn, grid.SortOrder);
var data = query.Skip((grid.PageIndex - 1) * grid.PageSize).Take(grid.PageSize).ToArray();
return data;
}
}
|
|
|
|

|
Dimitry,
Very clean example and nice application of Dynamic LINQ! But how would you handle the exception thrown:
Operator '=' incompatible with operand types 'DateTime?' and 'String'
when the column is a datetime (string in colModel but DateTime? in C#/database?
I am sure there are other types that would throw similar exceptions.
As far as I can tell, there is no way to receive any user specified type information in the Filters object passed in from jqGrid either.
EDIT: One option I have come across specific to Dynamic LINQ and this issue is to take the offending String strArg and running through DateTime.Parse(strArg) before passing in to Dynamic LINQ. That is fine on an explicit LINQ to Entities lambda expression, but when I tried to modify your approach (including a hack I added to fudge the DateTime type selection by looking at (filter.field.IndexOf("date") >= 0)) to work the rule to come out as:
"dateTimeCol=DateTime.Parse(@0)"
I then get:
"LINQ to Entities does not recognize the method 'System.DateTime Parse(System.String)' method, and this method cannot be translated into a store expression."
So, it appears Dynamic Linq just can't handle DateTime this way?
Thanks,
Mark
|
|
|
|

|
Hi Ilya,
Great work..
I managed to get this work in a MVC3 project. In that the records were not visible, though they looks to be loaded correctly (total records count, page numbers values are correct). Then I had to set 'repeatitems: true' to them to see. Is this the right way to do that?
However in that same system the search popup is not move-able, do I have missed a script there? Can you get me a clue to fix that issue..
- A random opportunity is like a taller chair, those who sit hang on, those who hang on fall
L.W.C. Nirosh.
Colombo,
Sri Lanka.
|
|
|
|

|
okay I got everything sorted out.. I added scripts to the view, then it started to work..
- A random opportunity is like a taller chair, those who sit hang on, those who hang on fall
L.W.C. Nirosh.
Colombo,
Sri Lanka.
|
|
|
|

|
I changed the app to connect to a database (Oracle 11.2, via Entity Framework and the ODAC data access components, version 11.2.0.2.30 beta).
When I look at the SQL submitted to the database, the filter parameters are embedded as constants, e.g.:
WHERE (N'User2' = "Extent1"."User")
instead of as parameters:
WHERE (:p__linq__0 = "Extent1"."User")
That causes the db server to parse the query each time. Correct results, but inefficient and slow.
Maybe the problem is in ODAC, in the way it interprets the ConstantExpression, so maybe it can't be fixed until Oracle fix it. But if I use a standard LINQ "Where" clause (i.e. not through the expression tree) it adds parameters correctly. So I guess there must be a way, I just need to mimic the expression tree that a LINQ query creates.
Here are the relevant lines (LinqExtensions.cs line 56):
ConstantExpression filter = Expression.Constant
(
Convert.ChangeType(value, memberAccess.Type)
);
condition = Expression.Equal(memberAccess, filter);
Any ideas? Can anyone confirm whether their queries get parameterized? Whether on Oracle or some other database.
Thanks
Paul
|
|
|
|

|
Thanks very much for this article. I was able to adapt your code for a non-MVC project I've been working on for several months. Your solution was ideal for solving my technical issues.
|
|
|
|

|
Very good article - it has helped me along with figuring out jqGrid. Thank you!
I've got two columns of data - one always has a Date and the other is nullable. Wanted to use the date picker for this so I added specified the colModel like this for those columns:
colModel: [
{name: 'FlavorPromotionStartDate', index: 'FlavorPromotionStartDate', width: 120, align: 'center',
sorttype: 'date', formatter: 'date', formatoptions: {newformat: 'm/d/Y' },
searchoptions: { sopt: ['eq', 'gt'], dataInit: function(elem) {
$(elem).datepicker({ changeYear: true, changeMonth: true, showButtonPanel: true})
}
}
},
colModel: [
{name: 'FlavorPromotionEndDate', index: 'FlavorPromotionEndDate', width: 120, align: 'center',
sorttype: 'date', formatter: 'date', formatoptions: {newformat: 'm/d/Y' },
searchoptions: { sopt: ['eq', 'gt'], dataInit: function(elem) {
$(elem).datepicker({ changeYear: true, changeMonth: true, showButtonPanel: true})
}
}
},
and then I added
[StringValue("gt")]
Greater
to the WhereOperation.cs file.
And over in the LinqExtension.cs file I added in the switch(operation) for Where:
case WhereOption.Greater:
condition = Expression.GreaterThan(memberAccess, filter);
lambda = Expression.Lambda(condition, parameter);
break;
For the non-nullable date field it works great, but for the nullable data (FlavorPromotionEndDate), I get an Invalid cast from 'System.String' to 'System.Nullable`1[[System.DateTime, mscorlib, Version=4.0.0.0, Culture=neutral, ... " exception.
This happens over in LinqExtensions.cs where this is happening:
ConstantExpression filter = Expression.Constant
(
Convert.ChangeType(value, memberAccess.Type)
);
Ideas?
Thanks,
Matt
“You can't teach people to be lazy - either they have it, or they don't.”
-Dagwood Bumstead
modified on Tuesday, May 31, 2011 7:40 PM
|
|
|
|

|
So a solution for this problem came from a reply to a stackoverflow post by the very smart C# compiler fellow, Eric Lippert, here[^]. Essentially, what I did was something like his post suggested:
static Expression MyGreaterThan(Expression e1, Expression e2)
{
if (IsNullableType(e1.Type) && !IsNullableType(e2.Type))
e2 = Expression.Convert(e2, e1.Type);
else if (!IsNullableType(e1.Type) && IsNullableType(e2.Type))
e1 = Expression.Convert(e1, e2.Type);
return Expression.GreaterThan(e1, e2);
}
static bool IsNullableType(Type t)
{
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}
I created my own GreaterThan, GreatThanOrEqual, LessThan, LessThanOrEqual, Equal and NotEqual and refactored out the common stuff from his example. It works well, essentially taking the non-nullable type and converting it to the nullable type so that the Expression.GreaterThan (or whichever one is called) will work properly.
“You can't teach people to be lazy - either they have it, or they don't.”
-Dagwood Bumstead
|
|
|
|

|
Thanks for your comment
|
|
|
|

|
Hi my friend, i use your code with very small modifications, but throw me some errors when the code call a type.GetProperty(prop) because the prop is in an another object of this property, for example:
public class A
{
string prop { get; set; }
}
public class B
{
string prop2 { get; set;}
A prop3 { get; set; }
}
when the Linq try to access prop throws an error because its not a property of A. I dont know how to fix the problem, i think the answer its only reflection but i lost.
PD: sorry for my bad english
|
|
|
|

|
Have downloaded ur solution and just running the project, but am getting this error while running in MvcControllerFactory.cs am getting value cannot be null, wht am missing anything, any modifications that i need to do
|
|
|
|

|
I am getting the same error. It appears that MvcControllerFactory.GetZControllerInstance is getting called when jqGrid is trying to load and it is being passed a null controllerType. I am unsure at the point why that is happening. Any help would be appreciated. Thank you.
|
|
|
|

|
Maybe the reason is changes in new version of ASP.NET MVC. I'll try to check the solution when I have spare time
|
|
|
|

|
if (controllerType == null)
{
return null;
}
else
{
return Activator.CreateInstance(controllerType, new FakeComputersRepository()) as IController;
}
i fixed error by code are above. It work for me.
Chain Saw Electric
I hope it can help you pass this error.
|
|
|
|

|
My table contains 5 255 char wide fields.
for this table jqGrid search window width is too small, horizontal scollbar appears.
Search window contains 3 columns with equal width.
Column name and search operation width is too big.
How to increase search window width and decrease column name and search operand columns widths ?
Andrus
|
|
|
|
|
|
|

|
Hello.
Just want to thank you for the article. It help me alot.
Bellow is my final adaptation of your code and a few of the comments suggested.
I joined them all and make a method that suit my needs.
Linq Extension
public static IQueryable<T> Where<T>(this IQueryable<T> pQuery, SearchCriteria[] pCriterias, GroupOperator pGroupOp, bool pCaseSensitive)
{
if (pCriterias.Count() == 0)
return pQuery;
LambdaExpression lambda;
Expression resultCondition = null;
ParameterExpression parameter = Expression.Parameter(pQuery.ElementType, "p");
foreach (var searchCriteria in pCriterias)
{
if (string.IsNullOrEmpty(searchCriteria.Column))
continue;
MemberExpression memberAccess = null;
foreach (var property in searchCriteria.Column.Split('.'))
memberAccess = MemberExpression.Property
(memberAccess ?? (parameter as Expression), property);
ConstantExpression filter = Expression.Constant
(
Convert.ChangeType(searchCriteria.Value, memberAccess.Type)
);
Expression condition = null;
switch (searchCriteria.Operation)
{
case WhereOperation.Equal:
if (pCaseSensitive || searchCriteria.CaseSensitive)
{
condition = Expression.Equal(memberAccess, filter);
}else{
Expression toLower = Expression.Call(memberAccess, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
condition = Expression.Equal(toLower, Expression.Constant(searchCriteria.Value.ToString().ToLower()));
}
break;
case WhereOperation.NotEqual:
if (pCaseSensitive || searchCriteria.CaseSensitive)
{
condition = Expression.NotEqual(memberAccess, filter);
}
else
{
Expression toLower = Expression.Call(memberAccess, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
condition = Expression.NotEqual(toLower, Expression.Constant(searchCriteria.Value.ToString().ToLower()));
}
break;
case WhereOperation.Greater:
if (pCaseSensitive || searchCriteria.CaseSensitive)
{
condition = Expression.GreaterThan(memberAccess, filter);
}
else
{
Expression toLower = Expression.Call(memberAccess, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
condition = Expression.GreaterThan(toLower, Expression.Constant(searchCriteria.Value.ToString().ToLower()));
}
break;
case WhereOperation.GreaterOrEqual:
if (pCaseSensitive || searchCriteria.CaseSensitive)
{
condition = Expression.GreaterThanOrEqual(memberAccess, filter);
}
else
{
Expression toLower = Expression.Call(memberAccess, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
condition = Expression.GreaterThanOrEqual(toLower, Expression.Constant(searchCriteria.Value.ToString().ToLower()));
}
break;
case WhereOperation.Less:
if (pCaseSensitive || searchCriteria.CaseSensitive)
{
condition = Expression.LessThan(memberAccess, filter);
}
else
{
Expression toLower = Expression.Call(memberAccess, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
condition = Expression.LessThan(toLower, Expression.Constant(searchCriteria.Value.ToString().ToLower()));
}
break;
case WhereOperation.LessEqual:
if (pCaseSensitive || searchCriteria.CaseSensitive)
{
condition = Expression.LessThanOrEqual(memberAccess, filter);
}
else
{
Expression toLower = Expression.Call(memberAccess, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
condition = Expression.LessThanOrEqual(toLower, Expression.Constant(searchCriteria.Value.ToString().ToLower()));
}
break;
case WhereOperation.Contains:
if (pCaseSensitive || searchCriteria.CaseSensitive)
{
condition = Expression.Call(memberAccess,
typeof(string).GetMethod("Contains"),
Expression.Constant(searchCriteria.Value));
}
else
{
Expression toLower = Expression.Call(memberAccess, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
condition = Expression.Call(toLower,
typeof(string).GetMethod("Contains"),
Expression.Constant(searchCriteria.Value.ToString().ToLower()));
}
break;
default:
continue;
}
if (pGroupOp == GroupOperator.AND)
resultCondition = resultCondition != null ? Expression.And(resultCondition, condition) : condition;
else
resultCondition = resultCondition != null ? Expression.Or(resultCondition, condition) : condition;
}
lambda = Expression.Lambda(resultCondition, parameter);
MethodCallExpression result = Expression.Call(
typeof(Queryable), "Where",
new[] { pQuery.ElementType },
pQuery.Expression,
lambda);
return pQuery.Provider.CreateQuery<T>(result);
}
Usage(VB.NET):
Dim seee =
Dim isSearch As Boolean = CType(_search, Boolean)
If (isSearch) Then
If (filters <> String.Empty) Then
Dim filter As Filter = Filter.Create(filters)
Dim searchCriterias = (From r In filter.rules _
Select New SearchCriteria With _
{.Column = r.field, _
.Operation = CType(StringEnum.Parse(GetType(WhereOperation), r.op), WhereOperation), _
.Value = r.data, _
.CaseSensitive = False}).ToArray()
If (filter.groupOp = "AND") Then
seee = seee.Where(searchCriterias, GroupOperator.AND, False)
Else
seee = seee.Where(searchCriterias, GroupOperator.OR, False)
End If
End If
End If
|
|
|
|

|
Forgot to show the changes in WhereOperation, SearchCriteria and also a enum used to the group Operator
I havent implement the operations commented.
Regards
public enum WhereOperation
{
[StringValue("eq")] Equal,
[StringValue("ne")] NotEqual,
[StringValue("lt")] Less,
[StringValue("le")] LessEqual,
[StringValue("gt")] Greater,
[StringValue("ge")] GreaterOrEqual,
[StringValue("cn")] Contains
}
public struct SearchCriteria
{
public string Column;
public object Value;
public WhereOperation Operation;
public bool CaseSensitive;
}
public enum GroupOperator
{
AND,
OR
}
|
|
|
|

|
Thanks for your reply. I think it can help somebody else
|
|
|
|

|
This was VERY helpful to me. Thanks!
“You can't teach people to be lazy - either they have it, or they don't.”
-Dagwood Bumstead
|
|
|
|

|
This method really helped me. Many thanks.
In case anybody wants to implement "Starts With" and "Ends With", here is the switch case I added.
case WhereOperation.StartsWith:
if (pCaseSensitive || searchCriteria.CaseSensitive)
{
condition = Expression.Call(memberAccess,
typeof(string).GetMethod("StartsWith", new[] { typeof(string) }),
Expression.Constant(searchCriteria.Value));
}
else
{
Expression toLower = Expression.Call(memberAccess, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
condition = Expression.Call(toLower,
typeof(string).GetMethod("StartsWith", new[] { typeof(string) }),
Expression.Constant(searchCriteria.Value.ToString().ToLower()));
}
break;
case WhereOperation.EndsWith:
if (pCaseSensitive || searchCriteria.CaseSensitive)
{
condition = Expression.Call(memberAccess,
typeof(string).GetMethod("EndsWith", new[] { typeof(string) }),
Expression.Constant(searchCriteria.Value));
}
else
{
Expression toLower = Expression.Call(memberAccess, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
condition = Expression.Call(toLower,
typeof(string).GetMethod("EndsWith", new[] { typeof(string) }),
Expression.Constant(searchCriteria.Value.ToString().ToLower()));
}
break;
---------------------------
Make a note of new[] { typeof(string) } after the method name.
|
|
|
|

|
very well explained and accurate.
Help me alot
|
|
|
|

|
Excellent. Thank you for this very clear article.
|
|
|
|

|
Hi Ilya,
Great post. it really helped us a lot.
the only problem we found, is something performance related when using the OR operation of jqGrid.
We came to this solution using binaries expressions to build the OR operation.
Hope you find it useful.
Congrats!
public struct SearchCriteria
{
public string Column;
public object Value;
public WhereOperation Operation;
}
var searchCriterias = grid.Where.rules.Select(Rule => new SearchCriteria
{
Column = Rule.field,
Operation =
(WhereOperation)
StringEnum.Parse(
typeof (WhereOperation),
Rule.op),
Value = Rule.data
}).ToArray();
query = query.WhereOr(searchCriterias);
public static IQueryable<T> WhereOr<T>( this IQueryable<T> Query, SearchCriteria [ ] Criterias )
{
if( Criterias.Count( ) == 0 )
return Query;
LambdaExpression lambda;
Expression resultCondition = null;
ParameterExpression parameter = Expression.Parameter( Query.ElementType, "p" );
foreach( var searchCriteria in Criterias )
{
if( string.IsNullOrEmpty( searchCriteria.Column ) )
continue;
MemberExpression memberAccess = null;
foreach( var property in searchCriteria.Column.Split( '.' ) )
memberAccess = MemberExpression.Property
( memberAccess ?? ( parameter as Expression ), property );
ConstantExpression filter = Expression.Constant
(
Convert.ChangeType( searchCriteria.Value, memberAccess.Type )
);
Expression condition = null;
switch( searchCriteria.Operation )
{
case WhereOperation.Equal:
condition = Expression.Equal( memberAccess, filter );
break;
case WhereOperation.NotEqual:
condition = Expression.NotEqual( memberAccess, filter );
break;
case WhereOperation.Greater:
condition = Expression.GreaterThan( memberAccess, filter );
break;
case WhereOperation.GreaterOrEqual:
condition = Expression.GreaterThanOrEqual( memberAccess, filter );
break;
case WhereOperation.Less:
condition = Expression.LessThan( memberAccess, filter );
break;
case WhereOperation.LessEqual:
condition = Expression.LessThanOrEqual( memberAccess, filter );
break;
case WhereOperation.Contains:
condition = Expression.Call( memberAccess,
typeof( string ).GetMethod( "Contains" ),
Expression.Constant( searchCriteria.Value ) );
break;
default:
continue;
}
resultCondition = resultCondition != null ? Expression.Or( resultCondition, condition ): condition;
}
lambda = Expression.Lambda( resultCondition, parameter );
MethodCallExpression result = Expression.Call(
typeof( Queryable ), "Where",
new [ ] { Query.ElementType },
Query.Expression,
lambda );
return Query.Provider.CreateQuery<T>( result );
}
|
|
|
|

|
Thanks!
If I'll use this code again I'll pay attention to your remark
|
|
|
|

|
Ilya: Thanks for a great article!
sdreyesg: I tried using your OR-extension, but my application keep crashing when I use the OR filter. What I see is that the query generated looks like this:
Where(x=> (x.y==0) Or (x.z==0))
I am guessing that it is the word "Or" that is misplaced here. Should it not be "||" in order to work?
Have you encountered a similar issue with your implementation?
|
|
|
|

|
Replace this with the existing try block in LinqExtensions.cs in method IQueryable<T> Where<T> to handle null values;
/*Code*/
try
{
Type targetType = memberAccess.Type;
if (memberAccess.Type.IsGenericType && memberAccess.Type.GetGenericTypeDefinition() == typeof(Nullable<>))
targetType = memberAccess.Type.GetGenericArguments()[0];
//change param value type
//necessary to getting bool from string
filter = Expression.Constant
(
Convert.ChangeType(value, targetType)
);
}
|
|
|
|

|
How would you suppose I could make it case sensitivity.
modified on Monday, July 26, 2010 8:12 PM
|
|
|
|

|
Got it by chaining an expression call to ToLower on the member access
Converted to VB:
Case WhereOperation.Contains
Dim toLower As Expression = Expression.Call(memberAccess, GetType(String).GetMethod("ToLower", System.Type.EmptyTypes))
condition = Expression.[Call](toLower, GetType(String).GetMethod("Contains"), Expression.Constant(value.ToString().ToLower))
lambda = Expression.Lambda(condition, parameter)
Exit Select
|
|
|
|

|
thanks for sharing your improvement
|
|
|
|

|
public static bool Comparer(this object value1, object value2, WhereOperation operation)
{
string strValue1 = value1.ToString().ToLowerInvariant().Trim();
string strValue2 = value2.ToString().ToLowerInvariant().Trim();
double dblValue1 = -1;
double dblValue2 = -1;
bool areNumbers = double.TryParse(strValue1, out dblValue1) && double.TryParse(strValue2, out dblValue2);
switch (operation)
{
case WhereOperation.Equal:
return areNumbers ? dblValue1 == dblValue2 : strValue1 == strValue2;
case WhereOperation.NotEqual:
return areNumbers ? dblValue1 != dblValue2 : strValue1 != strValue2;
case WhereOperation.Contains:
return strValue1.Contains(strValue2);
case WhereOperation.DoesNotContain:
return !strValue1.Contains(strValue2);
case WhereOperation.BeginsWith:
return strValue1.StartsWith(strValue2);
case WhereOperation.DoesNotBeginWith:
return !strValue1.StartsWith(strValue2);
case WhereOperation.EndsWith:
return strValue1.EndsWith(strValue2);
case WhereOperation.DoesNotEndWith:
return !strValue1.EndsWith(strValue2);
case WhereOperation.LessThan:
return areNumbers ? dblValue1 < dblValue2 : true;
case WhereOperation.LessThanOrEqual:
return areNumbers ? dblValue1 <= dblValue2 : true;
case WhereOperation.GreaterThan:
return areNumbers ? dblValue1 > dblValue2 : true;
case WhereOperation.GreaterThanOrEqual:
return areNumbers ? dblValue1 >= dblValue2 : true;
}
return true; }
public static IQueryable<T> Where<T>(this IQueryable<T> query, string column, object value, WhereOperation operation)
{
try
{
if (string.IsNullOrEmpty(column))
return query;
ParameterExpression parameter = Expression.Parameter(query.ElementType, "p");
MemberExpression memberAccess = null;
foreach (var property in column.Split('.'))
memberAccess = Expression.Property(memberAccess ?? (parameter as Expression), property);
if (memberAccess == null)
return query.Where(x => true);
Expression conditional = Expression.Call(null, typeof(LinqExtensions).GetMethod("Comparer"), Expression.Convert(memberAccess, typeof(object)), Expression.Convert(Expression.Constant(value), typeof(object)), Expression.Constant(operation));
MethodCallExpression result = Expression.Call(typeof(Queryable), "Where", new[] { query.ElementType }, query.Expression, Expression.Lambda(conditional, parameter));
return query.Provider.CreateQuery<T>(result);
}
catch (Exception e)
{
return query.Where(x => true); }
}
|
|
|
|

|
Unfortunately this change does not work with NHibernate... I have not discovered a correction yet.
|
|
|
|

|
This change removes an issue where different types with throw an error when comparing. Now the code will do it's best to cast the value, and if it fails compare them as strings
value = Convert.ChangeType(value, memberAccess.Type);
to
Expression field;
try
{
value = Convert.ChangeType(value, memberAccess.Type);
field = memberAccess;
}
catch(FormatException ex)
{
value= value.ToString();
field=Expression.Call(memberAccess,memberAccess.Type.GetMethod("ToString",new Type[0]));
}
and then
condition = Expression.Equal(field, filter);
|
|
|
|
 |
|
|
General News Suggestion Question Bug Answer Joke Rant Admin
|
This article describes how to use jqGrid’s search toolbar with multiple filters in ASP.NET MVC.
| Type | Article |
| Licence | CPOL |
| First Posted | 12 Feb 2010 |
| Views | 125,634 |
| Downloads | 5,816 |
| Bookmarked | 128 times |
|
|