IEnumerable<dynamic> with linq - c#

I am trying to build dynamic data context, linq is not support dynamic type
I found this solution on
http://jrwren.wrenfam.com/blog/2010/03/04/linq-abuse-with-the-c-4-dynamic-type/
public static class ObjectExtensionMethod
{
public static IEnumerable<dynamic> Select(this object source, Func<dynamic, dynamic> map)
{
foreach (dynamic item in source as dynamic)
{
yield return map(item);
}
}
public static IEnumerable<dynamic> Where(this object source, Func<dynamic, dynamic> predicate)
{
foreach (dynamic item in source as dynamic)
{
if (predicate(item))
yield return item;
}
}
}
the problem with this solution is getting all data from database after that applying the where statement. is there any way to apply where statement before getting the data from database with dynamic type

the problem with this solution is getting all data from database after that applying the where statement.
The problem here is not with dynamic, but with the way you are iterating over the source. You are using foreach, and expect it to be translated to an SQL or sort of, but that is just wrong assumption. As soon as iterator is created by GetEnumerator() method call the query will be "materialized", even if real type of the source is implementing IQueryable<T>, end everything else will be performed in memory.
If you want the conditions to be translated to an SQL you need to implement IQueryableProvider.
Or, at least you can try to call underlying IQueryableProvider. But I'm not sure if it would work.
public static class ObjectExtensionMethod
{
public static IQueryable Select<T>(this IQueryable source, Expression<Func<dynamic, dynamic>> map)
{
var method = new Func<IQueryable<dynamic>, Expression<Func<dynamic, dynamic>>, IQueryable<dynamic>>(Queryable.Select).Method;
var call = Expression.Call(null, method, source.Expression, Expression.Quote(map));
return source.Provider.CreateQuery(call);
}
public static IQueryable Where(this IQueryable source, Expression<Func<dynamic, bool>> predicate)
{
var method = new Func<IQueryable<dynamic>, Expression<Func<dynamic, bool>>, IQueryable<dynamic>>(Queryable.Where).Method;
var call = Expression.Call(null, method, source.Expression, Expression.Quote(predicate));
return source.Provider.CreateQuery(call);
}
}
Please note that type of source parameter has changed from object to IQueryable and types of map and predicate parameters have changed to Expression<Func<,>>.

