Multiple linq query of order by to single one [duplicate] - c#

This question already has answers here:
evaluate column name in linq where clause
(2 answers)
Closed 10 years ago.
I have multiple line condition for order by as below
if (enum1)
{
var = itemlist.orderby(r => r.column1)
}
else if (emum2)
{
var = itemlist.orderby(r => r.column2)
}
And so on.. Any way to do such thing dynamically.

What you can do is to better manage the column selection for sorting, e.g. using some collection that holds the Func<> for KeySelector.
E.g. if you have a class name 'SomeDTO' with four properties, Prop1,2,3 and 4. And four corresponding enum members.
var searchByMapping = new Dictionary<SearchByEnum,Func<SomeDTO, object>>();
searchByMapping.Add(SearchByEnum.Prop1, x => x.Prop1);
searchByMapping.Add(SearchByEnum.Prop2, x => x.Prop2);
searchByMapping.Add(SearchByEnum.Prop3, x => x.Prop3);
coll = coll.OrderBy(searchByMapping[searchByEnumParam]).ToList();
This is a not so dynamic approach but a typesafe one.

Supposing your enum can have only two values :
enum yourEnum( X=0, Y}
enum1 was somewhere set:
itemlist = enum1==Enum.X? itemlist.Orderby(r => r.column1): itemlist.Orderby(r => r.column2)

I did using below extension method.
public static class IQueryableExtensions
{
public static IQueryable<T> OrderBy<T>(this IQueryable<T> items, string propertyName)
{
var typeOfT = typeof(T);
var parameter = Expression.Parameter(typeOfT, "parameter");
var propertyType = typeOfT.GetProperty(propertyName).PropertyType;
var propertyAccess = Expression.PropertyOrField(parameter, propertyName);
var orderExpression = Expression.Lambda(propertyAccess, parameter);
var expression = Expression.Call(typeof(Queryable), "OrderBy", new Type[] { typeOfT, propertyType }, items.Expression, Expression.Quote(orderExpression));
return items.Provider.CreateQuery<T>(expression);
}
}

Related

In LinqToEntities, How to pass dynamic column name to DbFunctions.Like

I have an IQueryable<T> from my DbSet in Entity Framework. I am provided a "Fuzzy Search String", named searchText, like so:
public List<T> Search<T>(string searchText)
{
using (var context = ...)
{
var baseQuery = context.Set<T>().AsQueryable();
baseQuery = baseQuery.Where(x =>
DbFunctions.Like(x.PropertyName, searchText)
|| DbFunctions.Like(x.PropertyTwo, searchText)
|| DbFunctions.Like(x.PropertyThree, searchText)
|| DbFunctio..... etc
);
return baseQuery.ToList();
}
}
But given the generic nature, I don't know what properties there are on the type. I can provide an abstract method to somebody implementing this which allows them to give me a List of Properties (or even PropertyInfo or whatever else, I can figure that out). But I don't know how to dynamically create the expression. This is what I have so far:
var baseQuery = context.Set<T>().AsQueryable();
var expression = baseQuery.Expression;
var colName = "colName"; // Or names, I can iterate.
var parameter = Expression.Parameter(typeof(T), "x");
var selector = Expression.PropertyOrField(parameter, colName);
expression = Expression.Call(typeof(DbFunctions), nameof(DbFunctions.Like),
new Type[] { baseQuery.ElementType, selector.Type },
expression, Expression.Quote(Expression.Lambda(selector, parameter)));
The problem here is... well, it doesn't work to begin with. But mainly that I'm not using the searchText anywhere in it, and don't know how to plug it in. I THINK I'm close... but have spent an inordinate amount of time on it.
Hopefully I'm getting your query logic right: if you want to build a set of LIKE conditions based on known type and list of column names, you could try something like this:
static private MethodInfo dbLikeMethod = typeof(DbFunctions).GetMethod(nameof(DbFunctions.Like), BindingFlags.Public | BindingFlags.Static, null, new Type[] {typeof(string), typeof(string)}, null); // I am targeting DbFunctions.Like(string, string). You might want another overload (or even mix them up depending on your inputs)
public List<T> Search<T>(string searchText) where T: class
{
using (var context = new ...)
{
var baseQuery = context.Set<T>().AsQueryable().Where(CreateExpression<T>(searchText));// you could probably find a more elegant way of plugging it into your query
return baseQuery.ToList();
}
}
Expression<Func<T, bool>> CreateExpression<T>(string searchText) where T : class
{
var cols = new List<string> {
"PropertyName",
"PropertyTwo" // i understand you've got a way to figure out which strings you need here
};
var parameter = Expression.Parameter(typeof(T), "x");
var dbLikeCalls = cols.Select(colName => Expression.Call(dbLikeMethod, Expression.PropertyOrField(parameter, colName), Expression.Constant(searchText))); // for convenience, generate list of DbFunctions.Like(x.<Property>, searchText) expressions here
var aggregatedCalls = dbLikeCalls.Skip(1).Aggregate((Expression)dbLikeCalls.First(), (accumulate, call) => Expression.OrElse(accumulate, call)); // aggregate the list using || operators: use first item as a seed and keep adding onto it
return Expression.Lambda<Func<T, bool>>(aggregatedCalls, parameter);
}

