How to use Expression for Dictionary? - c#

Let me re-post my question.
Here is my static class to wrap some call to Async
public static class QueryExtensions
{
internal static async Task<List<TSource>> ToListAsync<TSource>(this IQueryable<TSource> query)
{
//Initialzie
var call = Expression.Call(typeof(Enumerable), nameof(Enumerable.ToList), new Type[] { typeof(T) }, query.Expression);
//Execute
return await ExecuteAsync<List<TSource>>(call);
}
public static async Task<Dictionary<TKey, TSource>> ToDictionaryAsyn<TKey, TSource>(this IQueryable<TSource> query, Func<TSource, TKey> keySelector)
{
//I don't know how to pass keySelector to following
var call = Expression.Call(typeof(Enumerable), nameof(Enumerable.ToDictionary), new Type[] { typeof(TKey), typeof(TSource) }, query.Expression);
//Execute
return await ExecuteAsync<Dictionary<TKey, TSource>>(call);
}
private static Task<TResult> ExecuteAsync<TResult>(MethodCallExpression expression)
{
//Do some in Asyn
}
}
I will call it in following code
await list.AsQueryable().ToListAsync(); -- It work now
await list.AsQueryable().ToDictionaryAsyn(current => current.Name);
I don't know how to implement ToDictionaryAsyn method

If your goal is to generate something like (List<People> l) => l.ToDictionary(p => p.Name) you can achieve it like this:
// declare parameter of type IEnumerable<People>
var p = Expression.Parameter(typeof(IEnumerable<People>));
// find Enumerable.ToDictionary(this list, keySelector)
var method = typeof(Enumerable)
.GetMethods()
.Where(mi => mi.Name == nameof(Enumerable.ToDictionary) && mi.GetParameters().Length == 2)
.Single();
// apply generic parameters
var concreteMethod = method.MakeGenericMethod(new[] {typeof(People), typeof(string)});
// create selector
Expression<Func<People, string>> selector = p => p.Name;
var call = Expression.Call(null, concreteMethod, p, selector);
// create lambda
var lambda = Expression.Lambda<Func<List<People>, Dictionary<string,People>>>(call, p);
var func = lambda.Compile();
var result = func(list);

An example as below. Hope it helps.
public static class Extns
{
public static Dictionary<TKey, TSource> ToDictWithSelector<TKey, TSource>(this IQueryable<TSource> query,
Expression<Func<TSource, TKey>> keySelector)
{
var p = Expression.Parameter(typeof(IQueryable<TSource>));
var call = Expression.Call(typeof(Enumerable),
nameof(Enumerable.ToDictionary),
new[] { typeof(TSource), typeof(TKey) },
new Expression[] { p, keySelector });
var lambda = Expression.Lambda<Func<IQueryable<TSource>, Dictionary<TKey, TSource>>>(call, p);
Func<IQueryable<TSource>, Dictionary<TKey, TSource>> func = lambda.Compile();
return func(query.AsQueryable());
}
}
// usage of the above extension
static void Main(string[] args)
{
var list = new List<People>();
list.Add(new People() { Name = "Eagle", Age = 18 });
list.Add(new People() { Name = "Sam", Age = 30 });
list.Add(new People() { Name = "May", Age = 24 });
Expression<Func<People, string>> selector = p => p.Name;
Dictionary<string, People> rr1 = list.AsQueryable().ToDictWithSelector(selector);
Expression<Func<People, int>> selector2 = p => p.Age;
Dictionary<int, People> rr2 = list.AsQueryable().ToDictWithSelector(selector2);
}

Related

Custom LINQ Where clause not actually applying Where