After long search the solution that is working for me
public static class ObjectExtensionMethod
{
public static IQueryable Select(this IQueryable source, Expression<Func<dynamic, dynamic>> map)
{
try
{
var method = new Func<IQueryable<dynamic>, Expression<Func<dynamic, dynamic>>, IQueryable<dynamic>>(Queryable.Select).Method;
Expression conversion = Expression.Convert(source.Expression, typeof(System.Linq.IQueryable<dynamic>));
var call = Expression.Call(null, method, conversion, Expression.Quote(map));
return source.Provider.CreateQuery(call);
}
catch (Exception ex)
{
return null;
}
}
public static IQueryable Where<T>(this IQueryable source, Expression<Func<T, bool>> predicate)
{
try
{
var method = new Func<IQueryable<T>, Expression<Func<T, bool>>, IQueryable<T>>(Queryable.Where).Method;
Expression conversion = Expression.Convert(source.Expression, typeof(System.Linq.IQueryable<T>));
var call = Expression.Call(null, method, conversion, predicate);
return source.Provider.CreateQuery(call);
}
catch (Exception ex)
{
return null;
}
}
public static IEnumerable<dynamic> ToList(this IQueryable source)
{
return source as dynamic;
}
}
the problem know passing Expression<Func<dynamic, dynamic>>
to solve this problem
public Expression<Func<T, Boolean>> GetWhereFunc<T>(List<WhereCondition> searchFieldList,T item)
{
try
{
if (searchFieldList == null || searchFieldList.Count == 0)
{
return null;
}
ParameterExpression pe = Expression.Parameter(item.GetType(), "c");
//ParameterExpression pe = Expression.Parameter(typeof(object), "c");
Type itemType = item.GetType();
//combine them with and 1=1 Like no expression
Expression combined = null;
Type tempPropType;
if (searchFieldList != null)
{
foreach (WhereCondition fieldItem in searchFieldList)
{
if (string.IsNullOrEmpty(fieldItem.Value))
continue;
if (!string.IsNullOrEmpty(fieldItem.GridTblName))
continue;
//Expression for accessing Fields name property
Expression columnNameProperty = Expression.Property(pe, fieldItem.ColumName);
//the name constant to match
Expression columnValue = null;
tempPropType = itemType.GetProperty(fieldItem.ColumName).PropertyType;
if (tempPropType == typeof(DateTime) || tempPropType == typeof(DateTime?))
{
if (string.IsNullOrEmpty(fieldItem.Value))
{
}
else
{
DateTime tempdate = DateTime.Parse(fieldItem.Value);
TimeZone zoneclient = TimeZone.CurrentTimeZone;
TimeSpan spclient = zoneclient.GetUtcOffset(tempdate);
tempdate = tempdate.AddMinutes(-1 * spclient.TotalMinutes);
fieldItem.Value = tempdate.ToString();
}
}
if (tempPropType == typeof(Guid) || tempPropType == typeof(Guid?))
{
if (string.IsNullOrEmpty(fieldItem.Value))
{
columnValue = Expression.Constant(null);
}
else
{
if (tempPropType == typeof(Guid?))
{
columnValue = Expression.Constant((Guid?)Guid.Parse(fieldItem.Value), typeof(Guid?));
}
else
{
columnValue = Expression.Constant(Guid.Parse(fieldItem.Value));
}
}
}
else if (tempPropType.IsGenericType && tempPropType.GetGenericTypeDefinition() == typeof(Nullable<>))
{
if (string.IsNullOrEmpty(fieldItem.Value))
{
columnValue = Expression.Constant(null);
}
else
{
columnValue = Expression.Constant(Convert.ChangeType(fieldItem.Value, tempPropType.GetGenericArguments()[0])
, tempPropType);
}
}
else
{
columnValue = Expression.Constant(Convert.ChangeType(fieldItem.Value, tempPropType));
}
Expression e1 = null;
MethodInfo method;
switch (fieldItem.Cond)
{
case Condetion.Equal:
e1 = Expression.Equal(columnNameProperty, columnValue);
break;
case Condetion.Greater:
e1 = Expression.GreaterThan(columnNameProperty, columnValue);
break;
case Condetion.GreaterOrEqual:
e1 = Expression.GreaterThanOrEqual(columnNameProperty, columnValue);
break;
case Condetion.Lower:
e1 = Expression.LessThan(columnNameProperty, columnValue);
break;
case Condetion.LowerOrEqual:
e1 = Expression.LessThanOrEqual(columnNameProperty, columnValue);
break;
case Condetion.NotEqual:
e1 = Expression.NotEqual(columnNameProperty, columnValue);
break;
case Condetion.Contaiens:
if (fieldItem.IsContaien)
{
Type tt = fieldItem.Values.GetType();
if (tt == typeof(List<dynamic>))
{
IEnumerable<dynamic> val = fieldItem.Values.Cast<dynamic>().ToList();
var someValueContain = Expression.Constant(val, val.GetType());
var convertExpression = Expression.Convert(columnNameProperty, typeof(object));
e1 = Expression.Call(someValueContain, "Contains", new Type[] { }, convertExpression);
}
else
{
var mval = fieldItem.Values.AsQueryable().Cast<dynamic>();
var someValueContain = Expression.Constant(mval, mval.GetType());
var convertExpression = Expression.Convert(columnNameProperty, typeof(object));
e1 = Expression.Call((
((Expression<Func<bool>>)
(() => Queryable.Contains(default(IQueryable<dynamic>), default(object))))
.Body as MethodCallExpression).Method,
someValueContain,
convertExpression);
}
}
else
{
method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var someValueContain = Expression.Constant(fieldItem.Value, columnValue.Type);
e1 = Expression.Call(columnNameProperty, method, someValueContain);
}
break;
case Condetion.StartWith:
method = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
var someValueStartWith = Expression.Constant(fieldItem.Value, columnValue.Type);
e1 = Expression.Call(columnNameProperty, method, someValueStartWith);
break;
case Condetion.EndWith:
method = typeof(string).GetMethod("EndsWith", new[] { typeof(string) });
var someValueEndWith = Expression.Constant(fieldItem.Value, columnValue.Type);
e1 = Expression.Call(columnNameProperty, method, someValueEndWith);
break;
case Condetion.NotContaiens:
if (fieldItem.IsContaien)
{
Type tt = fieldItem.Values.GetType();
if (tt == typeof(List<dynamic>))
{
IEnumerable<dynamic> val = fieldItem.Values.Cast<dynamic>().ToList();
var someValueContain = Expression.Constant(val, val.GetType());
var convertExpression = Expression.Convert(columnNameProperty, typeof(object));
e1 = Expression.Call(someValueContain, "Contains", new Type[] { }, convertExpression);
e1 = Expression.Not(e1);
}
else
{
var mval = fieldItem.Values.AsQueryable().Cast<dynamic>();
var someValueContain = Expression.Constant(mval, mval.GetType());
var convertExpression = Expression.Convert(columnNameProperty, typeof(object));
e1 = Expression.Call((
((Expression<Func<bool>>)
(() => Queryable.Contains(default(IQueryable<dynamic>), default(object))))
.Body as MethodCallExpression).Method,
someValueContain,
convertExpression);
e1 = Expression.Not(e1);
}
}
else
{
method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var someValueContain = Expression.Constant(fieldItem.Value, columnValue.Type);
e1 = Expression.Call(columnNameProperty, method, someValueContain);
e1 = Expression.Not(e1);
}
break;
}
if (combined == null)
{
combined = e1;
}
else
{
combined = Expression.And(combined, e1);
}
}
}
if (combined == null)
{
return null;
}
var mm = Expression.Lambda<Func<T, bool>>(combined, pe);
return mm;//.Compile();
}
catch (Exception ex)
{
Logs.Log(ex);
return null;
}
}
public class WhereCondition
{
public string ColumName { set; get; }
public string Value { set; get; }
public Condetion Cond { set; get; }
public string GridTblName { set; get; }
public IEnumerable<dynamic> Values { set; get; }
public bool IsContaien { set; get; }
public WhereCondition(string columName, string value, Condetion cond)
{
ColumName = columName;
Value = value;
Cond = cond;
}
public WhereCondition()
{
ColumName = "";
Value = "";
Cond = Condetion.Equal;
}
}
public enum Condetion { Equal, Greater, GreaterOrEqual, Lower, LowerOrEqual, NotEqual, Contaiens, NotContaiens, StartWith,EndWith }
We can now call the query like this.
WhereCondition whereCondition = new WhereCondition();
whereCondition.ColumName = "Id";
whereCondition.Cond = Condetion.Equal;
whereCondition.Value = id.ToString();
Expression<Func<T, bool>> whereEx = GetWhereFunc(new List<WhereCondition>(){whereCondition}, GetType());
return (from c in RDaynamicContext.GetTable(tbl)
select c).Where(whereEx).FirstOrDefault();

Related

Order by dynamic parameter

