NHibernate Linq request .All method not supported - c#

I would use a .All method in my request, but it seems, it isn't supported.
I have a parameterList, wich contains elements with a value and a name (like a dictionary) and others things.
And Parameters, a list of elements with Value and Name.
All my element in the first list must exist in the second.
The request I would use is :
linq.Where(u => (u.ParametersList.All(param =>
(Parameters.Any(p =>
p.Value== param.Value && p.Name== param.Name)))));
If you have an idea for use something else than the .All, I listen to you :)
I tried
!u.ParametersList.Any(param =>
!(Parameters.Any(p =>
p.Value== param.Value && p.Name== param.Name)));
but I guess Nhibernate don't make differences
I tried also
List<System.Tuple<String, String>> ParamTuples = Parameters.Select(p => new System.Tuple<String, String>(p.Value, p.Name)).ToList();
So, ParamTuples the elements of my second list
linq = linq.Where(url => (url.ParametersList.Any(param =>
ParamTuples.Any(p => p.Item1 == param.Value && p.Item2 == param.Name))));
But it didn't worked neither. Those methods are not supported.

just to give the idea you'll need to count the parameter matches per containing element id and match them with the filterparameter count
class Parameter
{
public virtual Entity Parent { get; set; }
public virtual string Name { get; set; }
public virtual string Value { get; set; }
}
ICriteria filter;
foreach(var param in parameterList)
{
var crit = Expression.And(Expression.Eq("Name", param.Name), Expression.Eq("Value", param.Value);
filter = (filter == null) ? crit : Expression.Or(filter, crit);
}
var subquery = QueryOver.Of<Parameter>()
.Where(filter)
.Select(Projections.Group("Parent.Id"));
.Where(Restrictions.Eq(Projections.Count<Parameter>(p => p.Id), parameterList.Count));
var results = QueryOver.Of<Entity>()
.WuithSubquery.WhereProperty(e => e.Id).IsIn(subquery)
.List();
Update: as from your answer the above matches all but you want any (off the top of my head)
var predicate = PredicateBuilder.False<Parameter>();
foreach (var param in Parameters)
{
predicate = predicate.Or(p => p.Name == param.Name && p.Value == param.Value);
}
// building (u => u.ParametersList.Any(predicate))
var u = Expression.Parameter(typeof(User), "u");
var parametersproperty = Expression.Property(u, "ParametersList");
var anyCall = Expression.Call(parametersproperty, typeof(Queryable).Getmethod("Any"), predicate);
var lambda = Expression.Lambda<User, bool>(u, anyCall);
linq = linq.Where(lambda);

Related

Dynamic Expression Parameter

How can i make dynamic expression parameter ?
i was try
public List<Station> GetStationsByTimeAndState(StationState stationState, int? categoryId = null, int? provinceId = null, int? districtId = null)
{
List<Station> stations;
Expression<Func<Station, bool>> exp = p => p.StationState == stationState;
if(categoryId==null){
exp+= p.CategoryId==categoryId;//or exp.Add()//exp.Update()
}
.....
return stations = stationDal.GetList(exp);
}
The GetList method calls the Where query that belongs to Linq in itself.
What I want to do is dynamically constructing and sending the expression inside the Where query.
You need to build a Linq Expression Tree for your example this would look like this:
// Parameter: p
Expression<Func<Station, bool>> exp;
var parameterExpression = Expression.Parameter(typeof(Station));
// p == stationState
var equalsStationState = Expression.Equal(
Expression.Property(stateParameterExpression, nameof(Station.StationState))
Expression.Constant(stationState));
if (categoryId != null)
{
// p.CategoryId == categoryId
var equalsCategory = Expression.Equal(
Expression.Property(stateParameterExpression, nameof(Station.CategoryId)),
Expression.Constant(categoryId));
// (p == stationState) && (p.CategoryId == categoryId)
var andExpression = Expression.AndAlso(equalsStationState, equalsCategory);
// p => (p == stationState) && (p.CategoryId == categoryId)
exp = predicate = Expression.Lambda(
andExpression,
stateParameterExpression);
}
The code above is not complete, but you can get an idea on how it works.
As an alternative you could conditionally chain Where methods, which would be much simpler:
IEnumerable<StationList> stations = allStations
.Where(s => s.StationState == stationState);
if (categoryId != null)
{
stations = stations.Where(s => s.CategoryId == category)
}
return stations;

How to make an List of Objects a parameter in a Lamba using C# Expression API

I'm trying to dynamically create an predicate to pass into a linq where clause. This is for a generic method that takes two list of the same Type and and list of property names to compare.
void SomeMethod<T>(List<T> oldRecords, List<T> newRecords, List<string> propertiesOfT)
{
// dynamically build predicate for this
var notMatch = oldRecords.Where(o => !newRecords.Any(n => n.Prop1 == o.Prop1 && n.Prop2 == o.Prop2)).ToList();
// do somethind with notMatch
}
I would like to convert this:
var notMatch = oldRecords.Where(o => !newRecords.Any(n => n.Prop1 == o.Prop1 && n.Prop2 == o.Prop2)).ToList();
To achieve this:
var predicate = "n => n.Prop1 == o.Prop1 && n.Prop2 == o.Prop2"; // sudo code
var notMatch = oldRecords.Where(o => !newRecords.Any(predicate));
or this
var predicate = "o => !newRecords.Any(n => n.Prop1 == o.Prop1 && n.Prop2 == o.Prop2)" // sudo code
var notMatch = oldRecords.Where(predicate);
How do I populate newRecords when dynamically creating the Expression?
And how would I reference parameter o and parameter n in the Expresssion.
I've gotten this far:
//construct the two parameters
var o = Expression.Parameter(typeof(T), "o");
var n = Expression.Parameter(typeof(T), "n");
// How to I go about populating o with values and n with values
// from oldRecords and newRecords? or is that no neccessary
var property = Expression.Property(o, typeof(T).GetProperty("Id").Name);
var value = Expression.Constant(Convert.ChangeType("12345", typeof(T).GetProperty("Id").PropertyType), typeof(T).GetProperty("Id").PropertyType);
BinaryExpression binaryExpression = Expression.MakeBinary(ExpressionType.Equal, property, value);
Any sudo code or clue where to look to achieve this?
With reflection it's quite easy. You just to have to think about it. Here's the working version.
void SomeMethod<T>(List<T> oldRecords, List<T> newRecords, List<string> propertiesOfT)
{
// get the list of property to match
var properties = propertiesOfT.Select(prop => typeof(T).GetProperty(prop)).ToList();
// Get all old record where we don't find any matching new record where all the property equal that old record
var notMatch = oldRecords.Where(o => !newRecords.Any(n => properties.All(prop => prop.GetValue(o).Equals(prop.GetValue(n))))).ToList();
}
And here's a sample set i tried and it works
public class test
{
public int id { get; set; } = 0;
public string desc { get; set; } = "";
public test(string s, int i)
{
desc = s;id = i;
}
}
private void Main()
{
var oldRecords = new List<test>()
{
new test("test",1),
new test("test",2)
};
var newRecords = new List<test>()
{
new test("test1",1),
new test("test",2)
};
SomeMethod(oldRecords, newRecords, new List<string>() { "id", "desc" });
}

C# IQueryable use dictionary inside Where throws Unable to create a constant value of type Exception

I am using the dictionary inside the IQueryable lambda linq throws the
Unable to create a constant value of type 'System.Collections.Generic.KeyValuePair`2
Code :
Dictionary<int, int> keyValues = new Dictionary<int, int>();
IQueryable<Account> = context.Account
.Where(W => keyValues
.Where(W1 => W1.Key == S.AccountID)
.Where(W1 => W1.Value == S.Balance)
.Count() > 0);
Details:
I have the data inside the dictionary like this
AccountID Balance
1 1000
2 2000
3 3000
I want the user which have the (ID = 1 AND Balance = 1000) OR (ID = 2 AND BALANCE = 2000) OR (ID = 3 AND BALANCE = 3000)
So how can I write the lambda for it ?
Edited
Thanks #caesay, Your answer help me lots.
I want one more favor from you.
From you answer I create the expression which look like below:
private static Expression<Func<Accounting, bool>> GenerateExpression(Dictionary<int, int> lstAccountsBalance)
{
try
{
var objAccounting = Expression.Parameter(typeof(Accounting));
Expression expr = null;
const bool NOT_ALLOWED = false;
if (lstAccountsBalance != null && lstAccountsBalance.Count > 0)
{
var clauses = new List<Expression>();
foreach (var kvp in lstAccountsBalance)
{
clauses.Add(Expression.AndAlso(
Expression.Equal(Expression.Constant(kvp.Key), Expression.Property(objAccounting, nameof(Accounting.ID))),
Expression.Equal(Expression.Constant(kvp.Value), Expression.Property(objAccounting, nameof(Accounting.Balance)))
));
}
expr = clauses.First();
foreach (var e in clauses.Skip(1))
{
expr = Expression.OrElse(e, expr);
}
var notAllowedExpr = Expression.AndAlso(
Expression.Equal(Expression.Constant(NOT_ALLOWED), Expression.Property(objAccounting, nameof(Accounting.ALLOWED))),
Expression.Equal(Expression.Constant(true), Expression.Constant(true))
);
expr = Expression.And(notAllowedExpr, expr);
}
var allowedExpr = Expression.AndAlso(
Expression.Equal(Expression.Constant(!NOT_ALLOWED), Expression.Property(objAccounting, nameof(Accounting.ALLOWED))),
Expression.Equal(Expression.Property(objAccounting, nameof(Accounting.ID)), Expression.Property(objAccounting, nameof(Accounting.ID)))
);
if (expr != null)
{
expr = Expression.OrElse(allowedExpr, expr);
}
else
{
expr = allowedExpr;
}
return Expression.Lambda<Func<Accounting, bool>>(expr, objAccounting);
}
catch (Exception ex)
{
throw objEx;
}
}
After that I compiled the expression like this:
Expression<Func<Accounting, bool>> ExpressionFunctions = GenerateExpression(lstAccountsBalance);
var compiledExpression = ExpressionFunctions.Compile();
And I used like this:
.Select(S => new
{
Accounting = S.Accounts
.Join(context.AccountInfo,
objAccounts => objAccounts.ID,
objAccountInfo => objAccountInfo.ID,
(objAccounts, objAccountInfo) => new Accounting
{
ID = objAccounts.ID,
Balance = objAccountInfo.Balance,
})
.Where(W => W.ID == user.ID)
.AsQueryable()
.Where(W => compiledExpression(W))
.Select(S1 => new Accounting()
{
ID = S1.ID,
Balance = S1.Balance
})
.ToList(),
}
And it throws the exception with the message:
System.NotSupportedException: The LINQ expression node type 'Invoke'
is not supported in LINQ to Entities.
Without Compile
Without the compile it works like charm. It gives the output want I want.
Expression<Func<Accounting, bool>> ExpressionFunctions = GenerateExpression(lstAccountsBalance);
Use:
.Select(S => new
{
Accounting = S.Accounts
.Join(context.AccountInfo,
objAccounts => objAccounts.ID,
objAccountInfo => objAccountInfo.ID,
(objAccounts, objAccountInfo) => new Accounting
{
ID = objAccounts.ID,
Balance = objAccountInfo.Balance,
})
.Where(W => W.ID == user.ID)
.AsQueryable()
.Where(ExpressionFunctions)
.Select(S1 => new Accounting()
{
ID = S1.ID,
Balance = S1.Balance
})
Thank you..
Everything inside of a EF linq query needs to be compiled to an Expression tree, and then to SQL, but the dictionary is an IEnumerable so there is no way that EF could know how to compile that.
You can either build the expression tree yourself, or use the System.Linq.Dynamic nuget package to build the sql yourself.
The example using System.Linq.Dynamic, first install the nuget package and add the using to the top of your file, then there will be a Where overload that takes a string as a parameter:
context.Account.Where(
String.Join(" OR ", keyValues.Select(kvp => $"(ID = {kvp.Key} AND Balance = {kvp.Value})")));
Essentially, everything inside of the Where clause will be executed directly as SQL, so be careful to use the Where(string, params object[]) overload to paramaterize your query if accepting user input.
The expression tree (untested) approach might look like the following:
var account = Expression.Parameter(typeof(Account));
var clauses = new List<Expression>();
foreach(var kvp in keyValues)
{
clauses.Add(Expression.AndAlso(
Expression.Equal(Expression.Constant(kvp.Key), Expression.Property(account, nameof(Account.AccountID))),
Expression.Equal(Expression.Constant(kvp.Value), Expression.Property(account, nameof(Account.Balance)))
));
}
var expr = clauses.First();
foreach (var e in clauses.Skip(1))
expr = Expression.OrElse(e, expr);
context.Account.Where(Expression.Lambda<Func<Account, bool>>(expr, account));
Essentially inside the foreach loop we're creating all of the (ID = ... AND Balance = ...) and then at the end we join them all with an OR.

Entity Framework building Where clause on the fly using Expression

Using Entity Framework C# and have this query, I need the part where it says:
where x.Login_Status == "Submitted"
to be dynamic. There are different cases it could be "Submitted" or null or something else and instead of writing multiple if statement with different queries in it, want to have a Predicate in a where clause.
status = (from x in ctx.table
where x.Login_Status == "Submitted"
orderby x.SUB_DATE descending
select new Model_Table()
{
Id = x.ID,
Name = x.NAME,
Code = x.Code,
DateSubmitted = x.SUB_DATE
}).ToList<Model_Table>();
Is that possible?
Solution:
Inside the if statement when more parameters encountered use this
where_expression = x => x.Login_Status == "Submitted" || x.Login_Status == null;
Here is a complete code that worked for me, anything between square brackets replace to suit your code:
Expression<Func<[Replace with your Entity], bool>> where_submitted = x => x.Login_Status == "Submitted";
// Check if all selected
if (CheckBox_Show_All_Submitted.Checked)
{
where_submitted = x => x.Login_Status == "Submitted" || x.Login_Status == null;
}
status =
ctx.[Replace with your Entity Table]
.Where(where_submitted)
.OrderByDescending(x => x.SUB_DATE)
.Select(x => new Model_Table
{
Id = x.ID,
Name = x.NAME,
Code = x.Code,
DateSubmitted = x.SUB_DATE
}).ToList<Model_Table>();
You need an Expression<Func<Entity,bool>>, not a Predicate<Entity>. The difference is that a predicate is a compiled delegate, and an expression is code as data and thus can be translated to SQL.
Here is an example:
//You can have this expression have different values based on your logic
Expression<Func<Entity,bool>> where_expression = x => x.Login_Status == "Submitted";
var query =
ctx.Table
.Where(where_expression)
.OrderByDescending(x => x.SUB_DATE)
.Select(x => new Model_Table())
{
Id = x.ID,
Name = x.NAME,
Code = x.Code,
DateSubmitted = x.SUB_DATE
}).ToList();
Please note that you need to replace Entity with the name of the real class.
Create an extension method for IQueryable like this:
public static class MethodExtensions{
public static IEnumerable<Model_Table> Query(this IQueryable<TEntity> source, string data){
return (from x in source
where x.Login_Status == data
orderby x.SUB_DATE descending
select new Model_Table()
{
Id = x.ID,
Name = x.NAME,
Code = x.Code,
DateSubmitted = x.SUB_DATE
}).ToList<Model_Table>();
}
}
Now you can use it like this:
var result = ctx.table.Query("somethingelse");

Build a dynamic where clause over multiple properties

What I have is a List<string> IndexFields which contains a list of property names.
My issue is that I need to build a where clause based on the elements in the list.
So far I have;
var sitem = List1.Where(p => (p.GetType().GetProperty(IndexFields[0])
.GetValue(p, null) as string) == "red").FirstOrDefault();
But that only allows me to specify a single property. What I need is a builder that can build based on all the names in the List<string> IndexFields list.
The most flexible way to create dynamic queries at runtime is by using the Expression API:
For example:-
var type = typeof(T);
var properties = IndexFields.Select(x => type.GetProperty(x));
// x
var paramter = Expression.Parameter(type);
// x.Foo, x.Bar, ...
var leftHandSides = properties.Select(
x => Expression.Property(parameter, x));
// "Baz"
var rightHandSide = Expression.Constant(...);
// x.Foo == "Baz", x.Bar = "Baz", ...
var equalityExpressions = leftHandSides.Select(
x => Expression.Equal(x, rightHandSide));
// x.Foo == "Baz" && x.Bar == "Baz" && ...
var aggregatedExpressions = equalityExpressions.Aggregate(
(x, y) => Expression.AndAlso(x, y));
// x => x.Foo == "Baz" && x.Bar == "Baz" && ...
var lambda = Expression.Lambda<Func<T,bool>>(
aggregatedExpressions, parameter)
var item = List1.Where(lambda).FirstOrDefault();
A huge advantage of building your queries like this is that the resulting expression can still be e.g. translated into SQL for use with Entity Framework, wheras using reflection inside the body of your lambda is really limiting.
I really do recommend taking some time to really understand the expression framework before using it, though. If you can grok what's going on, it saves you a ton of time in the long run.
You can read more at e.g:-
http://www.digitallycreated.net/Blog/37/dynamic-queries-in-entity-framework-using-expression-trees
https://stackoverflow.com/questions/1217539/net-expression-trees-tutorial
If you're looking for something quicker and dirtier, however, you can just go ahead and chain up those Where clauses inside a foreach:-
IEnumerable<T> query = List1;
foreach (var property in IndexFields)
{
// The variable "property" gets hoisted out of local context
// which messes you up if the query is being evaluated with
// delayed execution.
// If you're working in C# 6 though, you don't need to do this.
var localProperty = property;
query = query.Where(
p => (p.GetType().GetProperty(localProperty)
.GetValue(p, null) as string) == "red");
}
var sitem = query.FirstOrDefault();
You can use a PredicateBuilder for this:
var predicate = PredicateBuilder.New<string>();
if (aCondition)
{
predicate = predicate.And(s => s == "this");
}
if (bCondition)
{
predicate = predicate.And(s => s == "that");
}
you can try something like this, worked for me in linqpad
void Main() {
var listFields = new string[] { "Field1", "Field2" };
var listValues = new string[] { "value1", "value2" };
// prepare & show dummy data
var listItems = Enumerable.Range(1, 100).Select(aaIndex => new MyItem {
Name = string.Format("item{0}", aaIndex),
Field1 = string.Format("value{0}", aaIndex % 3),
Field2 = string.Format("value{0}", aaIndex % 7)
});
listItems.Dump();
// apply filtering
var filtered = listItems.Where(aaItem => Enumerable.Range(0, listFields.Length).All(aaIndex => {
var value1 = aaItem.GetType().GetProperty(listFields[aaIndex]).GetValue(aaItem, null);
var value2 = listValues[aaIndex];
if (value1 is IComparable) {
return ((IComparable)value1).CompareTo(value2) == 0;
}
return Convert.ToString(value1) == Convert.ToString(value2);
}));
filtered.Dump();
}
// Define other methods and classes here
class MyItem {
public string Name { get; set; }
public string Field1 { get; set; }
public string Field2 { get; set; }
}
The dynamic linq library worked well for stuff like this:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
It added overloads to take the different clauses as strings ie:
var query = List1.Where("Color1=""Red"" or Color2=""Red""");
In your case you could build the string from your index fields (probably in a loop but simplified here)
var query = List1.Where(IndexFields[0] + "=""Red"" or " IndexFields[1] + "=Red");
For use, download the sample package, and then grab LinqSamples\DynamicQuery\DynamicQuery\Dynamic.cs and compile with your project.

Categories