Linq select with column name - c#

I want to select with column name from table with LINQ
Basically I want select only single column records from a table with Column Name
this is what i have used so far
public static IQueryable<T> SelectByField<T>(this IQueryable<T> q, string ColumnName)
{
var param = Expression.Parameter(typeof(T), "p");
var prop = Expression.Property(param, ColumnName);
var exp = Expression.Lambda(prop, param);
string method = "Select";
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);
}
I am getting following error
Unable to cast object of type 'System.Data.Entity.Infrastructure.DbQuery1[System.String]' to type 'System.Linq.IQueryable1'.
I tried above method with reference to following method
public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string SortField, bool Ascending)
{
var param = Expression.Parameter(typeof(T), "p");
var prop = Expression.Property(param, SortField);
var exp = Expression.Lambda(prop, 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);
}

you can use dynamic library which will allow you to build a dynamic where clause with Linq
check these links:
Link to install the dynamic library
Scott Gu examples
regards

Related

No generic method 'ThenBy' on type 'System.Linq.Queryable'

I'm trying to create a generic OrderBy(fields collection) method to use in a new EF .Net Core 2.1 and managed to write the below code:
public static Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> GetOrderByFunction(List<SortColumn> columns)
{
Type typeQueryable = typeof(IQueryable<TEntity>);
ParameterExpression argQueryable = Expression.Parameter(typeQueryable, "p");
LambdaExpression outerExpression = Expression.Lambda(argQueryable, argQueryable);
Type entityType = typeof(TEntity);
ParameterExpression arg = Expression.Parameter(entityType, "x");
Expression expr = arg;
Expression resultExp = null;
foreach (SortColumn sc in columns)
{
PropertyInfo pi = entityType.GetProperty(sc.FieldName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
Expression propertyExpr = Expression.Property(expr, pi);
Type propertyType = pi.PropertyType;
LambdaExpression lambdaExp = Expression.Lambda(propertyExpr, arg);
String methodName = string.Empty;
if (resultExp != null)
{
methodName = sc.Descending ? "ThenBy" : "ThenByDescending";
/// No generic method 'ThenBy' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments.
/// No type arguments should be provided if the method is non - generic.
Expression exp = Expression.Call(typeof(Queryable), methodName, new Type[] { entityType, propertyType }, outerExpression.Body, Expression.Quote(lambdaExp));
MethodInfo minfo = typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(m => m.Name == methodName);
/// Method System.Linq.IOrderedQueryable`1
/// [TSource] OrderBy[TSource,TKey](System.Linq.IQueryable`1
/// [TSource],System.Linq.Expressions.Expression`1
/// [System.Func`2[TSource,TKey]]) is a generic method definition
/// Parameter name: method
resultExp = Expression.Call(minfo, exp, resultExp);
}
else
{
methodName = sc.Descending ? "OrderBy" : "OrderByDescending";
resultExp = Expression.Call(typeof(Queryable), methodName, new Type[] { entityType, propertyType }, outerExpression.Body, Expression.Quote(lambdaExp));
}
}
LambdaExpression orderedLambda = Expression.Lambda(resultExp, argQueryable);
return (Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>)orderedLambda.Compile();
}
My Sortcolumn class just hold the field name and order direction.
public class SortColumn
{
public string FieldName { get; }
public bool Descending { get; }
public SortColumn(string fieldName, bool descending)
{
FieldName = fieldName;
Descending = descending;
}
}
It works when my collection has only a single item like Id or Description but fails to sort by more than one field.
The errors I got are commented in the snippet:
No generic method 'ThenBy' on type 'System.Linq.Queryable' is compatible with the supplied type arguments
and arguments.
No type arguments should be provided if the method is non - generic.
It's strange because it works fine for the OrderBy command.
But even if I pass the OrderBy I was unable to "join" the methods to assemble a unique expression. The error I got is:
Method System.Linq.IOrderedQueryable'1 [TSource]
OrderBy[TSource,TKey](System.Linq.IQueryable'1
[TSource],System.Linq.Expressions.Expression'1
[System.Func'2[TSource,TKey]]) is a generic method definition
Parameter name: method
Things I tried:
Change typeof(Queryable) to use Enumerable or EnumerableQuery or even List, Use invoke, etc.
My goal is to dynamicaly pass a list of fields to order my GetAll() methods for my domain objects building a delegate function can give me something like:
var test = new List<TEntity>();
test.OrderBy(x => x.Id).ThenByDescending(x => x.Description);
I feel you are overcomplicating everything:
// Or IEnumerable<SortColumn> columns
public static Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> GetOrderByFunction<TEntity>(params SortColumn[] columns)
{
Type typeQueryable = typeof(IQueryable<TEntity>);
ParameterExpression argQueryable = Expression.Parameter(typeQueryable, "p");
Type entityType = typeof(TEntity);
ParameterExpression arg = Expression.Parameter(entityType, "x");
Expression resultExp = argQueryable;
bool first = true;
foreach (SortColumn sc in columns)
{
PropertyInfo pi = entityType.GetProperty(sc.FieldName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
Expression propertyExpr = Expression.Property(arg, pi);
Type propertyType = pi.PropertyType;
LambdaExpression lambdaExp = Expression.Lambda(propertyExpr, arg);
string methodName;
if (first)
{
first = false;
methodName = sc.Descending ? "OrderBy" : "OrderByDescending";
}
else
{
methodName = sc.Descending ? "ThenBy" : "ThenByDescending";
}
resultExp = Expression.Call(typeof(Queryable), methodName, new Type[] { entityType, propertyType }, resultExp, Expression.Quote(lambdaExp));
}
// Case empty columns: simply append a .OrderBy(x => true)
if (first)
{
LambdaExpression lambdaExp = Expression.Lambda(Expression.Constant(true), arg);
resultExp = Expression.Call(typeof(Queryable), "OrderBy", new Type[] { entityType, typeof(bool) }, resultExp, Expression.Quote(lambdaExp));
}
LambdaExpression orderedLambda = Expression.Lambda(resultExp, argQueryable);
return (Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>)orderedLambda.Compile();
}
I'll add that calling FieldName what is really a PropertyName is a little... misleading :-)
Note that I've added the case for when columns is empty: this because you return a IOrderedQueryable, so an ordering must always be present.

OrderBy with a dynamic string parameter in Lambda with related table

I have a extention orderByField
public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string
SortField, bool Ascending){
var param = Expression.Parameter(typeof(T), "p");
var prop = Expression.Property(param, SortField);
var exp = Expression.Lambda(prop, 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);
}
I use it for pass string parameter in it
string sortBy = "StatusID";
return dbContext.Requests.OrderByField(sortBy,true)
But how i can orderBy with a dynamic string parameter with column related with request table like
dbContext.Requests.OrderBy(x => x.Status.Description)
Assuming that you would receive SortField argument in the form of "Status.Description", you would:
First have to split argument to get all properties
Iterate over property names and building property accessor expressions
Use built expression as body of lambda expression
For example:
public static IQueryable<T> OrderByField<T>(this IQueryable<T> q, string SortField, bool Ascending)
{
var param = Expression.Parameter(typeof(T), "p");
Expression expBody = param;
string[] props = SortField.Split('.');
foreach (var prop in props)
{
expBody = Expression.Property(expBody, prop);
}
var exp = Expression.Lambda(expBody, 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);
}

Dynamic Create Func<T,TR> C#

I'm trying to create a dynamic lambda exp to filter some results on my list, but I can't figure out on how to create a dynamic func<,>
//Here I get the type of my object "Produto", but I can have other objects here...Like "Pedido" or another one.
Type type = typeof(Produto);
var param = Expression.Parameter(type, "arg");
var propName = Expression.Property(param, "Codigo");
var constP = Expression.Constant("123");
var nameStartsWith = Expression.Call(
propName,
typeof(string).GetMethod("StartsWith", new[] { typeof(string) }),
constP);
//Here I have to instantiate my Func<T, bool>
//I can't instantiate it with my variable "type" to use on my Where clausule.
//I don't know how to do this in another way, to create my lambda.
var WhereExp = Expression.Lambda<Func<type, bool>>(nameStartsWith, param);
return _uow.produto.GetAll().AsQueryable().Where(WhereExp).ToList();
You need to create a generic method. Something like this (untested):
public Expression<Func<T, bool>> DynamicWhere<T>(string pname, string value) where T : class
{
var param = Expression.Parameter(typeof(T), "arg");
var propName = Expression.Property(param, pname);
var constP = Expression.Constant(value);
var nameStartsWith = Expression.Call(
propName,
typeof(string).GetMethod("StartsWith", new[] { typeof(string) }),
constP);
return Expression.Lambda<Func<T, bool>>(nameStartsWith, param);
}
Then you can use it like this:
var WhereExp = DynamicWhere<Produto>("Codigo", "123");
return _uow.produto.GetAll().AsQueryable().Where(WhereExp).ToList();

Creating linq expression trees for dynamic objects

I am trying to build an expression tree dynamically for sorting. The sorting will happen at the action filter of my web api. So the type of object will be unknown until runtime.
Here's the overview:
At the action filter level:
IEnumerable<object> model = null;
context.Response.TryGetContentValue(out model);
model=model.OrderByExtension(orderByField, orderDirection);
context.Response.Content=new ObjectContent<IEnumerable<object>>(model, new JsonMediaTypeFormatter());
And the extension method:
public static IQueryable<T> OrderByExtension<T>(this IQueryable<T> source, string sortProperty, Sorting.SortingOption sortOrder)
{
var type = source.FirstOrDefault().GetType(); //Gets the type of object passed, since typeof(T) is only object at this point
var property = type.GetProperty(sortProperty);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
var typeArguments = new Type[] { typeof(T), property.PropertyType };
var methodName = sortOrder == Sorting.SortingOption.Asc ? "OrderBy" : "OrderByDescending";
var resultExp = Expression.Call(typeof(Queryable), methodName, typeArguments, source.Expression, Expression.Quote(orderByExp));
return source.Provider.CreateQuery<T>(resultExp);
}
On the Expression.Call - I get the error: No generic method 'OrderByDescending' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments.
I am assuming there is a mismatch between the type 'object' and the actual type when the OrderBy method is invoked.
Is there anyway to get this to work ?
Thanks in advance.
ps: I also tried to create a generic method for OrderBy via .MakeGenericMethod - but no success.
The problem is that you're generating an expression of a function of the actual underlying type, not the type of the source expression (object). You'll need to add a conversion to your type in the generated expression.
public static IQueryable<object> OrderByExtension(this IQueryable<object> source, string sortProperty, SortOrder sortOrder = SortOrder.Unspecified)
{
var sourceType = typeof(object);
var underlyingType = source.First().GetType();
var propertyType = underlyingType.GetProperty(sortProperty).PropertyType;
var param = Expression.Parameter(sourceType);
var body = Expression.Property(
Expression.Convert(param, underlyingType), sortProperty
);
var lambda = Expression.Lambda(body, param);
var sortMethod = sortOrder == SortOrder.Descending ? "OrderByDescending" : "OrderBy";
var expr = Expression.Call(typeof(Queryable), sortMethod, new Type[] { sourceType, propertyType },
source.Expression, lambda
);
return source.Provider.CreateQuery<object>(expr);
}

How do I apply OrderBy on an IQueryable using a string column name within a generic extension method?

public static IQueryable<TResult> ApplySortFilter<T, TResult>(this IQueryable<T> query, string columnName)
where T : EntityObject
{
var param = Expression.Parameter(typeof(T), "o");
var body = Expression.PropertyOrField(param,columnName);
var sortExpression = Expression.Lambda(body, param);
return query.OrderBy(sortExpression);
}
Because the type for OrderBy is not inferred from sortExpression I need to specify it something like this at run time:
var sortExpression = Expression.Lambda<T, TSortColumn>(body, param);
Or
return query.OrderBy<T, TSortColumn>(sortExpression);
I don't think this is possible however as TSortColumn can only be determined during runtime.
Is there a way around this?
We did something similar (not 100% the same, but similar) in a LINQ to SQL project. Here's the code:
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values) {
var type = typeof(T);
var property = type.GetProperty(ordering);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderBy", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));
return source.Provider.CreateQuery<T>(resultExp);
}
We didn't actually use a generic, we had a known class, but it should work on a generic (I've put the generic placeholder where it should be).
Edit: For descending order, pass in OrderByDescending instead of "OrderBy":
MethodCallExpression resultExp = Expression.Call(typeof(Queryable), "OrderByDescending", new Type[] { type, property.PropertyType }, source.Expression, Expression.Quote(orderByExp));
You can also use Dynamic Linq
Info here
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
C# download here
http://msdn.microsoft.com/en-us/vcsharp/bb894665.aspx
Then just add the using Linq.Dynamic; and you automatically get 2 additional extension methods that can be used like this
return query.OrderBy("StringColumnName");
I've extended your functions to add support for Child Properties.
private static LambdaExpression GenerateSelector<TEntity>(String propertyName, out Type resultType) where TEntity : class
{
// Create a parameter to pass into the Lambda expression (Entity => Entity.OrderByField).
var parameter = Expression.Parameter(typeof(TEntity), "Entity");
// create the selector part, but support child properties
PropertyInfo property;
Expression propertyAccess;
if (propertyName.Contains('.'))
{
// support to be sorted on child fields.
String[] childProperties = propertyName.Split('.');
property = typeof(TEntity).GetProperty(childProperties[0]);
propertyAccess = Expression.MakeMemberAccess(parameter, property);
for (int i = 1; i < childProperties.Length; i++)
{
property = property.PropertyType.GetProperty(childProperties[i]);
propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
}
}
else
{
property = typeof(TEntity).GetProperty(propertyName);
propertyAccess = Expression.MakeMemberAccess(parameter, property);
}
resultType = property.PropertyType;
// Create the order by expression.
return Expression.Lambda(propertyAccess, parameter);
}
private static MethodCallExpression GenerateMethodCall<TEntity>(IQueryable<TEntity> source, string methodName, String fieldName) where TEntity : class
{
Type type = typeof(TEntity);
Type selectorResultType;
LambdaExpression selector = GenerateSelector<TEntity>(fieldName, out selectorResultType);
MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName,
new Type[] { type, selectorResultType },
source.Expression, Expression.Quote(selector));
return resultExp;
}
You can use these functions like:
GenerateMethodCall<TEntity>(source, "OrderByDescending", fieldName);
I used your idea for extension method for OrderBy. But in case of "many to many" I am getting error. For example you have table Site, Customer and Customer_site.
For given Site I want to sort by customer name and in OrderBy extension (when I pass "site.customer" where customer is navigation property) I get error in line: propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
This is what I use (with some enhancements :-) ):
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByValues) where TEntity : class
{
IQueryable<TEntity> returnValue = null;
string orderPair = orderByValues.Trim().Split(',')[0];
string command = orderPair.ToUpper().Contains("DESC") ? "OrderByDescending" : "OrderBy";
var type = typeof(TEntity);
var parameter = Expression.Parameter(type, "p");
string propertyName = (orderPair.Split(' ')[0]).Trim();
System.Reflection.PropertyInfo property;
MemberExpression propertyAccess;
if (propertyName.Contains('.'))
{
// support to be sorted on child fields.
String[] childProperties = propertyName.Split('.');
property = typeof(TEntity).GetProperty(childProperties[0]);
propertyAccess = Expression.MakeMemberAccess(parameter, property);
for (int i = 1; i < childProperties.Length; i++)
{
Type t = property.PropertyType;
if (!t.IsGenericType)
{
property = t.GetProperty(childProperties[i]);
}
else
{
property = t.GetGenericArguments().First().GetProperty(childProperties[i]);
}
propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
}
}
else
{
property = type.GetProperty(propertyName);
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));
returnValue = source.Provider.CreateQuery<TEntity>(resultExpression);
if (orderByValues.Trim().Split(',').Count() > 1)
{
// remove first item
string newSearchForWords = orderByValues.ToString().Remove(0, orderByValues.ToString().IndexOf(',') + 1);
return source.OrderBy(newSearchForWords);
}
return returnValue;
}
Regards
Slobodan
It seems that this is the way to do it, now to verify that:
// ***** OrderBy(company => company) *****
// Create an expression tree that represents the expression
// 'whereCallExpression.OrderBy(company => company)'
MethodCallExpression orderByCallExpression = Expression.Call(
typeof(Queryable),
"OrderBy",
new Type[] { queryableData.ElementType, queryableData.ElementType },
whereCallExpression,
Expression.Lambda<Func<string, string>>(pe, new ParameterExpression[] { pe }));
// ***** End OrderBy *****
If you are able to add "System.Linq.Dynamic" package then,
Too easy without any complication,
fisrt insatll package "System.Linq.Dynamic" from NuGet package manager then
try as below as your need,
Ex:
public IQueryable<TEntity> GetWithInclude(Expression<Func<TEntity, bool>> predicate,
List<string> sortBy, int pageNo, int pageSize = 12, params string[] include)
{
try
{
var numberOfRecordsToSkip = pageNo * pageSize;
var dynamic = DbSet.AsQueryable();
foreach (var s in include)
{
dynamic.Include(s);
}
return dynamic.OrderBy("CreatedDate").Skip(numberOfRecordsToSkip).Take(pageSize);
}
catch (Exception e)
{
throw new Exception(e.Message);
}
}
Hope this will help
I fixed this code a bit: https://stackoverflow.com/a/1670085/5852630
This code works with sequential sorting: first execute "OrderBy", then "ThenBy"(Not "OrderBy"!)
public static IQueryable<TEntity> OrderBy<TEntity>(this IQueryable<TEntity> source, string orderByValues) where TEntity : class
{
IQueryable<TEntity> returnValue = null;
string[] orderPairs = orderByValues.Trim().Split(',');
Expression resultExpression = source.Expression;
string strAsc = "OrderBy";
string strDesc = "OrderByDescending";
foreach (string orderPair in orderPairs)
{
if (string.IsNullOrWhiteSpace(orderPair))
continue;
string[] orderPairArr = orderPair.Trim().Split(' ');
string propertyName = orderPairArr[0].Trim();
string orderNarrow = orderPairArr.Length > 1 ? orderPairArr[1].Trim() : string.Empty;
string command = orderNarrow.ToUpper().Contains("DESC") ? strDesc : strAsc;
Type type = typeof(TEntity);
ParameterExpression parameter = Expression.Parameter(type, "p");
System.Reflection.PropertyInfo property;
Expression propertyAccess;
if (propertyName.Contains('.'))
{
// support to be sorted on child fields.
String[] childProperties = propertyName.Split('.');
property = typeof(TEntity).GetProperty(childProperties[0]);
propertyAccess = Expression.MakeMemberAccess(parameter, property);
for (int i = 1; i < childProperties.Length; i++)
{
Type t = property.PropertyType;
if (!t.IsGenericType)
{
property = t.GetProperty(childProperties[i]);
}
else
{
property = t.GetGenericArguments().First().GetProperty(childProperties[i]);
}
propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
}
}
else
{
property = type.GetProperty(propertyName);
propertyAccess = Expression.MakeMemberAccess(parameter, property);
}
if (property.PropertyType == typeof(object))
{
propertyAccess = Expression.Call(propertyAccess, "ToString", null);
}
LambdaExpression orderByExpression = Expression.Lambda(propertyAccess, parameter);
resultExpression = Expression.Call(typeof(Queryable), command, new Type[] { type, property.PropertyType == typeof(object) ? typeof(string) : property.PropertyType },
resultExpression, Expression.Quote(orderByExpression));
strAsc = "ThenBy";
strDesc = "ThenByDescending";
}
returnValue = source.Provider.CreateQuery<TEntity>(resultExpression);
return returnValue;
}
Here is my adaptation from #Davy Landman's answer (I wanted an extension method) and I simplified a bit.
public static IQueryable<T> SortBy<T>(this IQueryable<T> source,
String propertyName,
WebControls.SortDirection direction)
{
if (source == null) throw new ArgumentNullException("source");
if (String.IsNullOrEmpty(propertyName)) return source;
// Create a parameter to pass into the Lambda expression
//(Entity => Entity.OrderByField).
var parameter = Expression.Parameter(typeof(T), "Entity");
// create the selector part, but support child properties (it works without . too)
String[] childProperties = propertyName.Split('.');
MemberExpression property = Expression.Property(parameter, childProperties[0]);
for (int i = 1; i < childProperties.Length; i++)
{
property = Expression.Property(property, childProperties[i]);
}
LambdaExpression selector = Expression.Lambda(property, parameter);
string methodName = (direction > 0) ? "OrderByDescending" : "OrderBy";
MethodCallExpression resultExp = Expression.Call(typeof(Queryable), methodName,
new Type[] { source.ElementType, property.Type },
source.Expression, Expression.Quote(selector));
return source.Provider.CreateQuery<T>(resultExp);
}
It can be used like this:
gridview1.DataSource = DbContext.TB_CARS.SortBy("model", SortDirection.Descending);
//OR
gridview1.DataSource = DbContext.TB_CARS.SortBy("owner.first_name", 0);

Categories

Resources