I'm trying to make and order by with dynamic parameter, this is the code:
var res = from c in db.CUSTOMERS select c;
if (!string.IsNullOrEmpty(sortBy) && !string.IsNullOrEmpty(direction))
{
var property = typeof(CUSTOMERS).GetProperty(sortBy);
if(direction == "asc")
{
res = res.OrderBy(x => property.GetValue(x));
}
else
{
res = res.OrderBy(x => property.GetValue(x));
}
}
return res.ToList();
but I get this error :
LINQ to Entities does not recognize the method 'System.Object GetValue(System.Object)' method, and this method cannot be translated into a store expression.
If you want to do sorting on the Server Side, you have to correct expression.
var query = from c in db.CUSTOMERS select c;
if (!string.IsNullOrEmpty(sortBy) && !string.IsNullOrEmpty(direction))
{
query = query.ApplyOrderBy(new [] {Tuple.Create(sortBy, direction != "asc")});
}
return query.ToList();
And implementation:
public static class QueryableExtensions
{
public static IQueryable<T> ApplyOrderBy<T>(this IQueryable<T> query, IEnumerable<Tuple<string, bool>> order)
{
var expr = ApplyOrderBy(typeof(T), query.Expression, order);
return query.Provider.CreateQuery<T>(expr);
}
static Expression MakePropPath(Expression objExpression, string path)
{
return path.Split('.').Aggregate(objExpression, Expression.PropertyOrField);
}
static Expression ApplyOrderBy(Type entityType, Expression queryExpr, IEnumerable<Tuple<string, bool>> order)
{
var param = Expression.Parameter(entityType, "e");
var isFirst = true;
foreach (var tuple in order)
{
var lambda = Expression.Lambda(MakePropPath(param, tuple.Item1), param);
var methodName =
isFirst ? tuple.Item2 ? nameof(Queryable.OrderByDescending) : nameof(Queryable.OrderBy)
: tuple.Item2 ? nameof(Queryable.ThenByDescending) : nameof(Queryable.ThenBy);
queryExpr = Expression.Call(typeof(Queryable), methodName, new[] { entityType, lambda.Body.Type }, queryExpr, lambda);
isFirst = false;
}
return queryExpr;
}
}

How to use dynamically generated OrderBy in LINQ?