Dynamically build select list from linq to entities query

I'm looking for a way to dynamically create a select list from a iQueryable object.
Concrete example, i want to do something like the following:
public void CreateSelectList(IQueryable(of EntityModel.Core.User entities), string[] columns)
{
foreach(var columnID in columns)
{
switch(columnID)
{
case "Type":
SelectList.add(e => e.UserType);
break;
case "Name":
SelectList.add(e => e.Name);
break;
etc....
}
}
var selectResult = (from u in entities select objSelectList);
}
So all properties are known, i however don't know beforehand what properties are to be selected. That will be passed via the columns parameter.
I know i'm going to run into issues with the type of the selectResult type, because when the select list is dynamic, the compiler doesn't know what the properties of the anonymous type needs to be.
If the above is not possible: The scenario I need it for is the following:
I'm trying to create a class that can be implemented to display a paged/filtered list of data. This data can be anything (depends on the implementations).The linq used is linq to entities. So they are directly linked to sql data. Now i want to only select the columns of the entities that i am actually showing in the list. Therefore i want the select to be dynamic. My entity might have a hundred properties, but if only 3 of them are shown in the list, i don't want to generate a query that selects the data of all 100 columns and then only uses 3 of them. If there is a different approach that I haven't thought of, I'm open to ideas
Edit:
Some clarifications on the contraints:
- The query needs to work with linq to entities (see question subject)
- an entity might contain 100 columns, so selecting ALL columns and then only reading the ones i need is not an option.
- The end user decides what columns to show, so the columns to select are determined at run time
- i need to create a SINGLE select, having multiple select statements means having multiple queries on the database, which i don't want
Dynamic select expression to a compile time known type can easily be build using Expression.MemberInit method with MemberBindings created using the Expression.Bind method.
Here is a custom extension method that does that:
public static class QueryableExtensions
{
public static IQueryable<TResult> Select<TResult>(this IQueryable source, string[] columns)
{
var sourceType = source.ElementType;
var resultType = typeof(TResult);
var parameter = Expression.Parameter(sourceType, "e");
var bindings = columns.Select(column => Expression.Bind(
resultType.GetProperty(column), Expression.PropertyOrField(parameter, column)));
var body = Expression.MemberInit(Expression.New(resultType), bindings);
var selector = Expression.Lambda(body, parameter);
return source.Provider.CreateQuery<TResult>(
Expression.Call(typeof(Queryable), "Select", new Type[] { sourceType, resultType },
source.Expression, Expression.Quote(selector)));
}
}
The only problem is what is the TResult type. In EF Core you can pass the entity type (like EntityModel.Core.User in your example) and it will work. In EF 6 and earlier, you need a separate non entity type because otherwise you'll get NotSupportedException - The entity or complex type cannot be constructed in a LINQ to Entities query.
UPDATE: If you want a to get rid of the string columns, I can suggest you replacing the extension method with the following class:
public class SelectList<TSource>
{
private List<MemberInfo> members = new List<MemberInfo>();
public SelectList<TSource> Add<TValue>(Expression<Func<TSource, TValue>> selector)
{
var member = ((MemberExpression)selector.Body).Member;
members.Add(member);
return this;
}
public IQueryable<TResult> Select<TResult>(IQueryable<TSource> source)
{
var sourceType = typeof(TSource);
var resultType = typeof(TResult);
var parameter = Expression.Parameter(sourceType, "e");
var bindings = members.Select(member => Expression.Bind(
resultType.GetProperty(member.Name), Expression.MakeMemberAccess(parameter, member)));
var body = Expression.MemberInit(Expression.New(resultType), bindings);
var selector = Expression.Lambda<Func<TSource, TResult>>(body, parameter);
return source.Select(selector);
}
}
with sample usage:
var selectList = new SelectList<EntityModel.Core.User>();
selectList.Add(e => e.UserType);
selectList.Add(e => e.Name);
var selectResult = selectList.Select<UserDto>(entities);
What you are going for is possible, but it's not simple. You can dynamically build EF queries using the methods and classes in the System.Linq.Expressions namespace.
See this question for a good example of how you can dynamically build your Select expression.
I believe this is what you need:
var entities = new List<User>();
entities.Add(new User { Name = "First", Type = "TypeA" });
entities.Add(new User { Name = "Second", Type = "TypeB" });
string[] columns = { "Name", "Type" };
var selectResult = new List<string>();
foreach (var columnID in columns)
{
selectResult.AddRange(entities.Select(e => e.GetType().GetProperty(columnID).GetValue(e, null).ToString()));
}
foreach (var result in selectResult)
{
Console.WriteLine(result);
}
This code outputs:
First
Second
TypeA
TypeB
UPDATE (according to comments)
// initialize alist of entities (User)
var entities = new List<User>();
entities.Add(new User { Name = "First", Type = "TypeA", SomeOtherField="abc" });
entities.Add(new User { Name = "Second", Type = "TypeB", SomeOtherField = "xyz" });
// set the wanted fields
string[] columns = { "Name", "Type" };
// create a set of properties of the User class by the set of wanted fields
var properties = typeof(User).GetProperties()
.Where(p => columns.Contains(p.Name))
.ToList();
// Get it with a single select (by use of the Dynamic object)
var selectResult = entities.Select(e =>
{
dynamic x = new ExpandoObject();
var temp = x as IDictionary<string, Object>;
foreach (var property in properties)
temp.Add(property.Name, property.GetValue(e));
return x;
});
// itterate the results
foreach (var result in selectResult)
{
Console.WriteLine(result.Name);
Console.WriteLine(result.Type);
}
This code outputs:
First
TypeA
Second
TypeB

