add where clauses to linq query with generic column name - c#

I want to add where clauses to a linq query only if there is the column on the generic type table.
code example :
this function is generic for all tables in model. I want to add where condition for all the tabels that have "AccountId" column.
public IQueryable RetrieveAll(params Expression>[] eagerProperties) {
var entitySet = ResolveEntitySet(typeof(T));
var query = context.CreateQuery<T>(entitySet);
foreach (var e in eagerProperties)
{
query = query.Expand(e);
}
var type = typeof(T);
var account = type.GetProperty("AccountId");
if(account!=null)
{
query = query.where(x=>x...)
}
return query
I need something like
Guid g = new Guid("3252353h....")
query.where(x=>x.AccountId == g)
Thanks

You must create the Where predicate dynamically using Expression Tree, look code below:
public static IQueryable<T> RetrieveAll<T>(params Expression[] eagerProperties)
{
var type = typeof(T);
var entitySet = ResolveEntitySet(type);
var query = context.CreateQuery<T>(entitySet);
foreach (var e in eagerProperties)
{
query = query.Expand(e);
}
var account = type.GetProperty("AccountId");
if (account != null)
{
Guid g = new Guid("3252353h....");
var parameter = Expression.Parameter(type);
var property = Expression.Property(parameter, account);
var guidValue = Expression.Constant(g);
var lambdaPredicate = Expression.Lambda<Func<T, bool>>(Expression.Equal(property, guidValue), parameter);
return query.Where(lambdaPredicate);
}
return query;
}

Related

Generic Method of Searching all Fields within an IEnumerable for any match in c#

I code below that works well with an API for the DataTables plugin; for each column that the DataTables searches in, regardless of type, the filter works as expected based on what is supplied.
DataTables also has a "Global" search feature where you can search in one field and if there is a match in ANY of the rows for said data then a match is returned.
What I am Hoping For:
A way to perform a search on an IEnumerable such that if any of the fields match the search the result is returned.
NameValueCollection nvc = HttpUtility.ParseQueryString(Request.RequestUri.Query);
var generalSearch = nvc["sSearch"];
if (!string.IsNullOrWhiteSpace(generalSearch))
{
var generalSearchProperties = typeof(T).GetProperties();
foreach (var currentProperty in generalSearchProperties)
{
Type propType = currentProperty.PropertyType;
set = set.Where(StaticUtility.PropertyEquals<T>(currentProperty, generalSearch, propType));
/* ^^^^^ */
/*
Instead of the "Where" here I am looking for something like "where or" which can be added to the IEnumerable.
*/
}
}
Original Code:
protected virtual IQueryable<T> FilterEntitiesBySearchParameters(IQueryable<T> set)
{
try
{
NameValueCollection nvc = HttpUtility.ParseQueryString(Request.RequestUri.Query);
var props = typeof(T).GetProperties();
foreach (var prop in props)
{
var name = prop.Name;
var val = nvc[name];
Type propType = prop.PropertyType;
if (val != null)
{
set = set.Where(StaticUtility.PropertyEquals<T>(prop, val, propType));
}
if (nvc.GetPairs().Where(p => p.Value == name).Where(p => p.Key.StartsWith("mDataProp")).Any())
{
var key = nvc.GetPairs().Where(p => p.Value == name).Where(p => p.Key.StartsWith("mDataProp")).FirstOrDefault().Key;
key = key.Replace("mDataProp", "sSearch");
val = nvc[key];
if (!String.IsNullOrEmpty(val))
set = set.Where(StaticUtility.PropertyEquals<T>(prop, val, propType));
}
}
return set;
} catch (Exception exc)
{
return set;
}
}
If I understand your request correctly, you basically want to search through your data and match any field for equality? If that's true, then simply add your matching data to a new set, and filter it by .Distinct() after the fact, to make sure you get one record of each. Something like so...
NameValueCollection nvc = HttpUtility.ParseQueryString(Request.RequestUri.Query);
var results = new IEnumerable<T>();
var generalSearch = nvc["sSearch"];
if (!string.IsNullOrWhiteSpace(generalSearch))
{
var generalSearchProperties = typeof(T).GetProperties();
foreach (var currentProperty in generalSearchProperties)
{
Type propType = currentProperty.PropertyType;
results.AddRange(set.Where(StaticUtility.PropertyEquals<T>(currentProperty, generalSearch, propType)));
}
}
return results.Distinct();
You could try to first create a BinaryExpression consisting of all the different options, then pass that expression to the Where() method of the query.
Assuming your StaticUtility class is used to create expressions, you might try something like the following:
NameValueCollection nvc = HttpUtility.ParseQueryString(Request.RequestUri.Query);
// Container for filter expression
BinaryExpression filter = null;
var generalSearch = nvc["sSearch"];
if (!string.IsNullOrWhiteSpace(generalSearch)) {
var generalSearchProperties = typeof(T).GetProperties();
foreach (var currentProperty in generalSearchProperties) {
Type propType = currentProperty.PropertyType;
if (filter == null) {
// Start with first filter expression
filter = StaticUtility.PropertyEquals<T>(currentProperty, generalSearch, propType);
} else {
// Add another filter using OR
BinaryExpression other = StaticUtility.PropertyEquals<T>(currentProperty, generalSearch, propType);
filter = BinaryExpression.OrElse(filter, other);
}
}
}
// Add actual filter to query
set = set.Where(filter);

LINQ Delegate Lambda Select Statement Bind Child Property

I am creating a delegate for a Select statement in LINQ. Some of the property bindings are to child properties on the object I'm selecting from.
This is the LINQ statement I want to put in my delegate:
var list = dataSet.Select(x => new ViewModel()
{
Name = x.Name,
ClassType = x.ClassType.Description
};
I can get the Name no worries with my code, but I do not know how to get the ClassType.Description.
Here is my current code:
protected Func<Student, ManagerStudentListViewModel> GetSelectStatement()
{
var studentType = typeof(Student);
var viewModelType = typeof(ManagerStudentListViewModel);
var parameterExpression = Expression.Parameter(studentType, "x");
var newInstantiationExpression = Expression.New(viewModelType);
// Name Binding
var viewModelProperty = viewModelType.GetProperty("Name");
var studentProperty = studentType.GetProperty("Name");
var nameMemberExpression = Expression.Property(parameterExpression, studentProperty);
var nameBinding = Expression.Bind(viewModelProperty, nameMemberExpression);
// ClassType.Description Binding
// ???
var bindings = new List<MemberAssignment>() { nameBinding, classTypeBinding };
var memberInitExpression = Expression.MemberInit(newInstantiationExpression, bindings);
var lambda = Expression.Lambda<Func<Student, ManagerStudentListViewModel>>(memberInitExpression, parameterExpression);
return lambda.Compile();
}
Accessing deeply nested members is no different than accessing any other properties, provided you know the name of the members. Just create an expression to get the first property, then add the expression to get the second.
Expression<Func<Student, ManagerStudentListViewModel>> GetSelectStatement()
{
var studentType = typeof(Student);
var viewModelType = typeof(ManagerStudentListViewModel);
var param = Expression.Parameter(studentType, "x");
var nameValue = Expression.Property(param, "Name");
var classTypeValue = Expression.Property(
Expression.Property(param, "ClassType"), // get the class type
"Description"); // get the description of the class type
var nameMemberBinding = Expression.Bind(
viewModelType.GetProperty("Name"),
nameValue);
var classTypeMemberBinding = Expression.Bind(
viewModelType.GetProperty("ClassType"),
classTypeValue);
var initializer = Expression.MemberInit(
Expression.New(viewModelType),
nameMemberBinding,
classTypeMemberBinding);
return Expression.Lambda<Func<Student, ManagerStudentListViewModel>>(initializer, param);
}

How to search a database using properties of model

I have an MVC4 program in which I have a dropdown with a list of all of the properties of a model. I want the user to be able to select which property then type in a string value to the textbox to search. The issue is I can't dynamically change the query with the value of the dropdown.
public ActionResult SearchIndex(string searchString, string searchFields)
{
var selectListItems = new List<SelectListItem>();
var first = db.BloodStored.First();
foreach(var item in first.GetType().GetProperties())
{
selectListItems.Add(new SelectListItem(){ Text = item.Name, Value = item.Name});
}
IEnumerable<SelectListItem> enumSelectList = selectListItems;
ViewBag.SearchFields = enumSelectList;
var bloodSearch = from m in db.BloodStored
select m;
if (!String.IsNullOrEmpty(searchString))
{
PropertyInfo selectedSearchField = getType(searchFields);
string fieldName = selectedSearchField.Name;
//Dynamic Linq query this is how it needs to be set up to pass through linq.dynamic without exception
bloodSearch = bloodSearch.Where("x => x." + fieldName + " == " + "#0", searchString).OrderBy("x => x." + fieldName);
return View(bloodSearch);
}
return View(bloodSearch);
}
Here is my getType method
public PropertyInfo getType(string searchFields)
{
var first = db.BloodStored.First();
foreach (var item in first.GetType().GetProperties())
{
if(searchFields == item.Name)
{
return item;
}
}
return null;
}
I have updated my code to reflect the working query encase it can help anyone else out.
Here is a link to the post that I found my answer from Dynamic query with LINQ won't work
you should use the Dynamic.cs library. you can find it here as well as examples. You can then create your where clauses dynamically.
dynamic.cs
you could build the expression yourself.
static Expression<Func<T, bool>> GetExpression<T>(string propertyName, string propertyValue)
{
var parameterExp = Expression.Parameter(typeof(T), "type");
var propertyExp = Expression.Property(parameterExp, propertyName);
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var someValue = Expression.Constant(propertyValue, typeof(string));
var containsMethodExp = Expression.Call(propertyExp, method, someValue);
return Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp);
}
stole this from a post by Marc Gravell.

Building an OrderBy Lambda expression based on child entity's property

I'm trying to generate a LINQ OrderBy clause using lambda expressions with an input of the column name of an entity as a string (in the "sortOn" variable below).
The code below works fine for a sortOn value like "Code" generating the lambda
p => p.Code
But I would also like to sort on a child entity, where the lambda might be
p => p.Category.Description
So in this instance, I would just like to set sortOn = "Category.Description" and have the correct lamdba expression generated.
Is this possible? Any suggestions about the best way to do this would be welcomed.
This code works fine for the simple case:
var param = Expression.Parameter(typeof (Product), "p");
var sortExpression = Expression.Lambda<Func<Product, object>>(
Expression.Property(param, sortOn), param);
if (sortAscending ?? true)
{
products = products.OrderBy(sortExpression);
}
else
{
products = products.OrderByDescending(sortExpression);
}
The use-case for this problem is displaying a grid of data and being able to sort the data, simply by passing the column name to be sorted on back to the server. I'd like to make the solution generic, but have started using a particular type (Product in the example) for now.
This will generate proper lambda expression:
var sortOn = "Category.Description";
var param = Expression.Parameter(typeof(Product), "p");
var parts = sortOn.Split('.');
Expression parent = param;
foreach (var part in parts)
{
parent = Expression.Property(parent, part);
}
var sortExpression = Expression.Lambda<Func<Product, object>>(parent, param);
Here is an extension OrderBy method which works for any number of nested parameters.
public static IQueryable<T> OrderBy<T>(this IQueryable<T> query, string key, bool asc = true)
{
try
{
string orderMethodName = asc ? "OrderBy" : "OrderByDescending";
Type type = typeof(T);
Type propertyType = type.GetProperty(key)?.PropertyType; ;
var param = Expression.Parameter(type, "x");
Expression parent = param;
var keyParts = key.Split('.');
for (int i = 0; i < keyParts.Length; i++)
{
var keyPart = keyParts[i];
parent = Expression.Property(parent, keyPart);
if (keyParts.Length > 1)
{
if (i == 0)
{
propertyType = type.GetProperty(keyPart).PropertyType;
}
else
{
propertyType = propertyType.GetProperty(keyPart).PropertyType;
}
}
}
MethodCallExpression orderByExpression = Expression.Call(
typeof(Queryable),
orderMethodName,
new Type[] { type, propertyType },
query.Expression,
CreateExpression(type, key)
);
return query.Provider.CreateQuery<T>(orderByExpression);
}
catch (Exception e)
{
return query;
}
}
The CreateExpression method which is used in my solution is defined in this post.
The usage of the OrderBy extension method is as follows.
IQueryable<Foo> q = [Your database context].Foos.AsQueryable();
IQueryable<Foo> p = null;
p = q.OrderBy("myBar.name"); // Ascending sort
// Or
p = q.OrderBy("myBar.name", false); // Descending sort
// Materialize
var result = p.ToList();
The type Foo and its properties are also taken from the same post as method CreateExpression.
Hope you find this post helpful.
You can use the Dynamic LINQ Query Library to do this easily. Assuming you have an IQueryable<T> implementation of Product, you can easily do:
IQueryable<Product> products = ...;
// Order by dynamically.
products = products.OrderBy("Category.Description");
The blog post has a link to the libary, and you'll have to build/include the project in your solution yourself, but it works very well, and the parsing is very robust. It prevents you from having to write the parsing code yourself; even for something so simple, if the requirements expand, the library has you covered, whereas a homegrown solution does not.
It also has a number of other dynamic operators (Select, Where, etc.) so you can perform other dynamic operations.
There's no magic under the hood, it just parses the strings you pass it and then creates the lambda expressions based on the parsing results.
If you don't need expressions, how about:
products = products.Orderby(p1 => p1.Code).ThenBy(p2 => p2.Category.Description)
Hi you can also create an extension method like which can sort to any depth not only just child
public static IEnumerable<TSource> CustomOrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
{
List<string> list=new List<string>();
List<TSource> returnList=new List<TSource>();
List<int> indexList = new List<int>();
if (source == null)
return null;
if (source.Count() <= 0)
return source;
source.ToList().ForEach(sc=>list.Add(keySelector(sc).ToString())); //Extract the strings of property to be ordered
list.Sort(); //sort the list of strings
foreach (string l in list) // extract the list of indexes of source according to the order
{
int i=0;
//list.ForEach(l =>
foreach (var s in source.ToList())
{
if (keySelector(s).ToString() == l)
break;
i++;
}
indexList.Add(i);
}
indexList.ForEach(i=>returnList.Add(source.ElementAt(i))); //rearrange the source according to the above extracted indexes
return returnList;
}
}
public class Name
{
public string FName { get; set; }
public string LName { get; set; }
}
public class Category
{
public Name Name { get; set; }
}
public class SortChild
{
public void SortOn()
{
List<Category> category = new List<Category>{new Category(){Name=new Name(){FName="sahil",LName="chauhan"}},
new Category(){Name=new Name(){FName="pankaj",LName="chauhan"}},
new Category(){Name=new Name(){FName="harish",LName="thakur"}},
new Category(){Name=new Name(){FName="deepak",LName="bakseth"}},
new Category(){Name=new Name(){FName="manish",LName="dhamaka"}},
new Category(){Name=new Name(){FName="arev",LName="raghaka"}}
};
var a = category.CustomOrderBy(s => s.Name.FName);
}
}
Its custom method and right now it works only for string property only however it can be reactified using generics to work for any primitive type. I hope this will help.