Based on API, I can have multiple parameters which can be used in order by. There is a function which creates a dynamic order by parameter as a string. I want to use this in .OrderBy but not sure how to do this.
API Call:
{{url}}?keyword=singer&page=12&size=5&sortby=LastName&sortby=FirstName
Code:
public CallCenterPageResult<CallCenterCustomerSummary> GetCustomers(int page, int pageSize, IEnumerable<SortParameter> sortParameters, string keyword)
{
using (var ctx = new EFCallCenterContext())
{
var customerDetails = ctx.CallCenterCustomers
.Where(ccs => ccs.IsDeleted == false && (ccs.FirstName.Contains(keyword) || ccs.LastName.Contains(keyword) || ccs.Phone.Contains(keyword)))
.OrderBy(sortParameters.ToOrderBy()) // "LastName ASC, FirstName ASC"
.Skip(pageSize * (page - 1)).Take(pageSize)
.ToList();
return customerDetails;
}
}
Extension Method to get order by:
static class RepositoryExtensions
{
public static string ToOrderBy(this IEnumerable<SortParameter> parameters)
{
return string.Join(", ", parameters.Select(p => p.SortBy + (p.Descending ? " DESC" : " ASC")));
}
}
Output:
"LastName ASC, FirstName ASC"
Extension method to accept dynamic LINQ:
public static class Extension
{
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderBy");
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "OrderByDescending");
}
public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenBy");
}
public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> source, string property)
{
return ApplyOrder<T>(source, property, "ThenByDescending");
}
static IOrderedQueryable<T> ApplyOrder<T>(IQueryable<T> source, string property, string methodName)
{
var props = property.Split('.');
var type = typeof(T);
var arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (string prop in props)
{
// use reflection (not ComponentModel) to mirror LINQ
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi); // Errors out here.
type = pi.PropertyType;
}
var delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
var lambda = Expression.Lambda(delegateType, expr, arg);
var result = typeof(Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof(T), type)
.Invoke(null, new object[] { source, lambda });
return (IOrderedQueryable<T>)result;
}
}
Error:
System.ArgumentNullException: Value cannot be null.
Parameter name: property
Scrren shot:
This is the first time working with this complex query so not sure how to do this. I can add more info if needed.
It looks like the error occurs because pi is null. And it is null because, I would assume, the class standing in for the T generic doesn't have a property named LastName ASC, FirstName ASC. I would try something like the following:
var props = property.Split(",");
... //this code stays the same
foreach(string prop in props) {
var propNameAndDirection = prop.Split(" ");
PropertyInfo pi = type.GetProperty(propNameAndDirection[0]);
... //continue as necessary, using propNameAndDirection[1]
... //to decide OrderBy or OrderByDesc call
Hopefully this sets you in the right direction.
After some trial and error, I am able to find an answer.
Tested with followings:
.OrderBy("LastName ASC, FirstName ASC")
.OrderBy("LastName ASC")
.OrderBy("LastName ASC,FirstName DESC")
Linq:
public CallCenterPageResult<CallCenterCustomerSummary> GetCustomers(int page, int pageSize, IEnumerable<SortParameter> sortParameters, string keyword)
{
using (var ctx = new EFCallCenterContext())
{
var customerDetails = ctx.CallCenterCustomers
.Where(ccs => ccs.IsDeleted == false && (ccs.FirstName.Contains(keyword) || ccs.LastName.Contains(keyword) || ccs.Phone.Contains(keyword)))
.OrderBy(o => o.Equals(sortParameters.ToOrderBy()))
.Skip(pageSize * (page - 1)).Take(pageSize)
.ToList();
return customerDetails;
}
}
Helper Class:
public static class OrderByHelper
{
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> enumerable, string orderBy)
{
return enumerable.AsQueryable().OrderBy(orderBy).AsEnumerable();
}
public static IQueryable<T> OrderBy<T>(this IQueryable<T> collection, string orderBy)
{
foreach (var orderByInfo in ParseOrderBy(orderBy))
{
collection = ApplyOrderBy(collection, orderByInfo);
}
return collection;
}
private static IQueryable<T> ApplyOrderBy<T>(IQueryable<T> collection, OrderByInfo orderByInfo)
{
var props = orderByInfo.PropertyName.Split('.');
var type = typeof (T);
var arg = Expression.Parameter(type, "x");
Expression expr = arg;
foreach (var prop in props)
{
var pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
var delegateType = typeof (Func<,>).MakeGenericType(typeof (T), type);
var lambda = Expression.Lambda(delegateType, expr, arg);
string methodName;
if (!orderByInfo.Initial && collection is IOrderedQueryable<T>)
{
methodName = orderByInfo.Direction == SortDirection.Ascending ? "ThenBy" : "ThenByDescending";
}
else
{
methodName = orderByInfo.Direction == SortDirection.Ascending ? "OrderBy" : "OrderByDescending";
}
return (IOrderedQueryable<T>) typeof (Queryable).GetMethods().Single(
method => method.Name == methodName
&& method.IsGenericMethodDefinition
&& method.GetGenericArguments().Length == 2
&& method.GetParameters().Length == 2)
.MakeGenericMethod(typeof (T), type)
.Invoke(null, new object[] {collection, lambda});
}
private static IEnumerable<OrderByInfo> ParseOrderBy(string orderBy)
{
if (string.IsNullOrEmpty(orderBy))
{
yield break;
}
var items = orderBy.Split(',');
var initial = true;
foreach (var item in items)
{
var pair = item.Trim().Split(' ');
if (pair.Length > 2)
{
throw new ArgumentException(
$"Invalid OrderBy string '{item}'. Order By Format: Property, Property2 ASC, Property2 DESC");
}
var prop = pair[0].Trim();
if (string.IsNullOrEmpty(prop))
{
throw new ArgumentException(
"Invalid Property. Order By Format: Property, Property2 ASC, Property2 DESC");
}
var dir = SortDirection.Ascending;
if (pair.Length == 2)
{
dir = "desc".Equals(pair[1].Trim(), StringComparison.OrdinalIgnoreCase)
? SortDirection.Descending
: SortDirection.Ascending;
}
yield return new OrderByInfo {PropertyName = prop, Direction = dir, Initial = initial};
initial = false;
}
}
private class OrderByInfo
{
public string PropertyName { get; set; }
public SortDirection Direction { get; set; }
public bool Initial { get; set; }
}
private enum SortDirection
{
Ascending = 0,
Descending = 1
}
Referances:
Dynamic LINQ OrderBy on IEnumerable<T>
http://aonnull.blogspot.com/2010/08/dynamic-sql-like-linq-orderby-extension.html
If I understood the problem correctly.
Expression<Func<TEntity, TKey>> genericParameter = null;
genericParameter = x => x.foo;
var customerDetails = ctx.CallCenterCustomers
.Where(ccs => ccs.IsDeleted == false && (ccs.FirstName.Contains(keyword) || ccs.LastName.Contains(keyword) || ccs.Phone.Contains(keyword)))
.OrderBy(genericParameter)

Linq orderbys from collection [duplicate]

This question already has answers here:
Dynamic LINQ OrderBy on IEnumerable<T> / IQueryable<T>
(24 answers)
Closed 2 years ago.
How do I specify the argument passed to orderby using a value I take as a parameter?
Ex:
List<Student> existingStudends = new List<Student>{ new Student {...}, new Student {...}}
Currently implementation:
List<Student> orderbyAddress = existingStudends.OrderBy(c => c.Address).ToList();
Instead of c.Address, how can I take that as a parameter?
Example
string param = "City";
List<Student> orderbyAddress = existingStudends.OrderByDescending(c => param).ToList();
You can use a little bit of reflection to construct the expression tree as follows (this is an extension method):
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty,
bool desc)
{
string command = desc ? "OrderByDescending" : "OrderBy";
var type = typeof(TEntity);
var property = type.GetProperty(orderByProperty);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExpression = Expression.Lambda(propertyAccess, parameter);
var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
source.Expression, Expression.Quote(orderByExpression));
return source.Provider.CreateQuery<TEntity>(resultExpression);
}
orderByProperty is the Property name you want to order by and if pass true as parameter for desc, will sort in descending order; otherwise, will sort in ascending order.
Now you should be able to do existingStudents.OrderBy("City",true); or existingStudents.OrderBy("City",false);
Here's a possiblity using reflection...
var param = "Address";
var propertyInfo = typeof(Student).GetProperty(param);
var orderByAddress = items.OrderBy(x => propertyInfo.GetValue(x, null));
To expand on the answer by #Icarus: if you want the return type of the extension method to be an IOrderedQueryable instead of an IQueryable, you can simply cast the result as follows:
public static IOrderedQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty, bool desc)
{
string command = desc ? "OrderByDescending" : "OrderBy";
var type = typeof(TEntity);
var property = type.GetProperty(orderByProperty);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExpression = Expression.Lambda(propertyAccess, parameter);
var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
source.Expression, Expression.Quote(orderByExpression));
return (IOrderedQueryable<TEntity>)source.Provider.CreateQuery<TEntity>(resultExpression);
}
1) Install System.Linq.Dynamic
2) Add the following code
public static class OrderUtils
{
public static string ToStringForOrdering<T, TKey>(this Expression<Func<T, TKey>> expression, bool isDesc = false)
{
var str = expression.Body.ToString();
var param = expression.Parameters.First().Name;
str = str.Replace("Convert(", "(").Replace(param + ".", "");
return str + (isDesc ? " descending" : "");
}
}
3) Write your switch for selecting of Lambda function
public static class SortHelper
{
public static Expression<Func<UserApp, object>> UserApp(string orderProperty)
{
orderProperty = orderProperty?.ToLowerInvariant();
switch (orderProperty)
{
case "firstname":
return x => x.PersonalInfo.FirstName;
case "lastname":
return x => x.PersonalInfo.LastName;
case "fullname":
return x => x.PersonalInfo.FirstName + x.PersonalInfo.LastName;
case "email":
return x => x.Email;
}
}
}
4) Use your helpers
Dbset.OrderBy(SortHelper.UserApp("firstname").ToStringForOrdering())
5) You can use it with pagging (PagedList)
public virtual IPagedList<T> GetPage<TOrder>(Page page, Expression<Func<T, bool>> where, Expression<Func<T, TOrder>> order, bool isDesc = false,
params Expression<Func<T, object>>[] includes)
{
var orderedQueryable = Dbset.OrderBy(order.ToStringForOrdering(isDesc));
var query = orderedQueryable.Where(where).GetPage(page);
query = AppendIncludes(query, includes);
var results = query.ToList();
var total = Dbset.Count(where);
return new StaticPagedList<T>(results, page.PageNumber, page.PageSize, total);
}
Explanation
System.Linq.Dynamic allows us to set string value in OrderBy method. But inside this extension the string will be parsed to Lambda. So I thought it would work if we will parse Lambda to string and give it to OrderBy method. And it works!
Here's something I came up with for dealing with a conditional Descending. You could combine this with other methods of generating the keySelector func dynamically.
public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(this IQueryable<TSource> source,
System.Linq.Expressions.Expression<Func<TSource, TKey>> keySelector,
System.ComponentModel.ListSortDirection sortOrder
)
{
if (sortOrder == System.ComponentModel.ListSortDirection.Ascending)
return source.OrderBy(keySelector);
else
return source.OrderByDescending(keySelector);
}
Usage:
//imagine this is some parameter
var direction = System.ComponentModel.ListSortDirection.Ascending;
query = query.OrderBy(ec => ec.MyColumnName, direction);
Notice this allows you to chain this .OrderBy extension with a new parameter onto any IQueryable.
// perhaps passed in as a request of user to change sort order
// var direction = System.ComponentModel.ListSortDirection.Ascending;
query = context.Orders
.Where(o => o.Status == OrderStatus.Paid)
.OrderBy(ec => ec.OrderPaidUtc, direction);
private Func<T, object> GetOrderByExpression<T>(string sortColumn)
{
Func<T, object> orderByExpr = null;
if (!String.IsNullOrEmpty(sortColumn))
{
Type sponsorResultType = typeof(T);
if (sponsorResultType.GetProperties().Any(prop => prop.Name == sortColumn))
{
System.Reflection.PropertyInfo pinfo = sponsorResultType.GetProperty(sortColumn);
orderByExpr = (data => pinfo.GetValue(data, null));
}
}
return orderByExpr;
}
public List<T> OrderByDir<T>(IEnumerable<T> source, string dir, Func<T, object> OrderByColumn)
{
return dir.ToUpper() == "ASC" ? source.OrderBy(OrderByColumn).ToList() : source.OrderByDescending(OrderByColumn).ToList();``
}
// Call the code like below
var orderByExpression= GetOrderByExpression<SearchResultsType>(sort);
var data = OrderByDir<SponsorSearchResults>(resultRecords, SortDirectionString, orderByExpression);
This doesn't let you pass a string, as you asked for in your question, but it might still work for you.
The OrderByDescending method takes a Func<TSource, TKey>, so you can rewrite your function this way:
List<Student> QueryStudents<TKey>(Func<Student, TKey> orderBy)
{
return existingStudents.OrderByDescending(orderBy).ToList();
}
There are other overloads for OrderByDescending as well that take a Expression<Func<TSource, TKey>>, and/or a IComparer<TKey>. You could also look into those and see if they provide you anything of use.
The only solution that worked for me was posted here https://gist.github.com/neoGeneva/1878868 by neoGeneva.
I will re-post his code because it works well and I wouldn't want it to be lost in the interwebs!
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string sortExpression)
{
if (source == null)
throw new ArgumentNullException("source", "source is null.");
if (string.IsNullOrEmpty(sortExpression))
throw new ArgumentException("sortExpression is null or empty.", "sortExpression");
var parts = sortExpression.Split(' ');
var isDescending = false;
var propertyName = "";
var tType = typeof(T);
if (parts.Length > 0 && parts[0] != "")
{
propertyName = parts[0];
if (parts.Length > 1)
{
isDescending = parts[1].ToLower().Contains("esc");
}
PropertyInfo prop = tType.GetProperty(propertyName);
if (prop == null)
{
throw new ArgumentException(string.Format("No property '{0}' on type '{1}'", propertyName, tType.Name));
}
var funcType = typeof(Func<,>)
.MakeGenericType(tType, prop.PropertyType);
var lambdaBuilder = typeof(Expression)
.GetMethods()
.First(x => x.Name == "Lambda" && x.ContainsGenericParameters && x.GetParameters().Length == 2)
.MakeGenericMethod(funcType);
var parameter = Expression.Parameter(tType);
var propExpress = Expression.Property(parameter, prop);
var sortLambda = lambdaBuilder
.Invoke(null, new object[] { propExpress, new ParameterExpression[] { parameter } });
var sorter = typeof(Queryable)
.GetMethods()
.FirstOrDefault(x => x.Name == (isDescending ? "OrderByDescending" : "OrderBy") && x.GetParameters().Length == 2)
.MakeGenericMethod(new[] { tType, prop.PropertyType });
return (IQueryable<T>)sorter
.Invoke(null, new object[] { source, sortLambda });
}
return source;
}
Add the nugget package Dynamite to your code
Add the namespace Dynamite.Extensions
Eg : using Dynamite.Extensions;
Give Order by query like any SQL query
Eg : students.OrderBy(" City DESC, Address").ToList();
To extend the response of #Icarus: if you want to sort by two fields I could perform the following function (for one field the response of Icarius works very well).
public static IQueryable<T> OrderByDynamic<T>(this IQueryable<T> q, string SortField1, string SortField2, bool Ascending)
{
var param = Expression.Parameter(typeof(T), "p");
var body = GetBodyExp(SortField1, SortField2, param);
var exp = Expression.Lambda(body, param);
string method = Ascending ? "OrderBy" : "OrderByDescending";
Type[] types = new Type[] { q.ElementType, exp.Body.Type };
var mce = Expression.Call(typeof(Queryable), method, types, q.Expression, exp);
return q.Provider.CreateQuery<T>(mce);
}
This is the function that the body returns for the lambda expression, it works with string and int, but it is enough to add more types to make it work according to the need of each programmer
public static NewExpression GetBodyExp(string field1, string field2, ParameterExpression Parametro)
{
// SE OBTIENE LOS NOMBRES DE LOS TIPOS DE VARIABLE
string TypeName1 = Expression.Property(Parametro, field1).Type.Name;
string TypeName2 = Expression.Property(Parametro, field2).Type.Name;
// SE DECLARA EL TIPO ANONIMO SEGUN LOS TIPOS DE VARIABLES
Type TypeAnonymous = null;
if (TypeName1 == "String")
{
string var1 = "0";
if (TypeName2 == "Int32")
{
int var2 = 0;
var example = new { var1, var2 };
TypeAnonymous = example.GetType();
}
if (TypeName2 == "String")
{
string var2 = "0";
var example = new { var1, var2 };
TypeAnonymous = example.GetType();
}
}
if (TypeName1 == "Int32")
{
int var1 = 0;
if (TypeName2 == "Int32")
{
string var2 = "0";
var example = new { var1, var2 };
TypeAnonymous = example.GetType();
}
if (TypeName2 == "String")
{
string var2 = "0";
var example = new { var1, var2 };
TypeAnonymous = example.GetType();
}
}
//se declaran los TIPOS NECESARIOS PARA GENERAR EL BODY DE LA EXPRESION LAMBDA
MemberExpression[] args = new[] { Expression.PropertyOrField(Parametro, field1), Expression.PropertyOrField(Parametro, field2) };
ConstructorInfo CInfo = TypeAnonymous.GetConstructors()[0];
IEnumerable<MemberInfo> a = TypeAnonymous.GetMembers().Where(m => m.MemberType == MemberTypes.Property);
//BODY
NewExpression body = Expression.New(CInfo, args, TypeAnonymous.GetMembers().Where(m => m.MemberType == MemberTypes.Property));
return body;
}
to use it the following is done
IQueryable<MyClass> IqMyClass= context.MyClass.AsQueryable();
List<MyClass> ListMyClass= IqMyClass.OrderByDynamic("UserName", "IdMyClass", true).ToList();
if there is a better way to do this, it would be great if they share it
I managed to solve it thanks to: How can I make a Multiple property lambda expression with Linq
New Answer : this is a more complete answer that supports multiple columns for order by like SQL. Example : .OrderBy("FirstName,Age DESC") :
namespace Utility;
public static class QueryExtension
{
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByProperty, bool desc, bool isThenBy = false)
{
string command = isThenBy ? (desc ? "ThenByDescending" : "ThenBy") : (desc ? "OrderByDescending" : "OrderBy");
var type = typeof(TEntity);
var property = type.GetProperty(orderByProperty);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExpression = Expression.Lambda(propertyAccess, parameter);
var resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType },
source.Expression, Expression.Quote(orderByExpression));
return source.Provider.CreateQuery<TEntity>(resultExpression);
}
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string sqlOrderByList)
{
var ordebyItems = sqlOrderByList.Trim().Split(',');
IQueryable<TEntity> result = source;
bool useThenBy = false;
foreach (var item in ordebyItems)
{
var splt = item.Trim().Split(' ');
result = result.OrderBy(splt[0].Trim(), (splt.Length > 1 && splt[1].Trim().ToLower() == "desc"), useThenBy);
if (useThenBy)
useThenBy = true;
}
return result;
}
}
The second function iterates over orderby columns and uses the first one.
Use it like this :
using Utility;
...
public void MyMethod()
{
var query = _dbContext.Person.AsQueryable();
query.OrderBy("FirstName,Age DESC");
}
I'm way late to the party but none of these solutions worked for me. I was eager to try System.Linq.Dynamic, but I couldn't find that on Nuget, maybe depreciated? Either way...
Here is a solutions I came up with. I needed to dynamically use a mixture of OrderBy, OrderByDescending and OrderBy > ThenBy.
I simply created an extension method for my list object, a bit hacky I know... I wouldn't recommend this if it were something I was doing a lot of, but it's good for a one off.
List<Employee> Employees = GetAllEmployees();
foreach(Employee oEmployee in Employees.ApplyDynamicSort(eEmployeeSort))
{
//do stuff
}
public static IOrderedEnumerable<Employee> ApplyDynamicSort(this List<Employee> lEmployees, Enums.EmployeeSort eEmployeeSort)
{
switch (eEmployeeSort)
{
case Enums.EmployeeSort.Name_ASC:
return lEmployees.OrderBy(x => x.Name);
case Enums.EmployeeSort.Name_DESC:
return lEmployees.OrderByDescending(x => x.Name);
case Enums.EmployeeSort.Department_ASC_Salary_DESC:
return lEmployees.OrderBy(x => x.Department).ThenByDescending(y => y.Salary);
default:
return lEmployees.OrderBy(x => x.Name);
}
}

