LINQ - Where Clause using Contains - c#

I have a LINQ statement that I need to do a "contains" with, but also need some sort of loop.
The format of the data is as follows:
x.Product_Name = "product[x], product[y], product[z]"
As user selects multiple items from a list to search on.
I need to find anything within Product_Name that was selected from the user.
var names = JsonConvert.DeserializeObject<IEnumerable<string>>
(criteria.value).ToArray();
This line gets the items a user selected from the list and stores them in an array.
query = query.Where(x => names.contains(x.Product_Name))
Doesn't work because Product_Name is a flattened out version of products, so I can't do this.
What I need is something like the following:
foreach (string s in names)
{
projectsQuery = projectsQuery.Where(x => x.Product_Name.Contains(s));
}
But when the SQL is created for the above, it uses an AND conditional instead of an OR conditional. I need to find any instances where string s is contained within the Product_Name.

You can achieve it by creating Expression tree manually. Although it is kind of hard to manage code.
var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var xParameter = Expression.Parameter(typeof(x), "x");
var searchexpression = new stack<expression>();
foreach (string s in names)
{
var containsmethodexp = expression.call(Expression.Property(xParameter, "Product_Name", containsMethod, expression.constant(s));
if (searchexpression.count == 0)
{
searchexpression.push(containsmethodexp);
}
else
{
searchexpression.push(expression.orelse(containsmethodexp, searchexpression.pop()));
}
}
var finalResult = projectsQuery.Where(Expression.Lambda<Func<x, bool>>(searchexpression.pop(), new ParameterExpression[] { xParameter }));
here x is your Entity Name

Related

DataSet Group By with Expression Tree

I'm trying to convert the following lambda expression into an Expression tree
var datatable = "<DataTable retrieved and filled with SQLCommand>"
var aggregate = datatable.AsEnumerable().GroupBy(x =>
new {
MeasureCode = x.Field<string>("CODMEASURE"),
FieldName = x.Field<string>("FIELDNAME"),
Formula = x.Field<string>("FORMULA"),
TableCode = x.Field<string>("CODTAB")})
.Select(x => new
{
Organization = String.Join(",",x.Select(z=>z.CODDIV)),
x.Key.MeasureCode,
x.Key.FieldName,
x.Key.Formula,
x.Key.TableCode
});
and thanks to various articles I've been able to write the following code that performs the first group by
.GroupBy(x =>
new {
MeasureCode = x.Field<string>("CODMEASURE"),
FieldName = x.Field<string>("FIELDNAME"),
Formula = x.Field<string>("FORMULA"),
TableCode = x.Field<string>("CODTAB")})
Below the code that builds the expression tree:
1. Declare the "x" input parameter
var dataRowParameter = Expression.Parameter(typeof(DataRow), "x");
var dataRowType = typeof(DataRow);
2. Create an anonymous type with one property for each datatable columns using the TypeBuilder class
Dictionary<string, Expression> GroupByFields = new Dictionary<string, Expression>();
var helper = new ReflectionHelper();
var myTypeBuilder = helper.GetTypeBuilder();
foreach (DataColumn column in table.Columns)
{
if (column.ColumnName.Equals("CODDIV")) continue;
// Add the column as dynamic object property
myTypeBuilder.DefineField(column.ColumnName, column.DataType, FieldAttributes.Public);
3. While looping through the columns I also create the Expression for accessing the DataRow's Field method like x.Field("columnName")
// Get the expression for dynamically call the DataRow field method
MethodInfo mi = typeof(DataRowExtensions).GetMethod("Field", new Type[] { typeof(DataRow), typeof(string) });
mi = mi.MakeGenericMethod(column.DataType);
var columnName = Expression.Constant(column.ColumnName, typeof(string));
var valueGetter = Expression.Call(mi, new Expression[] { dataRowParameter, columnName });
//Add method definition to the list of fields
GroupByFields.Add(column.ColumnName, valueGetter);
}
// Create the type and assign it to a "new" expression
var dynamicType = myTypeBuilder.CreateType();
4. When I get all the columns, I create the association between the anonymous type property and the DataRowField
var bindingArray = dynamicType.GetFields()
.Select(p => Expression.Bind(p, GroupByFields[p.Name]))
.OfType<MemberBinding>()
.ToArray();
var GroupedRow = Expression.New(dynamicType.GetConstructor(Type.EmptyTypes));
var memberInit = Expression.Convert(Expression.MemberInit(GroupedRow, bindingArray),typeof(object));
var predicate = Expression.Lambda<Func<DataRow, object>>(memberInit, dataRowParameter);
5. Then to test the result I do the GroupBy using lambda from my Expression tree and one written as normal lambda expression.
var groupByResult = table.AsEnumerable().AsQueryable().GroupBy(predicate);
var aggregate = table.AsEnumerable().AsQueryable().GroupBy(x =>
new
{
MeasureCode = x.Field<string>("CODMEASURE")
});
/*.Select(x => new
{
Organization = String.Join(",",x.Select(z=>z.Field<string>("CODDIV"))),
});*/
6. The correctResult is the count from the groupby using the LINQ Lambda Expression and is giving the right result, the result variable instead is returning the wrong result the is simply all the record without grouping
var correctresult = aggregate.ToArray().Count();
var result = groupByResult.ToArray().Count();
return table;
I've tried to use the debug view to understand the difference between the two lambda but i see only a small difference.
The one returning the correct result is
.Call System.Linq.Queryable.GroupBy(
.Constant<System.Linq.EnumerableQuery`1[System.Data.DataRow]>(System.Data.EnumerableRowCollection`1[System.Data.DataRow]),
'(.Lambda #Lambda1<System.Func`2[System.Data.DataRow,<>f__AnonymousType2`4[System.String]]>))
.Lambda #Lambda1<System.Func`2[System.Data.DataRow,<>f__AnonymousType2`4[System.String,System.String,System.String,System.String]]>(System.Data.DataRow $x)
{
.New <>f__AnonymousType2`4[System.String](
.Call System.Data.DataRowExtensions.Field(
$x,
"CODMEASURE"),
)
}
The one NOT GROUPING is
.Call System.Linq.Queryable.GroupBy(
.Constant<System.Linq.EnumerableQuery`1[System.Data.DataRow]>(System.Data.EnumerableRowCollection`1[System.Data.DataRow]),
'(.Lambda #Lambda1<System.Func`2[System.Data.DataRow,System.Object]>))
.Lambda #Lambda1<System.Func`2[System.Data.DataRow,System.Object]>(System.Data.DataRow $x) {
(System.Object).New 13d459a0-b85d-4c0d-8ab1-8f0e9af36ad7(){
CODMEASURE = .Call System.Data.DataRowExtensions.Field(
$x,
"CODMEASURE")
}
}
So I suppose that the reason for which is not grouping is because the one built with expression tree is using a System.Object instead of an anonymous type with the right properties mapped
But I don't know how to make it using an anomymous type, any help would be appreciated.
Thanks