c# Lambda Expression - Get property value from string [duplicate]

This question already has answers here:
Get property value from string using reflection
(24 answers)
Closed 6 years ago.
Consider following lambda expression:
IQueryable<Product> query = query.Where(x => x.ProductName.Contains("P100"));
I need to convert above code something like this:
IQueryable<Product> query = query.Where(x => x.GetPropertyValue("ProductName").Contains("P100"));
Here I have added a dummy method GetPropertyValue("ProductName") to explain the requirement.
In above code the property should be resolved in run-time. In other words I need to access the property from a sting value E.g "ProductName"
How can I do this?
var parameterExp = Expression.Parameter(typeof(Product), "type");
var propertyExp = Expression.Property(parameterExp, propertyName);
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var someValue = Expression.Constant(propertyValue, typeof(string));
var containsMethodExp = Expression.Call(propertyExp, method, someValue);
Expression<Func<Product, bool>> predicate = Expression.Lambda<Func<T, bool>>
(containsMethodExp, parameterExp);
var query = query.Where(predicate);
You can have this extension method:
public static T GetPropertyValue<T>(this Product product, string propName)
{
return (T)typeof(Product).GetProperty(propName).GetValue(product, null);
}
Then:
IQueryable<Product> query = query.Where(x => x.GetPropertyValue<string>("ProductName").Contains("P100"));
Notice that this will not work with Entity Framework to query a database, but since you haven't tagged the question with entity framework, I'm not assuming you are using it

Apply a dynamically-built expression on non-generic DbSet

