Linq.Dynamic FirstOrDefault() nested in OrderBy - c#

I have the following Linq statement, which works totally fine:
query = query.OrderBy(m => m.MATERIAL_TXT.Where(mt => mt.LANG == "EN").FirstOrDefault().LTEXT);
Now I'm trying to make it dynamic by using the string based syntax from Linq.Dynamic:
query = query.OrderBy("MATERIAL_TXT.Where(LANG==\"EN\").FirstOrDefault().LTEXT");
But it throws the exception :
"No applicable aggregate method 'FirstOrDefault' exists"
It has to bedynamic so that it accepts other names instead of "MATERIAL_TXT".
What am I missing?

According to the documentation:
A subset of the Standard Query Operators is supported for objects that implement IEnumerable. Specifically, the following constructs are permitted, where seq is an IEnumerable instance, predicate is a boolean expression, and selector is an expression of any type:
seq.Where(predicate)
seq.Any()
seq.Any(predicate)
seq.All(predicate)
seq.Count()
seq.Count(predicate)
seq.Min(selector)
seq.Max(selector)
seq.Sum(selector)
seq.Average(selector)
FirstOrDefault isn't on the list, so it's reasonably safe to assume it isn't supported.

You can't use FirstOrDefault as string like that.
if you want to create dynamic orderBy try this :
Func<IQueryable<YourEntityType>, IOrderedQueryable<YourEntityType>> orderBy;
orderBy = x => x.OrderBy(m => m.MATERIAL_TXT.Where(mt => mt.LANG == "EN").FirstOrDefault().LTEXT);
Then you can use it like this :
orderBy(query);
for example you can use it in another method :
public List<YourEntityType> YourMethodName(Func<IQueryable<YourEntityType>, IOrderedQueryable<YourEntityType>> orderBy,IQueryable<YourEntityType> query=null)
{
query=query ?? GetYourEntityTypeList().AsQueryable();
return orderBy(query).ToList();
}
I Hope it will be useful .

Related

Using Custom Extension Methods inside LinQ (Compare two strings)

I wanna create a comparer for LinQ that is simplified. this is my current code.
DBContext.product_type
.Where(x => x.name.ToLower()(filter.ToLower())
|| x.description..ToLower()(filter.ToLower())
|| x.online_category.description..ToLower()(filter.ToLower()))
I wanna simplify it using linQ extension method. So far this is what I got.
DBContext.product_type
.Where(x => x.name.CompareToLower(filter)
|| x.description.CompareToLower(filter)
|| x.online_category.description.CompareToLower(filter))
public static bool CompareToLower(this string str, string comparer)
{
if (str.ToLower().Contains(comparer.ToLower()))
{
return true;
}
else
{
return false;
}
}
This is the error im getting
linq to Entities does not recognize the method 'Boolean CompareToLower(System.String, System.String)' method, and this method cannot be translated into a store expression.
Extension methods like that cannot be converted to SQL Query.
The answer provided by #meysamasadi will work, but will fetch your entire table and do the comparison on client side.
If you want the comparison to occur on the database-side, the most efficient way would be to change your collation to case-insensitive (as suggested in the comments).
As an alternative you can use following code:
DBContext.product_type
.Where(x =>
EF.Functions.Like(x.Name, filter)
|| EF.Functions.Like(x.Description, filter)
|| EF.Functions.Like(x.online_category.description, filter))
** Note that using the above method you can also include wildcards in the filter and patterns
https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.dbfunctionsextensions.like?view=efcore-5.0

Return List<object> from IEnumerable C#

I have a method:
public List<AxeResult> LoadAxes(string kilometriczone, string axe)
{
IEnumerable<AxeResult> allAxes = PullAxes();
var findAxes = allAxes.Select(a => a.KilometricZone.StartsWith(kilometriczone) || a.KilometricZone.StartsWith(axe));
return findAxes.Cast<AxeResult>().ToList();
}
I have this error:
IEnumerable<bool> does not contain a definition for ToList and
the best extension method overload Enumerable.ToList<AxeResult> (
IEnumerable<AxeResult>) requires a receptor type IEnumerable<AxeResult>
I want to return a List of AxeResult after the search operation.
What you want is to filter the collection. That's what Enumerable.Where is for:
public List<AxeResult> LoadAxes(string kilometriczone, string axe)
{
return PullAxes()
.Where(a => a.KilometricZone.StartsWith(kilometriczone) ||
a.KilometricZone.StartsWith(axe))
.ToList();
}
Some explanation in addition to the given answers:
Where() method acts like a filter and returns a subset of the same set
Select() method makes projection and returns new set
There is comprehensive explanation about the diffrence between .Select() and Where() methods here.

Combining expression trees