Add new lambda expressions using Expression Tree

I have been looking at many posts here and on the web but none of them seem to be helping.
I have a table with about 2 million records, it has over 200 columns.
A simple web service allow the user to pull a specific number of columns out of the table, the user has the option to choose which column to pull.
The result needs to be string of comma separated values, so my query needs to pull the requested columns and return a concatenate string.
I have done this using ADO.NET and pure SQL queries it works fine however I was asked to do it in Entity Framework.
Here is what I have and have done already.
I get the requested columns list as an array of strings.
The following is my query, not sure if it's the best solution or idea hence I'm asking for help here.
var valueList2 = ctx.mytable.Where(x => x.pcds == comValue).Select(x => new{temp = x.column1 +", "+ x.column2}).Select(x => x.temp).ToList();
The above gives me string of two columns separated by commas, I just need to somehow push my array of column names into the lambda part of it.
I did the following but then realised that it only works with a specific type of a class not anonymous, also I can't figure out how I can use it for a multiple columns and not make it so complex.
var createdType = typeof(mytable);
var Param = Expression.Parameter(typeof(string), "pr");
var obj = Expression.New(createdType);
var ValueProperty = createdType.GetProperty("long");
var ValueAssignment = Expression.Bind(ValueProperty, Param);
var memberInit = Expression.MemberInit(obj, ValueAssignment);
var lm = Expression.Lambda<Func<string, mytable>>(memberInit, Param);
Thank you
I'm using Dynamic Linq (source code). Sadly there is little documentation about how to use it :-) In a fun boomerang effect, there is an "evolved" version. The boomerang effect is because the code for generating the dynamic class is based on one of my responses :-) The remaining code seems to be very beautiful... And there is a full suit of unit tests with code samples!!! Note that this second library is a superset of the first library, so you can probably apply many examples to the first one! :-)
I'm adding some static methods to translate the result of a a Dynamic Linq query to a IEnumerable<object[]>.... Example code:
using (var ctx = new Model1())
{
var result = ctx.MyTable
.Take(100)
.SimpleSelect(new[] { "ID", "Col1", "Col2" })
.ToObjectArray();
foreach (var row in result)
{
Console.WriteLine(string.Join(", ", row));
}
}
More complex example:
var columnsNames = new[] { "SomeNullableInt32", "SomeNonNullableDateTimeColumn" };
// One for each column!
var formatters = new Func<object, string>[]
{
x => x != null ? x.ToString() : null,
x => ((DateTime)x).ToShortDateString()
};
var result = ctx.MyTable.Take(100).SimpleSelect(columnsNames).ToObjectArray();
foreach (var row in result)
{
var stringRow = new string[row.Length];
for (int i = 0; i < row.Length; i++)
{
stringRow[i] = formatters[i](row[i]);
}
Console.WriteLine(string.Join(", ", stringRow));
}
And the classes... One (SimpleSelect) produces the Dynamic SQL Select, and "anonymizes" the field names. I do this because for each type of return the Dynamic Linq will generate at runtime a class. This class won't be unloaded until the program ends. By anonymizing the columns (I rename them to Item1, Item2, Item3...) I increase the possibility that the same class will be reused. Note that different type of columns will generate different classes! (int Item1, string Item2 will be a different class from int Item1, DateTime Item2), the other (ToObjectArray) returns a IEnumerable<object[]>, something easier to parse.
public static class DynamicLinqTools
{
private static ConcurrentDictionary<Type, Func<object, object[]>> Converters = new ConcurrentDictionary<Type, Func<object, object[]>>();
public static IQueryable SimpleSelect(this IQueryable query, string[] fields)
{
// With a little luck, "anonymizing" the field names we should
// reduce the number of types created!
// new (field1 as Item1, field2 as Item2)
return query.Select(string.Format("new ({0})", string.Join(", ", fields.Select((x, ix) => string.Format("{0} as Item{1}", x, ix + 1)))));
}
public static IEnumerable<object[]> ToObjectArray(this IQueryable query)
{
Func<object, object[]> converter;
Converters.TryGetValue(query.ElementType, out converter);
if (converter == null)
{
var row = Expression.Parameter(typeof(object), "row");
// ElementType row2;
var row2 = Expression.Variable(query.ElementType, "row2");
// (ElementType)row;
var cast = Expression.Convert(row, query.ElementType);
// row2 = (ElementType)row;
var assign = Expression.Assign(row2, cast);
var properties = query.ElementType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.CanRead && x.GetIndexParameters().Length == 0)
.ToArray();
// (object)row2.Item1, (object)row2.Item2, ...
var properties2 = Array.ConvertAll(properties, x => Expression.Convert(Expression.Property(row2, x), typeof(object)));
// new object[] { row2.Item1, row2.Item2 }
var array = Expression.NewArrayInit(typeof(object), properties2);
// row2 = (ElementType)row; (return) new object[] { row2.Item1, row2.Item2 }
var body = Expression.Block(typeof(object[]), new[] { row2 }, assign, array);
var exp = Expression.Lambda<Func<object, object[]>>(body, row);
converter = exp.Compile();
Converters.TryAdd(query.ElementType, converter);
}
foreach (var row in query)
{
yield return converter(row);
}
}
}
This is a short and easy answer for whoever needs a different answer, but per our discussion with #xanatos, it's not the best as it also returns all the columns which need to be cut off before adding to a list of strings.
List<string> valueList = new List<string>();
using (var ctx = new DataEntities1())
{
var query = ctx.myTable.Where(x => x.pcds == scode).SingleOrDefault();
foreach (var item in columnsArray)
{
valueList.Add(typeof(myTable).GetProperty(onsColumns[Convert.ToInt32(item)]).GetValue(query).ToString());
}
}