I have the following scenario:
I have an unknown at compile time DbSet, which I get it via its type like:
DbSet entities = _repository.Context.Set(myType)
I have a dynamically-built expression of a given type,
Expression myFilter; //built as an expression of myType, constructed at runtime
How can I apply myFilter on entities, in order to filter out the entities based on myFilter?
below is a code that might help you: it finally creates a IQueryable of myType that actually represents something like
SELECT * FROM YourMappedTable WHERE Id = 1 but of cource, instead of using my expression that i have built for demo purpose, you could use your expression.
class Program
{
static void Main(string[] args)
{
using (var x = new DB01Entities ())
{
Type myType = typeof(Angajati);
var setMethod = typeof(DB01Entities).GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public).Where (a => a.Name == "Set" && a.IsGenericMethod).First ().GetGenericMethodDefinition ();
var mySet = setMethod.MakeGenericMethod(myType);
var realSet = mySet.Invoke(x, null);
var param1 = Expression.Parameter(myType, "param1");
var propertyExpresion = Expression.Property(param1, "Id");
var idExpresssion = Expression.Constant(1);
var body = Expression.Equal(propertyExpresion, idExpresssion);
var lambda = Expression.Lambda(body, param1);
var genericTypeCaster = typeof(Program).GetMethod("Caster", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).GetGenericMethodDefinition();
var effectiveMethod = genericTypeCaster.MakeGenericMethod(myType);
var filteredQueryable = effectiveMethod.Invoke(null, new Object[] {realSet, lambda });
}
}
private static IQueryable<T> Caster <T> (DbSet<T> theSet, Expression whereCondition) where T : class
{
return theSet.Where(whereCondition as Expression<Func<T, bool>>);
}
}
So "lambda" variable above is the equivalent for your "myFilter". It must be at runtime an
Expression<Func<YourType, bool>>.
And mySet is your "entities' DbSet.
Happy coding!

Lambdaexpression of Linq-query in a variable

How can I define a lambdaexpression that I want to use in a linq query as a variable?
For example when sorting a generic list by different properties of the listitems:
IList<SampleClass> list = new List<SampleClass>();
// Populate list
...
list.OrderBy(sampleclass => sampleclass.property1);
list.OrderBy(sampleclass => sampleclass.property2);
I would like to define the lambda expression (sampleclass => sampleclass.property1) in a variable and call:
// ??? define expression in a variable ???
Expression expression = sampleclass => sampleclass.property1;
// Sort list by variable expression
list.OrderBy(expression);
Thanks in advance
Tobi
You can use one of Func overloads (Func<T, TResult> precisely):
Func<SampleClass, PropertyType> expr = sampleclass => sampleclass.property1;
list.OrderBy(expr);
PropertyType is the type of variable stored as property1 in your SampleClass. If it was for example string, you would use Func<SampleClass, string>.
Define a Func<TSampleClass, TPropertyType> as follows:
List<SampleClass> list = new List<SampleClass>();
Func<SampleClass, int> expr = (c) => c.SomeProperty;
_HungerLevel = level;
class SampleClass
{
public int SomeProperty { get; set; }
}
You can use:
Func<SampleClass, int> f = sampleClass => sampleClass.Property1;
list.OrderBy(f);
This presumes the type of Property1 is int.
You have almost already done it.
The parameter is any function taking an item from the sequence and giving its key as a result (the key on which the ordering will be done). A lambda expression is just a variety of such a function.
These notations are possible :
list.OrderBy(sampleclass => sampleclass.property1);
or
Func<SampleClass,string> getKey = sampleclass => sampleclass.property1;
list.OrderBy(getKey);
or
string GetKey(SampleClass sampleclass)
{
return sampleclass.property1;
}
list.OrderBy(GetKey);
(I supposed here that property1 was a string but it's of course not a requirement !)
Just like other people said, you can use Func<T, TResult> to store delegate to your function.
If you want to use something else than Linq-To-Objects, then you should enclose this in Expression too. Something like Expression<Func<T, TResult>>.
If you are talking purely about LINQ to Objects, there's no need for Expressions here because the argument to Enumerable.OrderBy is a Func delegate:
var list = new List<SampleClass> ();
Func<SampleClass, PropertyType1) orderSelector1 = (obj => obj.Property1); // parentheses for clarity
var sequence1 = list.OrderBy (orderSelector1);
Func<SampleClass, PropertyType2) orderSelector1 = (obj => obj.Property2);
var sequence2 = list.OrderBy (orderSelector2);
If you want to assign multiple times, you can make Func return object:
var list = new List<SampleClass> ();
Func<SampleClass, object> orderSelector;
orderSelector = (obj => obj.Property1);
var sequence1 = list.OrderBy (orderSelector);
orderSelector = (obj => obj.Property2);
var sequence2 = list.OrderBy (orderSelector);
If you truly want dynamic property selection, i.e. calling OrderBy with a property specified by a string, you would need Expressions. There are plenty of examples in this thread that allow you to do something like:
var list = new List<SampleClass> ();
var sequence1 = list.OrderBy ("Property1");
var sequence2 = list.OrderBy ("Property2");

Categories