LINQ Query to check for a predicate in all columns in a table

I have a table with 30 columns,
and it contains 1000 rows.
I want a single LINQ query, which checks for a particular value in all columns and converts the result into a list.
For example:
table.where(allcolumnvalue.contains(searchvalue)).Tolist()
How to accomplish the above using one LINQ query. Any help is much appreciated.
For your request all of fields should have same type, at least in the static typed C#.
The method Queriable.Where gets the Expression<Func<T, bool>> predicate as parameter. So you need build the predicate o.p1 == val || o.p2 == val || o.p3 = val ... as Expression value. Here o is a parameter of Expression<Func<T, bool>>:
public Expression BuildExpression<TObj, TVal>(TObj obj, TVal val)
{
Expression<Func<TObj, bool>> predicate = (o) => o.p1 == val || ... || o.pN == val;
return predicate;
}
but we need build predicate dynamically for all properties of TObj that have type TVal.
To simplify the code we will build equal expression false || o.p1 == val || ... || o.pN == val.
public Expression<Func<TObj, bool>> BuildExpression<TObj, TVal>(TVal val)
{
var parameter = Expression.Parameter(typeof(TObj), "o");
var valExpression = Expression.Constant(val, typeof(TVal));
var body = Expression.Constant(false, typeof(bool));
var properties = typeof(TObj).GetProperties()
.Where(p => p.PropertyType == typeof(TVal));
foreach (var property in properties)
{
var propertyExpression = Expression.Property(parameter, property);
var equalExpression = Expression.Equal(propertyExpression, valExpression);
body = Expression.Or(body, equalExpression);
}
return Expression.Lambda<Func<TObj, bool>>(body, parameter);
}
. . .
using (var dbContext = new DbContext())
{
var whereExpression = BuildExpression<User, string>("foo");
var contaningsFoo = dbContext.Users.Where(whereExpression);
}
I got answer But Is not perfect answer But Is Worked well
public class GenericList<T>
{
void Add(T input) { }
public List<T> SerachFun(List<T> input, string search)
{
List<T> output = new System.Collections.Generic.List<T>();
foreach (var aa in input)
{
var columns = aa.GetType().GetProperties().ToList();
foreach (var bb in columns)
{
var cccc = bb.GetValue(aa);
bool result = cccc.ToString().Contains(search);
if (result)
{
output.Add(aa);
continue;
}
}
}
return output;
}
}
The Generic Class Object Created
public GenericList<table1> g = new GenericList<table1>();
the Generic Class Method Called :
var tabledetails=db.table1.ToList();
var resultcommonsearch = g.SerachFun(tabledetails, "Dhoni");
using code
public class GenericList<T>
{
public List<T> SerachFun(List<T> input, string search)
{
List<T> output = new System.Collections.Generic.List<T>();
foreach (var aa in input)
{
var columns = aa.GetType().GetProperties().ToList();
foreach (var bb in columns)
{
var cccc = bb.GetValue(aa);
if(cccc!=null)
{
bool result = cccc.ToString().Contains(search);
if (result)
{
output.Add(aa);
continue;
}
}
}
}
return output;
}
}
Try call method
public GenericList<table1> g = new GenericList<table1>();
var tabledetails=db.table1.ToList();
var resultcommonsearch = g.SerachFun(tabledetails, "Dhoni");

