Generic Func needed to perform sorting of Entity Framework collection - entity-framework

I have a grid with columns. When the grid column header is selected I post/ajax to server with header selected to return x rows.
In the following code, RefNo is integer while ProposalSectionNumber is a string.
How to make a generic function taking a string but return a Func to be used in the linq statement?
if (sort.dir == SortDirection.Asc)
{
switch (sort.field)
{
case "RefNo":
qry = qry.OrderBy(x => x.RefNo);
break;
case "ProposalSectionNumber":
qry = qry.OrderBy(x => x.ProposalSectionNumber);
break;
}
}
else
{
switch (sort.field)
{
case "RefNo":
qry = qry.OrderByDescending(x => x.RefNo);
break;
case "ProposalSectionNumber":
qry = qry.OrderByDescending(x => x.ProposalSectionNumber);
break;
}
}
I would like to do something like
string sortOrder = "RefNo"
var sortfunc = SortFunc(sortOrder)
if (sort.dir == SortDirection.Asc)
{
qry = qry.OrderBy(sortFunc)
}
else
{
qry = qry.OrderByDesc(sortFunc)
}
I have struggled creating the function SortFunc (which returns based on string or integer)
What is the best way to achieve this?

The problem with declaring a type for sortFunc is that it depends on the type of the field by which you sort. If all fields were of the same type, say, all strings, you could use the type of Expression<Func<MyEntity,string>> for your sortFunc variable.
There is another way of removing code duplication when sort fields do not share a common type. Introduce a generic helper method that takes sort order as a parameter, and call it instead of OrderBy/OrderByDescending:
private static IOrderedQueryable<T> AddOrderBy<T,TKey>(
IQueryable<T> orig
, Expression<Func<T,TKey>> selector
, bool isAscending
) {
return isAscending ? orig.OrderBy(selector) : orig.OrderByDescending(selector);
}
Now you can rewrite your code as follows:
var isAscending = (sort.dir == SortDirection.Asc);
switch (sort.field) {
case "RefNo":
qry = qry.AddOrderBy(x => x.RefNo, isAscending);
break;
case "ProposalSectionNumber":
qry = qry.AddOrderBy(x => x.ProposalSectionNumber, isAscending);
break;
}

Related

Why is generated query from expression using case instead of where statements?

I am working on a tool, that translates filter strings into linq/efcore expressions. Say following filter string is given:
{
'condition': 'OR',
'rules': [
{ 'column': 'Foo', 'value': 'Bar' },
{ 'column': 'Foo', 'value': 'Baz' },
]
}
Using an efcore extension I can filter a model like so:
_dbContext.MyModel
.Filter(filterString)
.ToList();
The issue is, that the generated query uses case instead of where statements:
SELECT
*
FROM
[MyModel] AS [c]
WHERE
(
CASE
WHEN [c].[Foo] = "Bar" THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END | CASE
WHEN [c].[Foo] = "Baz" THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END
) = CAST(1 AS bit)
instead of:
SELECT
*
FROM
[MyModel] AS [c]
WHERE ([c].[Foo] = "Bar" OR [c].[Foo] = "Baz")
The last query takes much less time. Why is case used instead of where? Is it possible to instruct the parser to use where?
Extension:
public static IQueryable<T> Filter<T>(this IQueryable<T> query, string? filter)
{
if (string.IsNullOrEmpty(filter))
{
return query;
}
Expression<Func<T, bool>>? predicate;
try
{
predicate = new FilterExpressionParser().ParseExpressionOf<T>(JsonDocument.Parse(filter));
}
catch (Exception ex)
{
throw new FilterException($"Filter \"{filter}\" could not be parsed into a predicate.", ex);
}
return query.Where(predicate);
}
The extension tries to parse the filter string as a json document and create an linq expression from it. The logic for that is inside the FilterExpressionParser:
using System.Linq.Expressions;
using System.Reflection;
using System.Text.Json;
public enum FilterConditionType
{
AND,
OR
}
public class FilterExpressionParser
{
public Expression<Func<T, bool>> ParseExpressionOf<T>(JsonDocument json)
{
var param = Expression.Parameter(typeof(T));
var conditions = ParseTree<T>(json.RootElement, param)!;
if (conditions.CanReduce)
{
conditions = conditions.ReduceAndCheck();
}
return Expression.Lambda<Func<T, bool>>(conditions, param);
}
private delegate Expression Binder(Expression? left, Expression? right);
private Expression? ParseTree<T>(JsonElement condition, ParameterExpression parameterExpression)
{
Expression? left = null;
var conditionString = condition.GetProperty(nameof(condition)).GetString()?.ToUpper();
if (!Enum.TryParse(conditionString, out FilterConditionType gate))
{
throw new ArgumentOutOfRangeException(nameof(condition), $"Not expected condition type: {condition}.");
}
JsonElement rules = condition.GetProperty(nameof(rules));
Binder binder = gate == FilterConditionType.AND ? Expression.And! : Expression.Or!;
Expression? bind(Expression? left, Expression? right) => left == null ? right : binder(left, right);
foreach (var rule in rules.EnumerateArray())
{
string? column = rule.GetProperty(nameof(column)).GetString();
object? toCompare = value.GetString().GetProperty(nameof(value));
Expression property = Expression.Property(parameterExpression, column);
BinaryExpression? right = Expression.Equal(property, Expression.Constant(toCompare, property.Type))
left = bind(left, right);
}
return left;
}
}
For combining predicates you have used bitwise operators Expression.And and Expression.Or Bitwise and shift operators
In C# generated result looks like
e => (e.Some > 1) & (e.Some < 10) | (e.Some == -1)
So, EF is also trying to convert bit operations to the SQL.
Instead of them use Expression.AndAlso and Expression.OrElse which are Boolean logical operators
e => (e.Some > 1) && (e.Some < 10) || (e.Some == -1)
For analysis generated expressions, I would suggest to use ReadableExpressions.Visualizers and probably you will find mistake by yourself.