Retrieve all values that partly contains values from a list

I have a table in my database called Citites. I want to retrieve all cities whose name contain any of the values from the strings list.
List<string> strings = new List<string>(new string[] {"burg", "wood", "town"} );
I tried this but it will only match the exact value from the strings list. I need to find values that contain e.g town, like cape town and townsend
List<City> cities = db.Cities.Where(c => strings.Contains(c.name));
EDIT
I'm using LINQ to SQL and Any() doesn't seem to be supported here:
Local sequence cannot be used in LINQ to SQL implementations of query
operators except the Contains operator.
This will do what you need, assuming your LINQ provider supports it - since you did not mention what are you using, we can't test it.
List<City> cities = db.Cities.Where(c => strings.Any(s => c.name.Contains(s)));
In detail: for a single value (like Capetown) you would write
strings.Any(s => "Capetown".Contains(s))
Then you just apply this expression inside your current Where condition as shown in the initial code example.
Since you mention that your LINQ provider does not support .Any() in this context, here is a much more complicated code that builds the query expression dynamically.
var strings = new [] { "burg", "wood", "town" };
// just some sample data
var cities = new[] { new City("Capetown"), new City("Hamburg"), new City("New York"), new City("Farwood") };
var param = Expression.Parameter(typeof(City));
var cityName = Expression.PropertyOrField(param, "Name"); // change the property name
Expression condition = Expression.Constant(false);
foreach (var s in strings)
{
var expr = Expression.Call(cityName, "Contains", Type.EmptyTypes, Expression.Constant(s));
condition = Expression.OrElse(condition, expr);
}
// you can apply the .Where call to any query. In the debugger view you can see that
// the actual expression applied is just a bunch of OR statements.
var query = cities.AsQueryable().Where(Expression.Lambda<Func<City, bool>>(condition, param));
var results = query.ToList();
// the class used in the test
private class City
{
public City(string name) { this.Name = name; }
public string Name;
}
But note that since you mentioned in other comments that the strings collection is rather large, you should really look into building a stored procedure and pass the values as XML parameter to that procedure (then load the XML as table and join it in the query) because this approach of building the query will probably soon run into some sort of "query has too many operands" exception.
I'm not sure if it is supported by your LINQ-provider, but at least in LINQ-To-Objects this works:
List<City> cities = db.Cities.Where(c => strings.Any(s=> c.Name.Contains(s)));
You need to check if the City name contains any of the string in the list, not the other way around:
protected bool ContainsSubstring(string cityName, List<string> strings)
{
foreach(string subString in strings)
{
if (cityName.Contains(subString)) return true;
}
return false;
}
...
List<City> cities = db.Cities.Where(c => this.ContainsSubstring(c.name, strings));
If you find it with a lot of loops, try using FUNC<> which will be better (in performance). I have a sample for that :
List<string> _lookup = new List<string>() { "dE", "SE","yu" };
IEnumerable<string> _src = new List<string> { "DER","SER","YUR" };
Func<string, List<string>, bool> test = (i,lookup) =>
{
bool ispassed = false;
foreach (string lkstring in lookup)
{
ispassed = i.Contains(lkstring, StringComparison.OrdinalIgnoreCase);
if (ispassed) break;
}
return ispassed;
};
var passedCities = _src.Where(i => test(i, _lookup));
var cities = from c in db.Cities.AsEnumerable()
from s in strings
where c.name.ToLower().Contains(s.ToLower())
select c.name;
Do you have the ability to call a stored proc or sql? - you could use SQL fulltextsearch, especially if you're searching multiple terms. It'd probably be a lot quicker than doing string comparisons in SQL.
http://technet.microsoft.com/en-us/library/ms142583.aspx
You could create your search terms by doing string.Join(" ", strings)