linq expression for dates filtered on Day, Month and Year

No data found using EQUAL? because of time difference?
Can someone show me how to create valid expression for date?
Table
UserId Username DateCreated
466 rebecca.manning#yahoo.com 5/10/1980 5:18:18 AM
467 lauren.oliver#yahoo.com 5/10/1980 5:18:18 AM
502 felicity.stewart#yahoo.com 5/10/1980 5:18:18 AM
534 sebastian.dowd#yahoo.com 5/10/1980 5:18:18 AM
535 nicola.howard#yahoo.com 5/10/1980 5:18:18 AM
570 evan.hodges#yahoo.com 4/10/1980 5:18:18 AM
607 nathan.henderson#yahoo.com 4/10/1980 4:17:17 AM
608 phil.vaughan#yahoo.com 4/10/1980 4:17:17 AM
642 piers.skinner#yahoo.com 4/10/1980 4:17:17 AM
643 julia.ferguson#yahoo.com 4/10/1980 4:17:17 AM
How can I create linq expression for dates filtered on Day, Month and Year only
using Linq Expression Builder?
I have a class "TableExpression.cs" which handles all filters "Where fieldname..", "Orders"
Instead of filtering
new Filters{FieldName="DateCreated",FieldValue= new DateTime(1980,10,10),LinqExprOpr = LinqExprOperator.LessThanOrEqual,LinqExprSrt = LinqExprSort.OrderBy,IsSortedField = false},
new Filters{FieldName="DateCreated",FieldValue= new DateTime(1979,10,10),LinqExprOpr = LinqExprOperator.GreaterThanOrEqual,LinqExprSrt = LinqExprSort.ThenBy,IsSortedField = false}
I was hoping to filter it using equal(=) operator.
new Filters{FieldName="DateCreated",FieldValue= new DateTime(1980,10,10),LinqExprOpr = **LinqExprOperator.Equal**,LinqExprSrt = LinqExprSort.OrderBy,IsSortedField = false},
My Dynamic Expression Builder Class
enter code here
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Common;
namespace DataAccess.Config.TableExpression
{
public static class ExressionBuilderV3
{
public static IQueryable<T> CreateExpressions<T>(IList<Filters> _filters, IQueryable<T> source)
{
Type ParmType = typeof(T);
ParameterExpression ParmExpr = Expression.Parameter(ParmType, ParmType.Name);
Expression ListOfExpr = null;
// Condition Query
foreach (var f in _filters)
{
PropertyInfo pInfo = ParmType.GetProperties().Where(_ => _.Name == f.FieldName).FirstOrDefault();
Type FieldType = Type.GetType(pInfo.PropertyType.FullName);
if (ListOfExpr == null)
{
ListOfExpr = AddOperatorExpression(ParmExpr, f.FieldName, f.FieldValue, FieldType, f.LinqExprOpr);
}
else
{
ListOfExpr = AddBitWiseExpression(ListOfExpr,
AddOperatorExpression(ParmExpr, f.FieldName, f.FieldValue, FieldType, f.LinqExprOpr)
, f.LinqExprBtw);
}
}
// Sort Query
Expression<Func<T, bool>> ExprTotal = Expression.Lambda<Func<T, bool>>(ListOfExpr, ParmExpr);
source = source.Where(ExprTotal);
ListOfExpr = null;
foreach (var f in _filters)
{
PropertyInfo pInfo = ParmType.GetProperties().Where(_ => _.Name == f.FieldName).FirstOrDefault();
Type FieldType = pInfo.PropertyType;
//*****************************************************************************************************
if (f.IsSortedField)
{
source = Sorting(source, ParmType, f.FieldName, f.LinqExprSrt);
}
//*****************************************************************************************************
}
Filters filter = _filters.SingleOrDefault(_ => _.IsSortedField == true && _.PageSize > 0);
if (filter != null)
{
source = source.Skip((filter.PageIndex -1) * filter.PageSize ).Take(filter.PageSize);
}
return source;
}
private static Expression AddBitWiseExpression(Expression _Expression1, Expression _Expression2, LinqExprBitWise _LinqExprBitWise)
{
Expression RtrnExpression = null;
switch (_LinqExprBitWise)
{
case LinqExprBitWise.Or:
RtrnExpression = Expression.Or(_Expression1, _Expression2);
break;
case LinqExprBitWise.AndAlso:
RtrnExpression = Expression.AndAlso(_Expression1, _Expression2);
break;
}
return RtrnExpression;
}
private static Expression AddOperatorExpression(ParameterExpression _ParmExpr, String _FieldName, object _FieldValue, Type _typeOf, LinqExprOperator _operator)
{
Expression FieldName = Expression.Property(_ParmExpr, _FieldName);
Expression FieldValue = Expression.Constant(_FieldValue, _typeOf);
Expression rtrnExpr = null;
#region
switch (_operator)
{
case LinqExprOperator.Equals:
rtrnExpr = Expression.Equal(FieldName, FieldValue);
break;
case LinqExprOperator.GreaterThan:
rtrnExpr = Expression.GreaterThan(FieldName, FieldValue);
break;
case LinqExprOperator.GreaterThanOrEqual:
rtrnExpr = Expression.GreaterThanOrEqual(FieldName, FieldValue);
break;
case LinqExprOperator.LessThan:
rtrnExpr = Expression.LessThan(FieldName, FieldValue);
break;
case LinqExprOperator.LessThanOrEqual:
rtrnExpr = Expression.LessThanOrEqual(FieldName, FieldValue);
break;
case LinqExprOperator.Contains:
rtrnExpr = Expression.Call(FieldName, typeof(string).GetMethod("Contains"), FieldValue);
break;
case LinqExprOperator.StartsWith:
rtrnExpr = Expression.Call(FieldName, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }), FieldValue);
break;
case LinqExprOperator.EndsWith:
rtrnExpr = Expression.Call(FieldName, typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }), FieldValue);
break;
}
#endregion
return rtrnExpr;
}
private static IQueryable<T> Sorting<T>(IQueryable<T> _Source, Type _ParmType, String _FieldName, LinqExprSort _LinqExprSort)
{
string orderMethodName = "";
switch (_LinqExprSort)
{
case LinqExprSort.OrderBy:
orderMethodName = "OrderBy";
break;
case LinqExprSort.OrderByDescending:
orderMethodName = "OrderByDescending";
break;
case LinqExprSort.ThenBy:
orderMethodName = "ThenBy";
break;
case LinqExprSort.ThenByDescending:
orderMethodName = "ThenByDescending";
break;
}
var param = Expression.Parameter(_ParmType);
var property = _ParmType.GetProperty(_FieldName);
return _Source.Provider.CreateQuery<T>(
Expression.Call(
typeof(Queryable),
orderMethodName,
new Type[] { _ParmType , property.PropertyType },
_Source.Expression,
Expression.Quote(
Expression.Lambda(
Expression.Property(param, property),
param))
));
}
/***************************************************************************/
}
}
What you need is a transformation method in this case (Func<DateTime, DateTime>)(x =>{ return x.Date;}) to give you the Date portion only. If you look at my example below you will see that when ever I have a transformation method I use Method.Call with the transformation method and the appropriate expression as input. Doing this allows any other type of object to be transformed prior to comparing.
void static Main()
{
var ops = new List<Ops>
{
/*new Ops
{
OperandType = typeof(string),
OpType=OpType.Equals,
OperandName = "Name",
ValueToCompare = "MM"
},
new Ops
{
OperandType = typeof(string),
OpType=OpType.Equals,
OperandName = "ID",
ValueToCompare = 1
},*/
new Ops
{
OperandType = typeof(DateTime),
OpType=OpType.Equals,
OperandName = "Date",
ValueToCompare = new DateTime(2014,12,1),
//key is this transformation method
TransformationMethod = ((Func<DateTime, DateTime>)(x =>{ return x.Date;})).Method
},
};
var testClasses = new List<TestClass>
{
new TestClass { ID =1, Name = "MM", Date = new DateTime(2014,12,1,2,32,10)},
new TestClass { ID =2, Name = "BB", Date = new DateTime(2014,12,2)}
};
var funct = ExpressionBuilder.BuildExpressions<TestClass>(ops);
foreach(var item in testClasses.Where(funct))
{
Console.WriteLine("ID " +item.ID);
Console.WriteLine("Name " +item.Name);
Console.WriteLine("Date" + item.Date);
}
}
// Define other methods and classes here
public enum OpType
{
Equals
}
public class Ops
{
public Type OperandType {get; set;}
public OpType OpType {get; set;}
public string OperandName {get;set;}
public object ValueToCompare {get;set;}
public MethodInfo TransformationMethod{get;set;}
}
public class TestClass
{
public int ID {get;set;}
public string Name {get; set;}
public DateTime Date {get;set;}
}
public class ExpressionBuilder
{
public static Func<T,bool> BuildExpressions<T>( List<Ops> opList)
{
Expression currentExpression= null;
var parameter = Expression.Parameter(typeof(T), "prop");
for(int i =0; i< opList.Count; i++)
{
var op = opList[i];
Expression innerExpression = null;
switch(op.OpType)
{
case OpType.Equals :
{
var innerParameter = Expression.Property(parameter,op.OperandName);
//Console.WriteLine(innerParameter);
var ConstExpression = Expression.Constant(op.ValueToCompare);
//Console.WriteLine(ConstExpression);
if(op.TransformationMethod != null)
{
innerExpression = Expression.Equal(Expression.Call(op.TransformationMethod, new []{innerParameter}),
Expression.Call(op.TransformationMethod, new []{ConstExpression}));
//Console.WriteLine(innerExpression);
}
else
innerExpression = Expression.Equal(innerParameter, ConstExpression);
break;
}
}
if (i >0)
{
currentExpression = Expression.And(currentExpression, innerExpression);
}
else
{
currentExpression = innerExpression;
}
}
var lambdaExpression = Expression.Lambda<Func<T,bool>>(currentExpression, new []{parameter});
//Console.WriteLine(lambdaExpression);
return lambdaExpression.Compile() ;
}
}

Categories

Resources