I have a custom Filter Expression where a user can send a dictionary to my api and I will create a custom where clause. I am no expert in writing custom LINQ so if there is a better way then that would be great as well.
All of this works (No errors) but it doesn't apply my filtering thus returning all it can find)
My Repository call where I append my custom filter (Look at the "FilterByString" area)
. _entities is a DbSet
// This is in my contructor
private readonly DbSet<T2> _entities;
_entities = _context.Set<T2>();
public async Task<BasePagedResponse<T1>> PagedList(Guid companyId, int pageNumber, int pageSize, Dictionary<string, string> filterParameters, Expression<Func<T2, bool>> predicate, Func<IQueryable<T2>, IIncludableQueryable<T2, object>> including = null)
{
try
{
var modelList = await _entities
.AsQueryable()
.Where(predicate)
.CustomInclude(including)
.FilterByString(filterParameters)
.Skip((pageNumber - 1) * pageSize)
.Take(pageSize).ToListAsync();
var mappedList = _mapper.Map<List<T1>>(modelList);
var total = await GetTotalRecords(companyId);
var pagedResult = new PagedResult<T1>(mappedList, total, pageNumber, pageSize);
return new BasePagedResponse<T1>(pagedResult, true, null);
}
catch (SqlException ex)
{
return new BasePagedResponse<T1>(null, false, new[] { new Error(GlobalVariables.error_list, $"Error listing models-({typeof(T1).Name}). {ex.Message}") });
}
}
My static SQL Helper that takes the source query and appends my custom Where Clause
public static IQueryable<T> FilterByString<T>(this IQueryable<T> source, Dictionary<string, string> filterParameters = null)
{
if (filterParameters != null)
{
foreach (var keyValuePair in filterParameters)
{
source.Where(Filter<T>(keyValuePair.Key, keyValuePair.Value));
}
}
return source;
}
My custom "Filter" (Looks like a lot but it just looks for types of string, bool or int to filter on)
private static Expression<Func<T, bool>> Filter<T>(string propertyName, string queryText)
{
var parameter = Expression.Parameter(typeof(T), "entity");
var getter = Expression.Property(parameter, propertyName);
if (getter.Type == typeof(string))
{
var stringContainsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var containsCall = Expression.Call(getter, stringContainsMethod,
Expression.Constant(queryText, typeof(string)));
return Expression.Lambda<Func<T, bool>>(containsCall, parameter);
}
if (getter.Type == typeof(int))
{
var stringEqualsMethod = typeof(int).GetMethod("Equals", new[] { typeof(int) });
var equalsCall = Expression.Call(getter, stringEqualsMethod,
Expression.Constant(value: Int32.Parse(queryText), typeof(Int32)));
return Expression.Lambda<Func<T, bool>>(equalsCall, parameter);
}
if (getter.Type == typeof(bool))
{
var stringEqualsMethod = typeof(bool).GetMethod("Equals", new[] { typeof(bool) });
var equalsCall = Expression.Call(getter, stringEqualsMethod,
Expression.Constant(value: bool.Parse(queryText), typeof(bool)));
return Expression.Lambda<Func<T, bool>>(equalsCall, parameter);
}
throw new Exception("No proper type defined for Filter Parameter. Supports only String, Int and Boolean");
}
It is a decorator API, where .Where(...) returns a query that is composed to include the predicate, but which does not change the original query; you need to retain the result:
foreach (var keyValuePair in filterParameters)
{
source = source.Where(Filter<T>(keyValuePair.Key, keyValuePair.Value));
// ^^^^^
}

Select distinct column from Entity Framework with dynamic column name