Entity Framework - LINQ - Use Expressions in Select

I am using within my code some EF LINQ expressions to keep complex queries over my model in one place:
public static IQueryable<User> ToCheck(this IQueryable<User> queryable, int age, bool valueToCheck = true)
{
return queryable.Where(ToBeReviewed(age, valueToCheck));
}
public static Expression<Func<User, bool>> ToCheck(int age, bool valueToCheck = true)
{
return au => au.Status == UserStatus.Inactive
|| au.Status == UserStatus.Active &&
au.Age.HasValue && au.Age.Value > age;
}
I am then able to use them in queries:
var globalQuery = db.Users.ToCheck(value);
And also in selects:
var func = EntityExtensions.ToCheck(value);
var q = db.Department.Select(d => new
{
OrdersTotal = d.Orders.Sum(o => o.Price),
ToCheck = d.Users.AsQueryable().Count(func),
})
What I am trying to achieve is to actually use the same expression/function within a select, to evaluate it for each row.
var usersQuery = query.Select(au => new {
Id = au.Id,
Email = au.Email,
Status = au.Status.ToString(),
ToBeChecked = ???, // USE FUNCTION HERE
CreationTime = au.CreationTime,
LastLoginTime = au.LastLoginTime,
});
I am pretty that threre would be a way using plain EF capabilities or LINQKit, but can't find it.
Answering my own question :)
As pointed by #ivan-stoev, the use of Linqkit was the solution:
var globalQueryfilter = db.Users.AsExpandable.Where(au => au.Department == "hq");
var func = EntityExtensions.ToCheck(value);
var usersQuery = globalQueryfilter.Select(au => new
{
Id = au.Id,
Email = au.Email,
Status = au.Status.ToString(),
ToBeChecked = func.Invoke(au),
CreationTime = au.CreationTime,
LastLoginTime = au.LastLoginTime,
});
return appUsersQuery;
It's required to use the AsExpandable extension method from Linqkit along with Invoke with the function in the select method.
I want to add one more example:
Expression<Func<AddressObject, string, string>> selectExpr = (n, a) => n == null ? "[no address]" : n.OFFNAME + a;
var result = context.AddressObjects.AsExpandable().Select(addressObject => selectExpr.Invoke(addressObject, "1"));
Also, expression can be static in a helper.
p.s. please not forget to add "using LinqKit;" and use "AsExpandable".

MongoDB C# Combining Fields

