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();
Related
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" });
}
suppose i am showing data in grid and i have many textboxes for filter the data.
textbox for employee id. if employee id textbox is empty then no where clause will be added but if it is not empty then where clause will be added for that. the same way we can filter data if salary textbox has value or employee name textbox has value.
i try to compose a conditional LINQ query but got error. here is mine
var sName="";
var r = from t in TblFamilies
where 1 == 1
if(sName!="")
{
&& t.Name="Keith";
};
select new
{
t.ID,
t.ParentID,
t.Name,
t.CurDate
};
r.Dump();
Try this:-
First select the data:-
var r = from t in TblFamilie
select new
{
t.ID,
t.ParentID,
t.Name,
t.CurDate
};
Then you can filter based on condition:-
if (sName!="")
r = r.Where(x => x.Name == sName);
If you want to mix And operator and Or operator together, check PredicateBuilder out here: http://www.albahari.com/nutshell/predicatebuilder.aspx
You can simply write like:
// begin with true if you start with And operator.
var predicate = PredicateBuilder.True<TblFamilie>();
predicate = predicate.And(t => t.CureDate < DateTime.UtcNow.AddDays(-1));
// you can mix with Or operator too.
predicate = predicate.Or(t => t.Name.Contains("blah"));
var results = context.TblFamilie
.Where(predicate)
.Select(new
{
// your projection here...
});
// begin with false if you start with Or operator.
var predicate2 = PredicateBuilder.False<TblFamilie>();
predicate2 = predicate2.Or(t => t.CureDate < DateTime.UtcNow.AddDays(-1));
// you can mix with And operator too.
predicate2 = predicate2.And(t => t.Name.Contains("blah"));
var results = context.TblFamilie
.Where(predicate)
.Select(new
{
// your projection here...
});
// even nesting is possible
var inner = PredicateBuilder.False<TblFamilie>();
inner = inner.Or (p => p.Name.Contains("foo"));
inner = inner.Or (p => p.Name.Contains("bar"));
var outer = PredicateBuilder.True<TblFamilie>();
outer = outer.And (p => p.CureDate > DateTime.UtcNow.AddDays(-3));
outer = outer.And (p => p.CureDate < DateTime.UtcNow.AddDays(-1));
outer = outer.And (inner);
var results = context.TblFamilie
.Where(outer)
.Select(new
{
// your projection here...
});
Updated
Okay, lets assume you have a Family class, and you get 'Families' from some where. You can use PredicateBuilder like this:
// you have 4 families from DB, API or anywhere.
var failies = new List<Family>
{
new Family { Id = 1, ParentId = 1, Name = "foo", Birthday = new DateTime(1971, 1, 1) },
new Family { Id = 1, ParentId = 1, Name = "bar", Birthday = new DateTime(1982, 1, 1) },
new Family { Id = 1, ParentId = 1, Name = "foobar", Birthday = new DateTime(1993, 1, 1) },
new Family { Id = 1, ParentId = 1, Name = "fake", Birthday = new DateTime(2000, 1, 1) },
};
// make predicate!
// if a family's Birthday is before than 1980 'or' Name contains "ke".
var predicate = PredicateBuilder.True<Family>();
predicate = predicate.And(o => o.Birthday < new DateTime(1980, 1, 1));
predicate = predicate.Or(o => o.Name.Contains("ke"));
// you should make IQueryable in order to use PredicateBuilder.
var result = failies.AsQueryable()
.Where(predicate)
.Select(o => new
{
o.Id, o.Name, o.Birthday // only project what you want.
})
.ToList();
// now, result should contains "foo" and "fake".
foreach (var family in result)
{
Debug.WriteLine("Name: " + family.Name);
}
Updated2
You can copy & paste to LinqPad in order to test how it works. Before you run this in the LinqPad,
Download LinqKit.dll from above link.
Make sure press 'F4' > Add > Browse > select LinqKit.dll > Add LinqKit namespace in the 'Additional Namespace Imports' tab.
In the Query panel, choose Language to 'C# Statement(s)'
paste this and run.
// you have 4 strings from DB, API or anywhere.
var strings = new List<string>
{
"foo",
"bar",
"foobar",
"fake"
};
// make predicate!
// if a string contains "oo" or "ke"
var predicate = PredicateBuilder.True<string>();
predicate = predicate.And(o => o.Contains("oo"));
predicate = predicate.Or(o => o.Contains("ke"));
// you should make IQueryable in order to use PredicateBuilder.
var result = strings.AsQueryable()
.Where(predicate)
.ToList();
// now, result should contains "foo", "foobar" and "fake".
foreach (var stringResult in result)
{
Debug.WriteLine("Name: " + stringResult);
}
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.
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);
}
I would like to implement the following logic against Entity Frameworks.
var items = from item in myContext
select new {
Value1 = TweakValue(item.Value1),
Value2 = TweakValue(item.Value2)
};
protected int TweakValue(int value)
{
// Custom processing here
return value;
}
This won't work because of the call to TweakValue() in the select clause. I understand that the query is converted to SQL, and that the problem is that TweakValue() cannot be converted to SQL. My question is what is the most economical way to implement this. Do I need a second loop to convert the values?
I'm still trying to get comfortable with LINQ expressions.
The simplest way is probably to just "move" the execution to the client to perform the transformation. In this case you'd just use:
var items = myContext.Select(item => new { item.Value1, item.Value2 })
.AsEnumerable()
.Select(item => new {
Value1 = TweakValue(item.Value1),
Value2 = TweakValue(item.Value2)
});
Note that you don't have to reuse the names for Value1 and Value2 - it's just easiest to do so.
If you really want to use query expressions:
var query = from item in myContext
select new { item.Value1, item.Value2 };
var items = from item in query.AsEnumerable()
select new {
Value1 = TweakValue(item.Value1),
Value2 = TweakValue(item.Value2)
};
If you want to perform filtering first, you can get that to occur in the database by putting the filtering, ordering etc before the call to AsEnumerable(). For example:
var query = from item in myContext
where item.Foo == bar
orderby item.Something
select new { item.Value1, item.Value2 };
var items = from item in query.AsEnumerable()
select new {
Value1 = TweakValue(item.Value1),
Value2 = TweakValue(item.Value2)
};
You don't need a loop, just another projection:
var items = myContext.Select(i => new {
Value1 = item.Value1,
Value2 = item.Value2
})
.AsEnumerable()
.Select(i => new {
Value1 = TweakValue(item.Value1),
Value2 = TweakValue(item.Value2)
});
Edit: Depending on what TweakValue actually does, you can push the whole thing to the server. Riffing on your current example:
public Expression<Func<Item, ItemProjection>> TweakValue()
{
return item => new ItemProjection
{
Value1 = item.Value1,
Value2 = item.Value2 + 0 // or something else L2E can understand...
};
}
Now use it like:
var exp = TweakValue();
var items = myContext.Select(exp);
Note I'm storing exp in a variable so that L2E doesn't try to directly invoke TweakValue in the query, which would fail.
Naturally, this only works if TweakValue does stuff that L2E can do.