I'm searching a way to get a list of distinct values, for one column of my table. I need to make a reusable method.
This is what I tried so far, but it doesn't work:
IEnumerable<string> GetDistinctValues<T>(string columnName)
{
T.Select(m => m.ColumnName).Distinct().ToList();
}
The desired solution should be an extension method for EF objects.
I've tried this post Dynamically select columns in runtime using entity framework but it works only for a single record not for a list.
The only problem I see with Linq.Dynamic is that there were no updates since 2013 and the project is pretty much dead
I would handle it via extensions, and improve reflection performance via caching (not elaborated here)
Extensions:
public static class QueryableExtensions
{
public static IReadOnlyCollection<TResult> GetDistinctValuesForProperty<T, TResult>(this IQueryable<T> query, Expression<Func<T, TResult>> propertyAccess)
{
return SelectDistinct(query, propertyAccess).ToList();
}
public static IReadOnlyCollection<object> GetDistinctValuesForProperty<TSource>(this IQueryable<TSource> query, string propertyName)
{
var unboundFuncType = typeof(Func<,>);
var unboundExprType = typeof(Expression<>);
var sourceType = typeof(TSource); // TSource
var resultType = typeof(TSource)
.GetProperty(propertyName)
.PropertyType; // TResult
// Func<TSource, TResult>
var funcType = unboundFuncType.MakeGenericType(new [] { sourceType, resultType });
// Expression<Func<TSource, TResult>>
var expressionType = unboundExprType.MakeGenericType(new [] { funcType });
// Instance of Expression<Func<TSource, TResult>>, for example x => x.Name
var propertyAccess = typeof(StringExtensions)
.GetMethod(nameof(StringExtensions.AsPropertyExpression), new[] { typeof(string) })
.MakeGenericMethod(new [] { sourceType, resultType })
.Invoke(null, new object[] { propertyName });
// SelectDistinct query transform
var selectDistinctMethod = typeof(QueryableExtensions)
.GetMethod(nameof(QueryableExtensions.SelectDistinct), BindingFlags.NonPublic | BindingFlags.Static)
.MakeGenericMethod(new [] { sourceType, resultType });
// IQueryable<TSource> ==> IQueryable<TResult>
var result = selectDistinctMethod.Invoke(null, new object[] { query, propertyAccess });
// Cast to object via IEnumerable and convert to list
return ((IEnumerable)result).Cast<object>().ToList();
}
private static IQueryable<TResult> SelectDistinct<TSource, TResult>(this IQueryable<TSource> query, Expression<Func<TSource, TResult>> propertyAccess)
{
return query.Select(propertyAccess).Distinct();
}
}
public static class StringExtensions
{
public static Expression<Func<T, TResult>> AsPropertyExpression<T, TResult>(this string propertyName)
{
var parameter = Expression.Parameter(typeof(T), "x");
var property = typeof(T).GetProperty(propertyName);
var body = Expression.MakeMemberAccess(parameter, property);
return Expression.Lambda<Func<T, TResult>>(body, parameter);
}
}
Usage:
public class Person
{
public string Name { get; }
public int Age { get; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
var people = new Person[]
{
new Person("John", 25), new Person("Peter", 25), new Person("Sean", 25),
new Person("John", 32), new Person("Peter", 32),
};
var query = people.AsQueryable();
var namePropertyExpression = "Name".AsPropertyExpression<Person, string>();
var agePropertyExpression = "Age".AsPropertyExpression<Person, int>();
// When you know the result type
var names1 = query.GetDistinctValuesForProperty(x => x.Name);
var ages1 = query.GetDistinctValuesForProperty(x => x.Age);
// When you know the result type, but you may want to reuse the property expression
var names2 = query.GetDistinctValuesForProperty(namePropertyExpression);
var ages2 = query.GetDistinctValuesForProperty(agePropertyExpression);
// When you just know the property name
var names3 = query.GetDistinctValuesForProperty("Name");
var ages3 = query.GetDistinctValuesForProperty("Age");
Finally I found a solution. I need to include reference to System.Linq.Dynamic (downloaded by nuget), and use the "Select" method that accept String to reference column.
using System.Linq.Dynamic;
public static async Task<IEnumerable<Object>> GetDistinctValuesForProperty<T>(this IQueryable<T> query, String PropertyName)
{
return await query.Select(PropertyName).Distinct().ToListAsync();
}
and call as
String ColumnName = "DateTimeInsert";
DbSet<Log> oDbSet = _uow.DbContext.Set<Log>();
Array DistinctValues;
if (typeof(Log).GetProperty(ColumnName) != null)
{
DistinctValues = (await oDbSet.GetDistinctValuesForProperty(ColumnName)).ToArray();
}
else
{
DistinctValues = new object[0];
}
I need to use array vs ienumerable due to a cast problem in case of datetime types
You can create a generic selector method using Expressions
public static Func<T, T> SelectorFunc<T>(string[] columns) {
// input parameter "o"
var xParameter = Expression.Parameter(typeof(T), "o");
// new statement "new Data()"
var xNew = Expression.New(typeof(T));
// create initializers
var bindings = columns.Select(o => o.Trim())
.Select(o =>
{
// property "Field1"
var mi = typeof(T).GetProperty(o);
// original value "o.Field1"
var xOriginal = Expression.Property(xParameter, mi);
// set value "Field1 = o.Field1"
return Expression.Bind(mi, xOriginal);
}
);
// initialization "new Data { Field1 = o.Field1, Field2 = o.Field2 }"
var xInit = Expression.MemberInit(xNew, bindings);
// expression "o => new Data { Field1 = o.Field1, Field2 = o.Field2 }"
var lambda = Expression.Lambda<Func<T, T>>(xInit, xParameter);
// compile to Func<Data, Data>
return lambda.Compile();
}
using it would be
T.Select( SelectorFunc<T>( new string[]{ "Column" } ) ).Distinct().ToList();
You can also use it any other linq functions like
T.Select( SelectorFunc<T>( new string[]{ "Column" } ) ).Where();
T.Select( SelectorFunc<T>( new string[]{ "Column" } ) ).AsQueryable();
for additional reference you can see the full OP here
LINQ : Dynamic select

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);
}
}