The Plan:
So now what I basically want is to take my propertys out of the class, let the user pick some and then pull a List with ONLY those propertys out of MongoDB.
The Code:
here is where the method starts:
private void DoStuffExecute(object obj)
{
Class class= new Class();
ExtractClass(class);
if (propList != null)
{
var result = classService.DoStuff(propList);
}
}
in "ExtractClass()" the Propertys are being pulled out of the Class.
void ExtractClass(object obj)
{
foreach (var item in obj.GetType().GetProperties())
{
propList.Add(item.Name);
}
}
and finally in "classService.DoStuff()" i try to set the "fields".
public List<class> DoStuff(List<string> Props)
{
try
{
var filter = Builders<class>.Filter.Empty;
var fields = Builders<class>.Projection.Include(x => x.ID);
foreach (var item in Props)
{
string str = "x.";
str += item.ToString();
fields = Builders<class>.Projection.Include(x => str);
fields = Builders<class>.Projection.Include(x => item);
}
var result = MongoConnectionHandler.MongoCollection.Find(filter).Project<class>(fields).ToList();
return result;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
var result = new List<class>();
return result;
}
}
when i run the programm it gives me an "Unable to determine the serialization information for x=> value"... since im giving it a string.
The Question:
Does anyone have an Idea how to repair the code above or even make the plan work in another way?
thank you.
First of all: you are using such code lines as : var filter = Builders<class>.Filter.Empty; It is not possible, because class is a reserved keyword in c# (https://msdn.microsoft.com/en-us/library/x53a06bb.aspx) I assume, it's your Model, and i will speak about it as about Model class.
Include Filter needs Expression as a parameter, not a string, you should construct is as a expression. That's the second thing. Third, you should combine your includes as a chain, So your part of creating Include Filter from string List should look like:
var filter = Builders<Model>.Filter.Empty;
var fields = Builders<Model>.Projection.Include(x => x.Id);
foreach (var item in Props)
{
var par = Expression.Parameter(typeof(Model));
var prop = Expression.Property(par, item);
var cast = Expression.Convert(prop, typeof(object));
var lambda = Expression.Lambda(cast, par);
fields = fields.Include((Expression<Func<Model, object>>)lambda);
}
I have all expresiions separate for better understanding: first you create Parameter (x=>), than you add property (x=>x.Property1), than you should cast it to object, and after all create Lambda Expression from it.
And now the last part: You don't need all of it, Include function could get jsut a string as a parameter. So you could instead of all expression call write this:
fields = fields.Include(item);

How to declare instantiation in Haxe macro function