I have the following expression:
public Expression<Func<T, bool>> UserAccessCheckExpression<T>(int userId) where T : class
{
return x => (IsAdmin || userId == CurrentUserId || userId == 0);
}
Then I want to apply this filter to several collections (IQueryable) like this one:
return tasks
.Where(t => t.TaskUsers
.Any(x => UserAccessCheckExpression<TaskUser>(x.User) && x.SomeBool == true));
I'm getting the following error while doing so:
Error 40 Cannot implicitly convert type System.Linq.Expressions.Expression<System.Func<TaskUser,bool>> to bool
I can't use workaround with interface inheritance (like TaskUser inherits interface with int UserId property (where T : IHasUserId)) since I want to combine logic.
The problem is that your UserAccessCheckExpression() method is returning an Expression while the Any() method is expecting a boolean.
Now, you can get your code to compile by compiling the Expression and invoking the method (using UserAccessCheckExpression<TaskUser>(x.User).Compile().Invoke(x.User)) but that would obviously fail on runtime because Linq-to-Entities wouldn't be able to translate your Any() to a store query as it no longer contains an Expression.
LinqKit is aiming to solve this problem using its own Invoke extension method that while letting your code compile, will make sure your Expression will get replaced back to its original form using another extension method named AsExpandable() that is extending the entity set.
Try this:
using LinqKit.Extensions;
return tasks
.AsExpandable()
.Where(t => t.TaskUsers.Any(
x => UserAccessCheckExpression<TaskUser>(x.User).Invoke(x)
&& x.SomeBool == true));
More on LinqKit
Yeah, so, you can't do that. There's a difference between an Expression<> and a Func<>. You're trying to use the UserAccessCheckExpression as a func. I'm not sure what you're trying to do, but you can compile it to a func and then use it sorta like you are:
var expr = UserAccessCheckExpression<TaskUser>(x.User);
var func = expr.Compile();
// Later use it like ...
var result = func();
But I expect you're using this with EF or Linq2Sql? That being the case you'll need to rewrite the expression. It can be done by hand (not easy) or, better, use a tool like PredicateBuilder.

Reuse of a LINQ query

This is not about the reuse of a result but more the statement itself.
Nor is it about an error when using var as mentioned in: LINQ to SQL: Reuse lambda expression
Out of sheer curiosity I was wondering if it is possible to reuse a single LINQ statement.
Lets say I have the following LINQ statement:
.Where(x => x.Contains(""));
Is it possible to extract the statement x => x.Contains("") and use some kind of reference to this for later usage in, lets say, another class?
So I can call it like: .Where(previouslySavedStatement);
You can store it in a variable. If you are working with IQueryable then use:
System.Linq.Expressions.Expression<Func<Foo, bool>> selector = x => x.Contains("");
If you are using IEnumerable then use:
Func<Foo, bool> selector = x => x.Contains("");
And use it in your query:
query.Where(selector);
Yes, you can write a function containing the query you want to reuse, which takes and returns an IQueryable<T>
public IQueryable<T> ContainsEmpty(IQueryable<T> query)
{
return query.Where(x => x.Contains(""));
}
Now you can reuse it:
query1 = ContainsEmpty(query1);
query2 = ContainsEmpty(another);
It depends. There's two Where methods, Enumerable.Where and Queryable.Where. If you're applying the .Where to an IEnumerable than the first one is called, if you're applying it to an IQueryable the second one is called.
Since Enumerable.Where takes in a Func, it isn't reusable. Since Queryable.Where takes in an expression, it is reusable. You can do so as follows:
var x = new List<string>().AsQueryable();
var query = x.Where (n => n.Contains("some string"));
//Extract the lambda clause
var expr = query.Expression;
var methodExpr = (MethodCallExpression)expr;
var quoteExpr = (UnaryExpression)methodExpr.Arguments[1];
var funcExpr = (Expression<Func<string, bool>>)quoteExpr.Operand;
You can then later re-apply the where expression:
var query2 = x.Where(funcExpr);
I wrote a library to address exactly this concern, it's called CLinq and you can find an implementation for the EntityFramework here: https://www.nuget.org/packages/CLinq.EntityFramework
It allows to create query snippets and use them everywhere you in a linq query. Following the example of Hamid, create the following expression:
System.Linq.Expressions.Expression<Func<Foo, bool>> selector = x => x.Contains("");
You can now use this query everywhere in your linq queries like this:
query.AsComposable().Where(o => selector.Pass(o));
Additionally to this simple example you're also able to combine your query snippets:
query.AsComposable().Where(o => selector.Pass(o) || anotherSelector.Pass(o));
or even merge them together:
query.AsComposable().Where(o => anotherSelector.Pass(selector.Pass(o)));
There's some more features, but I think it's really helpful, so check it out :)

How to build dynamic query by expression in c#

First, I have a variable like below:
List<string> values;
Now I need build query condition like below:
Expression<Func<docinstance, bool>> filter = d=>d.values.any(o=>o.value==values[0]||o.value==value[1]||.....)
Because I don't know how many items in variable values, so how can I build the query condition
You can use Any again inside
d => d.values.Any(o => values.Any(x => x == o.value))
You can use Contains:
d => d.values.Any(o => values.Contains(o.value))
See Documentation
The DbContext class of EF has a method Set<TEntity>() which return type is DbSet<TEntity> which implements Queryable interface which has all the query methods as extension methods.
At the end your query expression in translated into SQL and then executed. Which gives you the desired capability so that you can combine query methods and construct dynamic expressions.

Categories

Resources