Incorrect number of parameters supplied for lambda declaration with two parameters

I have query
return this._articlesRepository.GetEntities()
.Join(this._categoryRepository.GetEntities(),
article => article.CategoryId,
cat => cat.Id, (article, cat) => new {article, CategoryName = cat.Title}).Select(new T());
I have queryable extension for select. I try create extension for dynamic generated select query.
public static IQueryable<TResult> Select<TSource, TResult>(this IQueryable<TSource> source, TResult result)
{
var resultType = typeof(TResult);
var resultObj = Expression.New(resultType);
var receiverProperties = resultType.GetProperties();
Type type = typeof(TSource);
ParameterExpression sourceParameter = Expression.Parameter(type, "item");
ParameterExpression anyParameter = null;
var resultParameters = new List<MemberBinding>();
foreach (var receiverProperty in receiverProperties)
{
var sourceProperty = typeof(TSource).GetProperty(receiverProperty.Name);
if (sourceProperty != null)
{
var sourcePropertyAccess = Expression.MakeMemberAccess(sourceParameter, sourceProperty);
var memberInit = Expression.Bind(receiverProperty, sourcePropertyAccess);
resultParameters.Add(memberInit);
}
}
if (typeof(TSource).Name.Contains("AnonymousType"))
{
foreach (var property in typeof(TSource).GetProperties())
{
if (property.PropertyType.BaseType == typeof(BaseEntity))
{
anyParameter = Expression.Parameter(property.PropertyType, "item."+property.Name);
foreach (var receiverProperty in receiverProperties)
{
var sourceProperty = property.PropertyType.GetProperty(receiverProperty.Name);
if (sourceProperty != null)
{
var sourcePropertyAccess = Expression.MakeMemberAccess(anyParameter, sourceProperty);
var memberInit = Expression.Bind(receiverProperty, sourcePropertyAccess);
resultParameters.Add(memberInit);
}
}
}
}
}
if (anyParameter != null)
{
var selector = Expression.Lambda<Func<TSource, TResult>>(Expression.MemberInit(resultObj, resultParameters), sourceParameter, anyParameter);
var method = Expression.Call(typeof(Queryable), "Select", new[] { source.ElementType, selector.Body.Type }, source.Expression, Expression.Quote(selector));
return source.Provider.CreateQuery<TResult>(method);
}
else
{
var selector = Expression.Lambda<Func<TSource, TResult>>(Expression.MemberInit(resultObj, resultParameters), sourceParameter);
var method = Expression.Call(typeof(Queryable), "Select", new[] { source.ElementType, selector.Body.Type }, source.Expression, Expression.Quote(selector));
return source.Provider.CreateQuery<TResult>(method);
}
}
But my code fall down at this moment
var selector
= Expression.Lambda<Func<TSource, TResult>>
(Expression.MemberInit(resultObj, resultParameters),
sourceParameter,
anyParameter);
And I can't understand why? I understand that my code look bad. But then I fixed this.
My old code working fine
public static IQueryable<TResult> Select<TSource, TResult>(this IQueryable<TSource> source, TResult result)
{
var resultType = typeof(TResult);
var resultObj = Expression.New(resultType);
var receiverProperties = resultType.GetProperties();
Type type = typeof(TSource);
ParameterExpression sourceParameter = Expression.Parameter(type, "item");
var resultParameters = new List<MemberBinding>();
foreach (var receiverProperty in receiverProperties)
{
var sourceProperty = typeof(TSource).GetProperty(receiverProperty.Name);
if (sourceProperty != null)
{
var sourcePropertyAccess = Expression.MakeMemberAccess(sourceParameter, sourceProperty);
var memberInit = Expression.Bind(receiverProperty, sourcePropertyAccess);
resultParameters.Add(memberInit);
}
}
var selector = Expression.Lambda<Func<TSource, TResult>>(Expression.MemberInit(resultObj, resultParameters), sourceParameter);
var method = Expression.Call(typeof(Queryable), "Select", new[] { source.ElementType, selector.Body.Type }, source.Expression, Expression.Quote(selector));
return source.Provider.CreateQuery<TResult>(method);
}