I want to create a macro that generates this code for me:
if (myEntity.get(Attack) == null) myEntity.add(new Attack());
if (myEntity.get(Confused) == null) myEntity.add(new Confused());
if (myEntity.get(Defend) == null) myEntity.add(new Defend());
if (myEntity.get(Offense) == null) myEntity.add(new Offense());
In code I'd like to declare/use it like this:
EntityMacroUtils.addComponents(myEntity, Attack, Confused, Defend, Offense);
The current macro function looks like this:
macro public static function addComponents(entity:ExprOf<Entity>, components:Array<ExprOf<Class<Component>>>):Expr
{
var exprs:Array<Expr> = [];
for (componentClass in components)
{
var instance = macro $e { new $componentClass() }; // problem is here
var expr = macro if ($entity.get($componentClass) == null) $entity.add(instance);
exprs.push(expr);
}
return macro $b{ exprs };
}
This macro function is incorrect, I get the error:
EntityMacroUtils.hx:17: characters 22-43 : Type not found : $componentClass
The problem is I don't know how to define new $componentClass(). How would I solve this?
I also want to avoid to have Type.createInstance in the output code.
One way to programmatically generate instantiation code is by using "old school" enums AST building (compatible Haxe 3.0.1+):
// new pack.age.TheClass()
return {
expr:ENew({name:"TheClass", pack:["pack", "age"], params:[]}, []),
pos:Context.currentPos()
};
An improved syntax using reification is possible:
// new pack.age.TheClass()
var typePath = { name:"TheClass", pack:["pack", "age"], params:[] };
return macro new $typePath();
Now, for a convenient "instantiation helper" function syntax, we need to do some contorsions to extract a type path from the expression we receive in the macro function:
// new Foo(), new pack.Bar(), new pack.age.Baz()
instantiate(Foo, pack.Bar, pack.age.Baz);
macro static function instantiate(list:Array<Expr>)
{
var news = [for (what in list) {
var tp = makeTypePath(what);
macro new $tp();
}];
return macro $b{news};
}
#if macro
static function makeTypePath(of:Expr, ?path:Array<String>):TypePath
{
switch (of.expr)
{
case EConst(CIdent(name)):
if (path != null) {
path.unshift(name);
name = path.pop();
}
else path = [];
return { name:name, pack:path, params:[] };
case EField(e, field):
if (path == null) path = [field];
else path.unshift(field);
return makeTypePath(e, path);
default:
throw "nope";
}
}
#end
In case anyone is in need for answers, I got this Thanks to ousado on the Haxe IRC chat:
If you do it in macro alone you can do this:
var ct = macro : pack.age.SomeTypename;
var tp = switch ct { case TPath(tp):tp; case _: throw "nope"; }
var expr = macro new $tp();
..or, if you explicitly construct tp:
var tp = {sub:'SomeTypeName',params:[],pack:['pack','age'],name:"SomeModuleName"}
As you can see, the complex type path is explicitly given here.
Unfortunately, Haxe don't really have a concise syntax for types in expression positions. You can pass ( _ : TypeName ) to provide an expression that contains a ComplexType.
But if you want to pass a type as argument, you could do it like this:
import haxe.macro.Expr;
using haxe.macro.Tools;
class Thing {
public function new(){}
}
class OtherThing {
public function new(){}
}
class TMacroNew {
macro static function instances( arr:Array<Expr> ) {
var news = [for (e in arr) {
var ct = switch e.expr { case EParenthesis({expr:ECheckType(_,ct)}):ct; case _: throw "nope"; };
var tp = switch ct { case TPath(tp):tp; case _: throw "nope"; };
macro new $tp();
}];
trace( (macro $b{news}).toString());
return macro $b{news};
}
static function main(){
instances( (_:Thing), (_:Thing), (_:OtherThing) );
}
}
..if you want a list of types, you might want to go for a parameter list like ( _ : L< One,Two,Three> ).
The accepted answer is problematic because it breaks when type parameters are involved, or when support for non-nominal types should be included.
I updated the example using two alternatives for a more concise notation for the list of types, while still allowing syntax for actual types.
import haxe.macro.Expr;
using haxe.macro.Tools;
class Thing {
public function new(){}
}
class OtherThing {
public function new(){}
}
class TPThing<T>{
public function new(){}
}
class TMacroNew {
macro static function instances( e:Expr ) {
var tps = switch e.expr {
case EParenthesis({expr:ECheckType(_,TPath({params:tps}))}):tps;
case ENew({params:tps},_):tps;
case _: throw "not supported";
}
var type_paths = [ for (tp in tps) switch tp {
case TPType(TPath(tp)):tp;
case _: throw "not supported";
}];
var news = [for (tp in type_paths) macro new $tp()];
trace( (macro $b{news}).toString());
return macro $b{news};
}
static function main(){
instances( (_:L<Thing,Thing,OtherThing,TPThing<Int>> ) );
instances( new L<Thing,Thing,OtherThing,TPThing<Int>>() );
}
}
Edit:
The L in L< ... > could be any valid type name. Its only purpose is allowing to write a comma-separated list of types in valid syntax. Since macro functions take expressions as arguments, we have to use an expression that allows/requires a type, like: ( _ :T ), new T(), var v:T, function(_:T):T {}.

How to create and return an Expression<Func

I Use entity Framework 4.
I would like to be able to create a function that return an Expression func that will be use in a lambda expression.
var ViewModel = _db.Suppliers.Select(model => new {
model,SupType = model.SupplierType.SupplierTypeTexts.Where( st => st.LangID == 1)
});
I would like to make this call like that
var ViewModel = _db.Suppliers.Select(model => new {
model,SupType = model.SupplierType.GetText()
});
My Partial class is:
public partial class SupplierType
{
public Expression<Func<SupplierTypeText, bool>> GetText()
{
return p => p.LangID == 1;
}
How can i perform this.
Easy. For example, Let's assume you have a Product table that is mapped to Products EntitySet in your context, now you want to pass a predicate and select a Product:
Expression<Func<Product, bool>> GetPredicate(int id) {
return (p => p.ProductID == id);
}
You can call GetPredicate() with a Product ID to filter based on that:
var query = ctx.Products.Where(GetPredicate(1)).First();
The point really is that you can always pass a Lambda Expression to where an Expression<T> is needed.
EDIT:
You should change your code like this:
var ViewModel = _db.Suppliers.Select(model => new {
model,
SupType = model.SupplierType.SupplierTypeTexts.Where(GetText())
});
public Expression<Func<SupplierTypeText, bool>> GetText() {
return (stt => stt.LangID == 1);
}
If you want to dynamically create compiled Expression at runtime (as opposed to ones hardcoded against a particular data model at compile time) you need to use the static methods on the Expression class.