Expression.Lambda and query generation at runtime, simplest "Where" example

I was trying to generate a simple Lambda Expression at runtime with no luck... something like this:
var result = queryableData.Where(item => item.Name == "Soap")
Here is my example class and a fixture queryable:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
}
IQueryable<Item> queryableData = ...;
Then I generate a lambda expression at runtime correct code follows:
//"item" in "item =>..."
var item = Expression
.Parameter(typeof(Item), "item");
//property of my item, this is "item.Name"
var prop = Expression
.Property(item, "Name");
//then "Soap" in '... => item.Name=="Soap"'
var value = Expression.Constant("Soap");
//equality expression "==" in my primer
var equals = Expression.Equal(prop, value);
//then lambda
var lambda = Expression.Lambda<Func<Item, bool>>(equals, item);
//and here are the results
var results = queryableData.Where(lambda);
Big thanks to dtb for advice!
In the following query
var result = query.Where(item => item.Name == "Soap")
the lambda expression is
item => item.Name == "Soap"
You only need to construct this part, not the Where call which accepts an expression tree.
The expression tree for the lambda expression looks like this:
Lambda
/ \
Equal Parameter
/ \ item
Property \
"Name" Constant
| "Soap"
Parameter
item
In code:
var item = Expression.Parameter(typeof(Item), "item");
var prop = Expression.Property(item, "Name");
var soap = Expression.Constant("Soap");
var equal = Expression.Equal(prop, soap);
var lambda = Expression.Lambda<Func<Item, bool>>(equal, item);
var result = queryableData.Where(lambda);

Categories

Resources