C# Expression Tree Binding

So what I am trying to do is use expression trees to apply a predicate to each value in a collection (read map or list.All(predicate)). It appears that I am not getting the input parameter to the predicate bound to the value supplied by All, and I'm a little stuck. Here is the code (using linqpad) that I am working with::
public class SomeType
{
public IEnumerable<bool> Collection { get; set; }
}
void Main()
{
var list = new SomeType {
Collection = new List<bool> { true, true, true }
};
var functor = Compiler((SomeType t) => t.Collection, (bool x) => x);
functor(list).Dump();
}
MethodInfo FindMethod<TInput>(Type location, string name)
{
var handle = location
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(method => method.Name == name).First();
return handle.MakeGenericMethod(typeof(TInput));
}
Predicate<TObject> Compiler<TObject, TProperty>(
Expression<Func<TObject, IEnumerable<TProperty>>> selector,
Expression<Predicate<TProperty>> predicate)
{
var query = FindMethod<TProperty>(typeof(Enumerable), "All");
var expression = Expression.Call(query,
new Expression[] {
Expression.Invoke(selector, selector.Parameters),
Expression.Lambda<Func<TProperty, bool>>(predicate.Body,
Expression.Parameter(typeof(TProperty))),
});
return Expression.Lambda<Predicate<TObject>>(expression,
selector.Parameters).Compile();
}
Thanks and sorry if this was answered in another question (I looked for a while).
This does work, but I had to change the Predicate<TObject> to Func<TObject, bool>. If you want I can try to change it back.
static Predicate<TObject> Compiler<TObject, TProperty>(
Expression<Func<TObject, IEnumerable<TProperty>>> selector,
Expression<Func<TProperty, bool>> predicate)
{
var query = FindMethod<TProperty>(typeof(Enumerable), "All");
var expression = Expression.Call(
query,
Expression.Invoke(selector, selector.Parameters),
predicate);
return Expression
.Lambda<Predicate<TObject>>(expression, selector.Parameters)
.Compile();
}
5 minutes later... And if you really want to use Predicate<TObject>...
static Predicate<TObject> Compiler<TObject, TProperty>(
Expression<Func<TObject, IEnumerable<TProperty>>> selector,
Expression<Predicate<TProperty>> predicate)
{
var query = FindMethod<TProperty>(typeof(Enumerable), "All");
var predicateAsFunc = Expression.Lambda<Func<TProperty, bool>>(
predicate.Body,
predicate.Parameters);
var expression = Expression.Call(
query,
Expression.Invoke(selector, selector.Parameters),
predicateAsFunc);
return Expression
.Lambda<Predicate<TObject>>(expression, selector.Parameters)
.Compile();
}

Categories

Resources