Look the code below. I'd like to replace USERNAME by the field name received in the parameter field. This method must be able to make some search on several fields.
Thank,
public void Searching(string field, string stringToSearch)
{
var res =
from user in _dataContext.USERs where
user.USERNAME.Contains(stringToSearch)
select new
{
Id = user.ID,
Username = user.USERNAME
};
}
You need to forget about the anonymous type, maybe use Tuple<int,string> instead; but: how about:
IQueryable<Foo> source = // YOUR SOURCE HERE
// in-memory dummy example:
// source = new[] {
// new Foo {Id = 1, Bar = "abc"},
// new Foo {Id = 2, Bar = "def"}
// }.AsQueryable();
string field = "Bar";
string stringToSearch = "d";
var param = Expression.Parameter(typeof (Foo), "x");
var predicate = Expression.Lambda<Func<Foo, bool>>(
Expression.Call(
Expression.PropertyOrField(param, field),
"Contains", null, Expression.Constant(stringToSearch)
), param);
var projection = Expression.Lambda<Func<Foo, Tuple<int, string>>>(
Expression.Call(typeof(Tuple), "Create", new[] {typeof(int), typeof(string)},
Expression.PropertyOrField(param, "Id"),
Expression.PropertyOrField(param, field)), param);
Tuple<int,string>[] data = source.Where(predicate).Select(projection).ToArray();
As a matter of fact it is possible using the Expression API:
public void Searching(Expression<Func<User,string>> field, string stringToSearch)
{
var call = Expression.Call(field.Body, typeof (string).GetMethod("Contains"), new[] {Expression.Constant(value)});
Expression<Func<User, bool>> exp = Expression.Lambda<Func<User, bool>>(Expression.Equal(call, Expression.Constant(true)), field.Parameters);
var res = _dataContext.USERs.Where(exp).Select(u=>new { id= u.ID, Username = u.USERNAME});
}
What you are trying is not possible. You can however use the dynamic linq library to achieve what you want
Related
I'm trying to figure out for a IQueryable how I can build a csv file by dynamically selecting objects as strings.
for example:
I read this about dynamically selecting properties of a T ...
LINQ : Dynamic select
That would allow me to do something like this ...
var data = new List<T> { items };
var fields = new string[] { "Field1", "Field2", "Field3" };
// build row strings
var rows = set.Select(BuildRowObjectExpression<T, ProjectionOfT>(fields))
.Select(i => Serialise<ProjectionOfT>(i));
string Serialise<T>(T i, string separator)
{
var properties = typeof(T).GetProperties();
var values = new List<string>();
foreach (var p in properties)
values.Add(p.GetValue(i).ToString());
return string.Join(separator, values);
}
Func<T, Tout> BuildRowObjectExpression<T, Tout>(string[] fields)
{
// input parameter "o"
var xParameter = Expression.Parameter(typeof(T), "o");
// new statement "new Data()"
var xNew = Expression.New(typeof(T));
// create initializers
var bindings = fields.Select(o => {
// property "Field1"
var mi = typeof(T).GetProperty(o);
// original value "o.Field1"
var xOriginal = Expression.Property(xParameter, mi);
// set value "Field1 = o.Field1"
return Expression.Bind(mi, xOriginal);
}
);
// initialization "new T { Field1 = o.Field1, Field2 = o.Field2 }"
var xInit = Expression.MemberInit(xNew, bindings);
// expression "o => new T { Field1 = o.Field1, Field2 = o.Field2 }"
var lambda = Expression.Lambda<Func<T, string>>(xInit, xParameter);
// compile to Func<T, string>
return lambda.Compile();
}
What I was wondering however is:
How do I build this as an expression / func that I can use with an IQueryable to do something like this
// this would build me a string array from the specified properties
// in a collection of T joining the values using the given separator
var results = data.Select(i => BuildString(fields, "|")).ToArray();
I would ideally like to use this with an entity set.
String conversion/concatenation is not a database job. You'd better keep the two parts separate - data retrieval in database query and data transformation in memory query.
For instance, you can use the following custom extensions methods:
public static class Extensions
{
public static IQueryable<T> Select<T>(this IQueryable source, string[] fields)
{
var parameter = Expression.Parameter(source.ElementType, "o");
var body = Expression.MemberInit(
Expression.New(typeof(T)),
fields.Select(field => Expression.Bind(
typeof(T).GetProperty(field),
Expression.PropertyOrField(parameter, field))
)
);
var selector = Expression.Lambda(body, parameter);
var expression = Expression.Call(
typeof(Queryable), "Select", new[] { parameter.Type, body.Type },
source.Expression, Expression.Quote(selector)
);
return source.Provider.CreateQuery<T>(expression);
}
public static IEnumerable<string> Serialize<T>(this IEnumerable<T> source, string separator)
{
var properties = typeof(T).GetProperties();
return source.Select(item => string.Join(separator, properties.Select(property => property.GetValue(item))));
}
}
like this
var results = db.Data.Select<ProjectionOfT>(fields).Serialize("|");
If you want to avoid the ProjectionOfT class, there is no easy way to do that since it requires dynamic runtime class generation, so you'd better resort to System.Linq.Dynamic package.
I am trying search each property value of an IQueryable collection of T against the value of a search query. I have the following function and would like to know how do I ALSO test for NOT NULL and CONTAINS together?
private Expression<Func<T, bool>> PropertySearch
{
get
{
// Object that is passed to the lambda expression
ParameterExpression instance = Expression.Parameter(typeof(T), "val");
Expression whereExpr = Expression.Constant(true); // default is val => True
var _properties = typeof(T).GetProperties();
foreach (var prop in _properties)
{
var query = _httpRequest["query"].ToLower();
var property = Expression.Property(instance, prop);
var toStringCall = Expression.Call(Expression.Call(
property,
"ToString",
new Type[0]),
typeof(string).GetMethod("ToLower", new Type[0]));
whereExpr = Expression.And(whereExpr,
Expression.Call(toStringCall, typeof(string).GetMethod("Contains"),
Expression.Constant(query)));
}
return Expression.Lambda<Func<T, bool>>(whereExpr, instance);
}}
I have created a search extensions nuget package that performs this type of check. For your example I would do something like the following.
Note, this is without an IDE so may have some errors
/* *** Start: These can be made private reaonly fields ***/
var comparisonExpr = Expression.Constant(StringComparison.OrdinalIgnoreCase);
var zeroExpression = Expression.Constant(0)
var nullExpression = Expression.Constant(null)
MethodInfo IndexOfMethod = typeof(string).GetMethod("IndexOf", new[] { typeof(string), typeof(StringComparison) });
/* *** End ***/
Expression finalExpression = null
ParameterExpression instance = Expression.Parameter(typeof(T), "val");
var _properties = typeof(T).GetProperties();
var query = _httpRequest["query"].ToLower();
var queryExpr = Expression.Constant(query);
foreach (var prop in _properties)
{
//Get property
var propertyExpr = Expression.Property(instance, prop);
//Get property as string
var propStringExpr = Expression.Call(property, "ToString", new Type[0]);
//Perform IndexOf call
var indexOfExpr = Expression.Call(propStringExpr,
IndexOfMethod,
queryExpr,
comparisonExpr);
// Check index of is greater than or equal to zero
var containsExpr = Expression.GreaterThanOrEqual(containsExpr, zeroExpression);
if(finalExpression == null)
{
finalExpression = containsExp;
}
else
{
finalExpression = Expression.AndAlso(containsExpr);
}
}
return Expression.Lambda<Func<T, bool>>(finalExpression, instance);
I've removed the need for ToLower() and instead used IndexOf with a string comparison type
If you want to see how I have achieved similar functionality, take a look at NinjaNye.SearchExtensions on Github
https://github.com/ninjanye/SearchExtensions
If you wanted to search a collection of IQueryable you could use NinjaNye.SearchExtensions as follows
string query = _httpRequest["query"];
var result = data.SearchAll().Containing(query);
This will search all string properties (not all properties as you have above) and return just those where any property mathes the search term.
Hope this helps
You could probably use PredicateBuilder so you don't have to mess with expression trees yourself.
I have code which builds list only with one property "Name".
How to modify the code so it can build list with two properties "Name" and "Test_Result"
I know that anonymous type can be used to perform this, but how to put them to dynamic expression?
here is my code:
string item = "Name";
string item2 = "Test_Result";
Type studentType = typeof(Student);
ParameterExpression itemParam = Expression.Parameter(studentType, item);
MemberInfo itemProperty = studentType.GetProperty(item);
MemberExpression valueInItemField =
Expression.MakeMemberAccess(itemParam, itemProperty);
Expression<Func<Student, string>> selectExpression =
Expression<Func<Student, string>>
.Lambda<Func<Student, string>>(valueInItemField, itemParam);
IEnumerable<string> currentItemFields =
DeserializedStudents.Select(selectExpression.Compile());
I'm assuming that the "Name" and "Test_Result" here are flexible and cannot be hard-coded.
Anonymous types are fully defined regular classes; the only interesting thing about them is that the compiler provides the details instead of you.
I would suggest that the way to handle this scenario would be to use Tuple.Create to create an IEnumerable<Tuple<string,string>> and refer to them as Item1, Item2 (the names from Tuple<,>. The other option would be to use something like ExpandoObject, and then use either the IDictionary<string,object> API, or the dynamic API, to get the values back out.
For example:
string item1 = "Name";
string item2 = "Test_Result";
Type studentType = typeof(Student);
var itemParam = Expression.Parameter(studentType, "x");
var member1 = Expression.PropertyOrField(itemParam, item1);
var member2 = Expression.PropertyOrField(itemParam, item2);
var selector = Expression.Call(typeof(Tuple), "Create",
new[] { member1.Type, member2.Type }, member1, member2);
var lambda = Expression.Lambda<Func<Student, Tuple<string,string>>>(
selector, itemParam);
var currentItemFields = students.Select(lambda.Compile());
Here's the same projecting into a custom type with members name and result:
class ProjectedData
{
public string name { get; set; }
public string result { get; set; }
}
...
string item1 = "Name";
string item2 = "Test_Result";
Type studentType = typeof(Student);
var itemParam = Expression.Parameter(studentType, "x");
var member1 = Expression.PropertyOrField(itemParam, item1);
var member2 = Expression.PropertyOrField(itemParam, item2);
var selector = Expression.MemberInit(Expression.New(typeof(ProjectedData)),
Expression.Bind(typeof(ProjectedData).GetMember("name").Single(), member1),
Expression.Bind(typeof(ProjectedData).GetMember("result").Single(), member2)
);
var lambda = Expression.Lambda<Func<Student, ProjectedData>>(
selector, itemParam);
var currentItemFields = students.Select(lambda.Compile());
Or for the approach using a dictionary:
string[] fields = {"Name", "Test_Result"};
Type studentType = typeof(Student);
var itemParam = Expression.Parameter(studentType, "x");
var addMethod = typeof(Dictionary<string, object>).GetMethod(
"Add", new[] { typeof(string), typeof(object) });
var selector = Expression.ListInit(
Expression.New(typeof(Dictionary<string,object>)),
fields.Select(field => Expression.ElementInit(addMethod,
Expression.Constant(field),
Expression.Convert(
Expression.PropertyOrField(itemParam, field),
typeof(object)
)
)));
var lambda = Expression.Lambda<Func<Student, Dictionary<string,object>>>(
selector, itemParam);
var currentItemFields = students.Select(lambda.Compile());
I need to sort in ajax response grid by column name. Column value is number stored as a string.
Let's say some trivial class (in real-life situation there is no possibility to modify this class):
class TestObject
{
public TestObject(string v)
{
this.Value = v;
}
public string Value { get; set; }
}
then simple test:
[Test]
public void LambdaConstructionTest()
{
var queryable = new List<TestObject>
{
new TestObject("5"),
new TestObject("55"),
new TestObject("90"),
new TestObject("9"),
new TestObject("09"),
new TestObject("900"),
}.AsQueryable();
var sortingColumn = "Value";
ParameterExpression parameter = Expression.Parameter(queryable.ElementType);
MemberExpression property = Expression.Property(parameter, sortingColumn);
//// tried this one: var c = Expression.Convert(property, typeof(double));
LambdaExpression lambda = Expression.Lambda(property, parameter); //// constructs: o=>o.Value
var callExpression = Expression.Call(typeof (Double), "Parse", null, property);
var methodCallExpression = Expression.Call(
typeof(Queryable),
"OrderBy",
new[] { queryable.ElementType, property.Type },
queryable.Expression,
Expression.Quote(lambda)); // works, but sorts by string values.
//Expression.Quote(callExpression)); // getting: System.ArgumentException {"Quoted expression must be a lambda"}
var querable = queryable.Provider.CreateQuery<TestObject>(methodCallExpression);
// return querable; // <- this is the return of what I need.
}
Sorry for not being clear in my first post as #SLaks answer was correct but I do not know how to construct correct lambda expression in this case.
Finally found solution that is good for anyone who has strings in column and needs to sort by converted double value: (Special thanks for #SLaks his post was an eye opener):
[Test]
public void LambdaConstructionTest2()
{
// GIVEN
var queryable = new List<TestObject>
{
new TestObject("5"),
new TestObject("55"),
new TestObject("90"),
new TestObject("9"),
new TestObject("09"),
new TestObject("900"),
}.AsQueryable();
var sortingColumn = "Value";
// WHEN
ParameterExpression parameter = Expression.Parameter(queryable.ElementType);
MemberExpression property = Expression.Property(parameter, sortingColumn);
MethodCallExpression callExpression = Expression.Call(typeof (Double), "Parse", null, property);
LambdaExpression lambda = Expression.Lambda(callExpression, parameter); // = {Param_0 => Parse(Param_0.Value)}
UnaryExpression unaryExpression = Expression.Quote(lambda); // Expression<Func<TestObject,double>> = {Param_0 => Parse(Param_0.Value)}
var methodCallExpression = Expression.Call(
typeof (Queryable),
"OrderByDescending",
new[] { queryable.ElementType, lambda.ReturnType },
queryable.Expression,
unaryExpression);
var querable = queryable.Provider.CreateQuery<TestObject>(methodCallExpression);
// THEN
var expectedMaxValue = queryable.Max(x => Convert.ToDouble(x.Value));
var expectedMinValue = queryable.Min(x => Convert.ToDouble(x.Value));
var list = querable.ToList();
var actualMaxValue = Convert.ToDouble(list.First().Value);
var actualMinValue = Convert.ToDouble(list.Last().Value);
Assert.AreEqual(expectedMaxValue, actualMaxValue);
Assert.AreEqual(expectedMinValue, actualMinValue);
}
You can call Expression.Call() to create an expression node that calls a method.
Expression.Call(typeof(Double), "Parse", null, property)
i want to build a generic search window using linq to sql.
This is what i was trying to do:
class SearchWindow<T> : Form : Where T: class
{
public SearchWindow(Func<T, string> codeSelector,
Func<T, string> nameSelector)
{
var db = new DataContext();
var table = db.GetTable<T>();
var query = from item in table where
codeSelector(item).Contains(someText) &&
nameSelector(item).Contains(someOtherText)
select item;
}
}
And i was trying to use it like:
var searchWindow = new SearchWindow<SomeTable>(x => x.CodeColumn,
y => y.NameColumn).Show();
Bud saddly that doesn't work, i read about expression trees so i tried to do that with them, and i got:
public SearchWindow(codeColumn, nameColumn)
{
Table<T> table = db.GetTable<T>();
var instanceParameter = Expression.Parameter(typeof(T), "instance");
var methodInfo = typeof(string).GetMethod("Contains",
new Type[] { typeof(string) });
var codigoExpression = Expression.Call(Expression.Property(instanceParameter,
codeColumn),
methodInfo,
Expression.Constant("someText",
typeof(string)));
var nombreExpression = Expression.Call(Expression.Property(instanceParameter,
nameColumn),
methodInfo,
Expression.Constant("someOtherText",
typeof(string)));
var predicate = Expression.Lambda<Func<T, bool>>(
Expression.And(codigoExpression, nombreExpression), instanceParameter);
var query = table.Where(predicate);
}
And to use it i need to do:
new SearchWindow<SomeTable>("codeColumn", "nameColumn");
But i don't like the approach to need to enter the column names as a string, is there any way to do it in a fashion similar to my first approach (in order to have intellisense and strong typing)?
Thank you for your help.
Untested, but something like:
static IQueryable<T> Search<T>(
IQueryable<T> source,
Expression<Func<T, string>> codeSelector,
Expression<Func<T, string>> nameSelector,
string code, string name)
{
var row = Expression.Parameter(typeof(T), "row");
var body = Expression.AndAlso(
Expression.Call(
Expression.Invoke(codeSelector, row),
"Contains", null,
Expression.Constant(code, typeof(string))),
Expression.Call(
Expression.Invoke(nameSelector, row),
"Contains", null,
Expression.Constant(name, typeof(string))));
var lambda = Expression.Lambda<Func<T, bool>>(body, row);
return source.Where(lambda);
}
You pass in your table (GetTable<T>) as the source, and lambdas to indicate the columns (x => x.CodeColumn / y => y.NameColumn etc).
Update; tested on LINQ-to-Objects, I'm hopeful it'll work on LINQ-to-SQL as well:
var data = new[] {
new { Code = "abc", Name = "def"},
new { Code = "bcd", Name = "efg"},
new { Code = "ghi", Name = "jkl"}
}.AsQueryable();
var filtered = Search(data, x => x.Code, x => x.Name, "b", "f");
var arr = filtered.ToArray();
Use PredicateBuilder- it'll do the heavy lifting for you.