I've recently started playing with Linq (cue groans), and am trying to get the following to compile. Now, the whereclause part uses DynamicLinq, which works fine; it's the var placeholder variable that the compiler wants a real class for; unfortunately, I am using what I believe is an anonymous class, and am not sure how to take it from here. Any suggestions?
var query;
if(whereclause != string.Empty)
{
query = Directory.GetFiles(LRSettings.Default.OperatingDirectory, LRSettings.Default.FileExtension,
SearchOption.AllDirectories).AsQueryable()
.Select(Filename => new { Filename, new FileInfo(Filename)
.LastWriteTime, new FileInfo(Filename).Extension, new FileInfo(Filename).Length })
.Where(whereclause);
}
else
{
query = Directory.GetFiles(LRSettings.Default.OperatingDirectory,
LRSettings.Default.FileExtension,
SearchOption.AllDirectories)
.AsQueryable()
.Select(Filename => new { Filename, new FileInfo(Filename).LastWriteTime, new FileInfo(Filename).Extension, new FileInfo(Filename).Length });
}
Since it seems like the only difference in your query is whether or not to include a WHERE clause, you should just be able to do this:
var query = Directory.GetFiles(LRSettings.Default.OperatingDirectory, LRSettings.Default.FileExtension, SearchOption.AllDirectories)
.AsQueryable()
.Select(Filename => new { Filename, new FileInfo(Filename).LastWriteTime, new FileInfo(Filename).Extension, new FileInfo(Filename).Length });
if(whereclause != string.Empty)
{
query = query.Where(whereclause);
}
Since you're using an IEnumerable, I don't think you have to worry about pulling too much data without the Where() clause, since it doesn't get enumerated until you access query in some fashion (like binding to a form or whatnot).
Two immediate options are to use ?: or to extract the common/starting query.
For the former:
bool expr = SomeTrueOrFalseValue();
var l = new [] { 1,2,3 };
// both "true" and "false" branches unify an IEnumerable<int>
var q = expr
? l
: l.Where(x => x > 1);
// q typed as IEnumerable<int>
For the latter:
var q = l.AsEnumerable();
// q is typed as IEnumerable<int>
if (!expr) {
q = q.Where(x => x > 1);
}
// q is still typed as IEnumerable<int> - can't be changed after var
An assignment must be included in a var declaration so the variable's type can be determined.
But without var:
IEnumerable<int> q; // not practical/possible for complex types
if (expr) {
q = l;
} else {
q = l.Where(x => x > 1);
}
Related
Let's say I have an in-memory list called filterProfile.
var filterProfile = people.Select(x => new {x.Name,x.Age}).ToList();
I need to run a query like this in mongodb:
var resultData = await collection
.Find(x => filterProfile.Any(y => y.Name == x.Name && y.Age == x.Age))
.TolistAsync();
using $in operator you can check existence of a SINGLE field within an array.
How can I check TWO fields at the same time ?
Is it possible ?
The only way that comes to my mind is do sth like this :
var filter = Builders<Person>.Filter.Empty;
foreach (var item in people)
{
var nameFilter = Builders<Person>.Filter.Eq(x => x.Name, item.Name);
var ageFilter = Builders<Person>.Filter.Eq(x => x.Age, item.Age);
var loopFilter = Builders<Person>.Filter.And(nameFilter , ageFilter );
filter = Builders<Person>.Filter.Or(filter, loopFilter );
}
var resultData = await collection.Find(filter).TolistAsync();
it works but looks like not so clean to me.
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.
class MyClass
{
string identifier;
}
I have two lists of identical count
List<MyClass> myClassList;
List<string> identifierList;
I would like to know what is the best way of assigning identifier in the myClassList enumerating over identifierList?
I wrote this, but looks too long(and inefficient?). There must be a better way of doing this?
identifierList.Select((value,index)=>new {value, index}).ToList().ForEach(x=> myClassList[x.index].identifier = x.value);
Yes, you're approach is inefficient(you're creating a throwaway list) and not very readable and also throws an exception if both lists have a different size.
You're looking for the Enumerable.Zip extension method which joins by index:
var zipped = myClassList.Zip(identifierList, (c, s) => new { class = c, string = s});
foreach(var x in zipped)
x.class.identifier = x.string;
You can use Zip:
myClassList.Zip(identifierList, (klass, id) =>
{
klass.identifier = id;
return klass
});
Note that this will give you a mutating Linq expression.
However, I suspect this is probably clearer:
for(int i = 0; i < myClassList.Count; i++)
{
myClassList[i].identifier = identifierList[i];
}
It may not be as "hip" as using Linq, but it's easier to read and understand what's going on!
Well, how about simple query like this.
myClassList.Select((e, i) => { e.identifier = identifierList[i]; return e; }).ToList();
We need this .ToList() to execute the query, otherwise it'll just do nothing.
Example :
List<string> identifierList = new List<string>()
{
"abc", "def", "ghi"
};
List<MyClass> myClassList = new List<MyClass>()
{
new MyClass(), new MyClass(), new MyClass(),
};
myClassList.Select((e, i) => { e.Id = identifierList[i]; return e; }).ToList();
foreach (var item in myClassList)
Console.Write(item.Id + " ");
Output : abc def ghi
In case your collections are of different length you can use :
myClassList.Select((e, i) => { e.Id = i < identifierList.Count? identifierList[i] : e.Id; return e; }).ToList();
I need to configure the condition in config and same thing will be used as condition for lambda expression Where clause.
Below is the code i tried but all the list items are getting set with this value.
Test2 tt = new Test2();
tt.s2 = "TEST1";
tt.s3 = "TEST2";
tt.s4 = "TEST3";
Test2 tt1 = new Test2();
tt1.s2 = "TEST11";
tt1.s3 = "TEST21";
tt1.s4 = "TEST31";
Test2 tt2 = new Test2();
tt2.s2 = "TEST12";
tt2.s3 = "TEST22";
tt2.s4 = "TEST32";
List<Test2> test = new List<Test2>();
test.Add(tt);
test.Add(tt1);
test.Add(tt2);
var cond = "item => item.s2 == TEST1";
var test2List = test.Select(item => cond);
So, can anyone suggest how can I achieve this dynamic concept?
If you're trying to have your conditions expressed as strings then dynamic LINQ can do that for you. If you reference System.Linq.Dynamic then you can write conditions like
var cond = "s2 == \"TEST1\"";
var test2List = test.Where(cond);
test2List.ToList().ForEach(_ => Console.WriteLine($"{_.s2}, {_.s3}. {_.s4}"));
cond = "s2 == \"TEST1\" || s2 == \"TEST12\"";
test2List = test.Where(cond);
test2List.ToList().ForEach(_ => Console.WriteLine($"{_.s2}, {_.s3}. {_.s4}"));
which produces
TEST1, TEST2. TEST3
TEST1, TEST2. TEST3
TEST12, TEST22. TEST32
You can do something like
var filters = new List<Func<f_results, bool>>();
if (s2test) filters.Add(x => x.s2 == myvar);
if (s3test) filters.Add(x => x.s3 == myvar2);
test.Where (item => filters.All(item));
you can add filters as many as you like, in various conditions, it depends on how dynamic but you can do
if (s2test & s2equals) filters.Add(x => x.s2 == myvar) else if (!s2equals) filters.Add(x => x.s2 != myvar)
etc so you can use switches to build your statements up if you needed.
I don't sure I understand what you want but I'll try.
Lets say that your config is like this:
"s2 == TEST1"
in other time its:
"s3 == TEST2"
So you need to create Predict<Test2> and the body will be somthing like this:
var value= item.GetType().GetField("yourConfigField").GetValue(item);
return value == yourConfigValue;
When yourConfigFiled is s2 or s3 and yourConfigValue is TEST1 or TEST2
According to this page, you can create a lambda expression from a string by using an open source library called Dynamic Expresso.
var prices = new [] { 5, 8, 6, 2 };
var whereFunction = new Interpreter()
.ParseAsDelegate<Func<int, bool>>("arg > 5");
var count = prices.Where(whereFunction).Count();
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.