Conditional where with or criteria linqtosql

I've figured out how to do conditional queries with linq to sql and I've also figured out how to OR where clauses. Unfortunately I can't figure out how to do both at once. I can do a conditional where clause something like:
var ResultsFromProfiles = from AllPeeps in SearchDC.aspnet_Users
select AllPeeps;
if (SearchFirstNameBox.Checked)
{
ResultsFromProfiles = ResultsFromProfiles.Where(p => p.tblUserProfile.FirstName.Contains(SearchTerm));
}
if (SearchLastNameBox.Checked)
{
ResultsFromProfiles = ResultsFromProfiles.Where(p => p.tblUserProfile.LastName.Contains(SearchTerm));
}
This will get me any profiles where the first name AND the last name contain the search term.
Or I could do:
var ResultsFromProfiles = from p in SearchDC.aspnet_Users
where p.tblUserProfile.LastName.Contains(SearchTerm) ||
p.tblUserProfile.FirstName.Contains(SearchTerm)
select p;
This would get me any profiles where the first name OR the last name contains the search term.
I have a bunch of checkboxes where the user can specify which fields they want to search for teh search term, so I want to be able to build a query that will conditionally add them as in the first code snippet above, but add them as an OR so they work like the second snippet. That way it will search for any matches anywhere in the specified fields.
Any tips?
Yeah, use PredicateBuilder. It lets you build up dynamic queries with And or Or semantics. It's free and stable- I use it all over the place.
One way to do this is to manipulate the LINQ expression tree for your query. In your case, you'd need to build a lambda expression and substitute it in for your call to Where. Here is a working example based on a list, but the code to manipulate the expression tree is the same regardless of the query provider.
List<User> Users = new List<User>();
Users.Add(new User() { FirstName = "John", LastName = "Smith" });
Users.Add(new User() { FirstName = "Jane", LastName = "Smith" });
string Query = "John";
var Queryable = Users.AsQueryable();
var Results = (from u in Queryable
select u);
//initial method call... the lambda u => false is a place-holder that is about to be replaced
MethodCallExpression WhereExpression = (MethodCallExpression)Results.Where(u => false).Expression;
//define search options
Expression<Func<User, string, bool>> FilterLastName = (u, query) => u.LastName.Contains(query);
Expression<Func<User, string, bool>> FilterFirstName = (u, query) => u.FirstName.Contains(query);
//build a lambda based on selected search options... tie the u parameter to UserParameter and the query parameter to our Query constant
ParameterExpression UserParameter = Expression.Parameter(typeof(User), "u");
Expression Predicate = Expression.Constant(false); //for simplicity, since we're or-ing, we'll start off with false || ...
//if (condition for filtering by last name)
{
Predicate = Expression.Or(Predicate, Expression.Invoke(FilterLastName, UserParameter, Expression.Constant(Query)));
}
//if (condition for filtering by first name)
{
Predicate = Expression.Or(Predicate, Expression.Invoke(FilterFirstName, UserParameter, Expression.Constant(Query)));
}
//final method call... lambda u => false is the second parameter, and is replaced with a new lambda based on the predicate we just constructed
WhereExpression = Expression.Call(WhereExpression.Object, WhereExpression.Method, WhereExpression.Arguments[0], Expression.Lambda(Predicate, UserParameter));
//get a new IQueryable for our new expression
Results = Results.Provider.CreateQuery<User>(WhereExpression);
//enumerate results as usual
foreach (User u in Results)
{
Console.WriteLine("{0} {1}", u.FirstName, u.LastName);
}
Working with expression trees can typically be simplified by using the visitor pattern, but I've omitted that so you could more clearly see the work that has to be done.
Here's a suggestion.
I haven't tried compiling and running it, and LinqToSQL is full of surprises, so no guarantees :-)
var ResultsFromProfiles = from AllPeeps in SearchDC.aspnet_Users select AllPeeps;
IEnumerable<AspNet_User> total = new AspNew_User[0];
if (SearchFirstNameBox.Checked)
{
total = total.Concat(ResultsFromProfiles.Where(p => p.tblUserProfile.FirstName.Contains(SearchTerm));}
}
if (SearchLastNameBox.Checked)
{
total = total.Concat(ResultsFromProfiles.Where(p => p.tblUserProfile.LastName.Contains(SearchTerm));
}
total = total.Distinct();

Dynamic query in LINQ

How do I write a dynamic query for Linq, if I have say Customer class which holds the fields:
string name
string address
int phoneno
I have to query based on information given similar to
query = string.Empty;
if(!string.IsNullorEmpty(name))
{
query += "#name = name";
}
if(!string.IsNullorEmpty(address))
{
query += "#address = address";
}
if(!string.IsNullorEmpty(phoneno))
{
query += "#phoneno = phoneno";
}
var result = from condition in customer
where(query)
select condition;
Edit #1:
the items are changeable at run time like
private Customer[] GetCustomers(Dictionary<string,string> attributes)
{
here the attribute may be, name alone, or name and address, or name address and phoneno
foreach(string field in attributes.key)
{
query += field == attributes[key];
}
Customers[] =ExecuteQuery(query);
}
Is this kind of query supported by LINQ?
Edit #2:
Hi Mouk,
As I am new to C#, I am still struggling, this is not working for me.
var query = _ConfigFile.ConnectionMasterSection;
for(int i = 0; i < filter.count; i++)
{
query = result.Where(p => typeof(ConnectionMaster).GetProperty(filter[i].Attribute).Name == filter[i].Value);
}
This yeilds Empty, where as i used this
var query = _ConfigFile.ConnectionMasterSection;
//Hard coded
res.Where(q => q.category == filter[0].Value);
And it worked as I expected.
Hi Bryan Watts,
I tried your code also and I getting this error: "Lambda Parameter not in scope".
for(int i = 0; i < filter.count; i++)
{
Field item = filter[i];
MemberExpression param = Expression.MakeMemberAccess(Expression.Parameter(typeof(Connection), "p"), typeof(Connection).GetProperty(item.Attribute));
MemberExpression constant = Expression.MakeMemberAccess(Expression.Constant(item), typeof(Field).GetProperty("Value"));
}
try
{
var myquery = Queryable.Where(coll, Expression.Lambda<Func<Connection, bool>>(
Expression.Equal(param, constant), Expression.Parameter(typeof(Connection),"p")));
}
What is the mistake here?
Check out this http://www.albahari.com/nutshell/predicatebuilder.aspx, it allows for strongly typed predicate building, it can be really nice. If you want actually dynamic string built predicates than you can use the LINQ Dynamic Query Library provided by ScottGu.
Both will accomplish what you want although I would recommend the first option before the second.
Allowing you to do:
var predicate = PredicateBuilder.True<MyLinqType>();
if(!string.IsNullOrEmpty(name))
predicate = predicate.And(p => p.name == name);
...
var myResults = Context.MyLinTypeQueryTable.Where(predicate);
And more.
Here you go:
var result = from customer in Customers
where string.IsNullOrEmpty(phoneNo) || customer.PhoneNo == phoneNo
where string.IsNullOrEmpty(address) || customer.Address == address
select customer;
If you're concerned that this generate the optimal SQL query underneath, as always you should attach a SQL Query Analyzer and check. But I believe the expression parser in Linq To Sql will collapse down the where clauses as appropriate based on the value of the arguments.
You can use the fluent interface and add a new Where clause fpr each condition. Something like:
var result = from cus in customers select cus;
if(!string.IsNullOrEmpty(name))
result= result.Where(p => p.Name == name);
EDIT upon hte comment:
if you are querying over a collection in memory, you could retrieve the properties using reflection.
private Customer[] GetCustomers(Dictionary<string,string> attributes)
{
var result = from cus in customers select cus;
foreach(string key in attributes.Keys)
result= result.Where(p => GetProperty(p, key )== attributes[key]);
return result.ToList();
}
Supposing GetProperty retrieve the property by reflection.
Using Linq2Sql this method will result in retrieving all record an then iterating over them using reflection.
I've had good experience with Dynamic LINQ.
I used it for a rich HTML table that could be filtered and sorted server side. The server receives a request containing a request parameter where the key is the name of the property (for example 'Lastname') and the value is the value that the property needs to be sorted on (for example 'Smith'). Using that information I built a query string that I passed to the Dynamic LINQ's Where method.
Roughly, you could think of something like the following:
public static IQueryable<T> Filter<T>(this IQueryable<T> query, Dictionary<string, string> dictionary)
{
Type t = typeof(T);
StringBuilder sb = new StringBuilder();
PropertyInfo[] properties = t.GetProperties();
foreach(string key in dictionary.Keys)
{
PropertyInfo property = properties.Where(p => p.Name == key).SingleOrDefault();
if(property != null)
{
if (sb.Length > 0) sb.Append(" && ");
string value = dictionary[key];
sb.Append(string.Format(#"{0}.ToString().Contains(""{1}"")", key, value));
}
}
if (sb.Length > 0)
return query.Where(sb.ToString());
else
return query;
}
The code is out of the top of my head and thus untested.
Of course, this is the most basic version: it does a simple string comparison. If you want to have numerical comparison (meaning you want for example the User where UserID is exactly 100, not where the UserID.ToString().Contains("100")), or query nested Properties (Customer.Company.CompanyAddress for example), or query Collections this gets more complicated. You should also think about security: while Dynamic LINQ is not vulnerable to SQL injection, you shouldn't let it blindly parse all user input.
It sounds like you need to dynamically compose queries.
See my answer to this question.
It explains how queries against an IQueryable<T> are composed by the compiler, and what you can do to add dynamic elements.
Edit
Here is an example of how you would dynamically build a set of Where conditions on top of an IQueryable<Customer>:
// This method ANDs equality expressions for each property, like so:
//
// customers.Where(c => c.Property1 == value1 && c.Property2 == value2 && ...);
private IQueryable<Customer> FilterQuery(IQueryable<Customer> customers, IDictionary<string, string> filter)
{
var parameter = Expression.Parameter(typeof(Customer), "c");
Expression filterExpression = null;
foreach(var filterItem in filter)
{
var property = typeof(Customer).GetProperty(filterItem.Key);
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var equality = Expression.Equal(propertyAccess, Expression.Constant(filterItem.Value));
if(filterExpression == null)
{
filterExpression = equality;
}
else
{
filterExpression = Expression.And(filterExpression, equality);
}
}
if(filterExpression != null)
{
var whereBody = Expression.Lambda<Func<Customer, bool>>(filterExpression, parameter);
customers = customers.Where(whereBody);
}
return customers;
}

Categories

Resources