OrderBy with a String keySelector - c#

I have the following function that is extracting me distinct values based on the properties of an object, here Client.
public List<DistinctValue> GetDistinctValues(string propertyName)
{
//how should I specify the keySelector ?
Func<string, object> keySelector = item => propertyName;
var list = new List<DistinctValue>();
var values = this.ObjectContext.Clients.Select(CreateSelectorExpression
(propertyName)).Distinct().OrderBy(keySelector);
int i = 0;
foreach (var value in values)
{
list.Add(new DistinctValue() { ID = i, Value = value });
i++;
}
return list;
}
private static Expression<Func<Client, string>> CreateSelectorExpression
(string propertyName)
{
var paramterExpression = Expression.Parameter(typeof(Client));
return (Expression<Func<Client, string>>)Expression.Lambda(
Expression.PropertyOrField(paramterExpression, propertyName),
paramterExpression);
}
public class DistinctValue
{
[Key]
public int ID { get; set; }
public string Value { get; set; }
}
I'm doing this because I do not know in before which property values I'll need to extract.
It's working, just the result is not sorted.
Can you please help me correct the sorting to make the OrderBy work as expected?
The properties are strings and I don't need to chain the sorting. I don't need to specify the sorting order either.
Thanks a lot in advance,
John.

Your keySelector currently returns the same string for each (the property name); and since LINQ is typically a stable sort, this results in no overall change. Since you have already projected to the string values, you can simply use a trivial x=>x mapping here:
var values = this.ObjectContext.Clients.Select(
CreateSelectorExpression(propertyName)).Distinct().OrderBy(x => x);
to order by the items themselves.

Thanks for the elegant solution. I further expanded upon the CreateSelectorExpression method so it can be leveraged outside of the Client class in the example above.
public static Expression<Func<T, string>> CreateSelectorExpression<T>(string propertyName)
{
var paramterExpression = Expression.Parameter(typeof(T));
return (Expression<Func<T, string>>)Expression.Lambda(Expression.PropertyOrField(paramterExpression, propertyName),
paramterExpression);
}
Usage
Func<IQueryable<YourEntity>, IOrderedQueryable<YourEntity>> orderBy = o => o.OrderByDescending(CreateSelectorExpression<YourEntity>("Entity Property Name"));

Related

LINQ to Entities and polymorphism

Let's say I have a simple model:
public class Movie
{
public int ID { get; set; }
public string Name { get; set; }
}
And a DbContext:
public class MoviesContext : DbContext
{
...
public DbSet<Movie> Movies { get; set; }
}
Also I have a method in MoviesContext class that filters Movies by substring like this:
return Movies.Where(m => m.Name.Contains(filterString)).Select(m => m);
Now suppose I'd like to add a new model, say:
public class Person
{
public int ID { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string FullName { get { return FirstName + (MiddleName?.Length > 0 ? $" {MiddleName}" : "") + $" {LastName}"; } }
}
I also want to filter persons (DbSet Persons) by name (i.e. FullName). I'd like DRY, so it is preferrable to generalize a filter method of MoviesContext. And, what is important, I'd like to do filtering on the database level. So I have to deal with LINQ for Entities.
If not for this, the task is pretty simple. I could use an abstract class and add a virtual method that do the "contains substring" logic. Alternatively, I could use an interface. Unfortunately, because of LINQ for Entities, I can't use a FullName property (which is not convenient but bearable) and I can't write something like this:
return dbset.Where(ent => ent.NameContains(filterString)).Select(ent => ent);
So, how to solve this problem? I've found some solution (almost have my head broken), but I am not very happy with it. I'll post my solution separately, but I hope there is a more elegant one.
Reading your code a little more closely, instead of your NameFilterable abstract class, could you not do something like this:
public interface IHasPredicateGetter<T> {
[NotNull] Expression<Func<T, bool>> GetPredicateFromString([NotNull] string pValue);
}
public class Movie : IHasPredicateGetter<Movie> {
public int ID { get; set; }
public string Name { get; set; }
public Expression<Func<Movie, bool>> GetPredicateFromString(string pValue) {
return m => m.Name.Contains(pValue);
}
}
This prevents you from needing a cast, for example. It's so hard to grasp just what you're trying to do here, so I'm not sure this is the whole thing or not. You're still stuck with an instance method that should probably be a static method, but it couldn't implement an interface otherwise.
My solution looks like this.
[1] The base class:
public abstract class NameFilterable
{
protected static Expression<Func<T, bool>> False<T>() { return f => false; }
public virtual Expression<Func<T, bool>> GetNameContainsPredicate<T>(string filterString)
{
return False<T>();
}
}
[2] The Person class (I'll omit the Movie class, it is more simple):
public class Person : NameFilterable
{
...
public override Expression<Func<T, bool>> GetNameContainsPredicate<T>(string filterString)
{
return entity =>
String.IsNullOrEmpty(filterString) ||
(entity as Person).LastName.Contains(filterString) ||
(entity as Person).FirstName.Contains(filterString) ||
(((entity as Person).MiddleName != null) && (entity as Person).MiddleName.Contains(filterString))
;
}
}
[3] The filter methods in MoviesContext:
private static IQueryable<T> _filterDbSet<T>(DbSet<T> set, Expression<Func<T, bool>> filterPredicate) where T : class
{
return set
.Where(filterPredicate)
.Select(ent => ent);
}
private static IQueryable<T> _filterDbSet<T>(DbSet<T> set, string search = null) where T : NameFilterable, new()
{
T ent = new T();
return _filterDbSet<T>(set, (ent as NameFilterable).GetNameContainsPredicate<T>(search));
}
public static ICollection<T> Filter<T>(DbSet<T> set, string search = null) where T : NameFilterable, new()
{
return _filterDbSet(set, search).ToList();
}
And it seems that all this works pretty well. But I can't say it is very elegant.
[1] I have to use a generic T, though on the Person level I always work with Person objects (or descendants). So I have to convert T to Person (as Person).
[2] In GetNameContainsPredicate method, I can't write (because of LINQ for Entities):
return entity =>
{
Person p = entity as Person;
String.IsNullOrEmpty(filterString) ||
p.LastName.Contains(filterString) ||
p.FirstName.Contains(filterString) ||
((p.MiddleName != null) && p.MiddleName.Contains(filterString))
};
[3] I can't use static methods (statics couldn't be overridden), so I have to create a dummy T object (T ent = new T();).
[4] I still can't use a FullName.Contains(filterString)
So, the question remains: Maybe I miss something and there is a more elegant solution to the problem?
You could create a method that is responsible for search if a type has a particular property and filter for that property,if the object has not the property simply return null. Whith this you can create an expression that filters for this property
//gets the property info of the property with the giving name
public static PropertyInfo GetPropetyInfo<T>(string name)
{
var type = typeof(T);
var property = type.GetProperty(name);
return property;
}
//Creates an expression thats represents the query
public static Func<T, bool> GetFilterExpression<T>( string propertyName, object propertyValue)
{
var prop = GetPropetyInfo<T>(propertyName);
if(prop==null)return t=>false;
var parameter = Expression.Parameter(typeof(T), "t");
Expression expression = parameter;
var left = Expression.Property(expression, prop);
if (prop.PropertyType == typeof(string))
{
var toLower = typeof(string).GetMethods().FirstOrDefault(t => t.Name.Equals("ToLower"));
var tlCall = Expression.Call(left, toLower);
var right = Expression.Constant(propertyValue.ToString().ToLower());
var contains = Expression.Call(tlCall, typeof(string).GetMethod("Contains"), right);
var containsCall = Expression.IsTrue(contains);
expression = Expression.AndAlso(Expression.NotEqual(left, Expression.Constant(null)), containsCall);
}
else
{
if (prop.PropertyType.ToString().ToLower().Contains("nullable"))
{
var getValue = prop.PropertyType.GetMethods().FirstOrDefault(t => t.Name.Equals("GetValueOrDefault"));
var getValueCall = Expression.Call(left, getValue);
var right = Expression.Constant(propertyValue);
expression = Expression.Equal(getValueCall, right);
}
else
{
var value = Convert.ChangeType(propertyValue,prop.PropertyType);
var right = Expression.Constant(value);
expression = Expression.Equal(left, right);
}
}
return Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { parameter }).Compile();
}
The you can use it as follow
var expression = YOURCLASS.GetFilterExpression<Person>("LastName", "Jhon");
var result=dbset.Where(expression);
There are a few things I've done to get polymorphism with EF, but in your specific case of wanting reusable filters, I'm not sure it's worth the trouble. I've basically tried to do the same exact thing, but every time I end up realizing that there's no point. Ask yourself: what exactly are the benefits of doing this, and how is it any more flexible than what a Where clause already offers?
There are two issues. One is that it's hard or nigh impossible to get a filter to be used between two separate classes by using a shared interface (INamedObject for example). This is because you need a strongly typed expression. You can make a function that returns a strongly typed expression, but why would you not have just wrote the expression in the first place? The other issue is that you need a new filter expression for every search value, which is pretty close to where we are already.
If you perfected this, what would you have? The ability to infer type, specify a search value, and get an expression you could use? Isn't that essentially what we already have? The way Where clauses already are, they already have strong typing, and the ability to use dynamic search values. While it might feel a tiny bit redundant to say x => x.Name == value in more than one place, really the ability to specify such a concise and powerful filter statement is already a pretty amazing place to be.

Creating Dynamic Queries in C#

I am creating a custom reporting tool that allows the user to enter a list of criteria.
public partial class CustomReportCriteria
{
public int Id { get; set; }
public int ReportId { get; set; }
public string Operator { get; set; }
public string CriteriaValue { get; set; }
public string CriteriaType { get; set; }
public string CriteriaField { get; set; }
public string PropertyType { get; set; }
public virtual CustomReport CustomReport { get; set; }
}
I need to utilize these fields to create dynamic queries on my database. For example:
Select BidYear From FWOBid Where BidYear = 2015
// ^ this would look like this instead
Select CriteriaField From PropertyType Where CriteriaField [Operator] CriteriaValue
However, I may not always be querying the same table, and in most cases the query will require joins on other tables.
I already have generic methods for the where clauses:
public static IQueryable<T> Filter<T>(IQueryable<T> query, string propertyName, string propertyValue, string op)
{
PropertyInfo propertyInfo = typeof(T).GetProperty(propertyName);
return Filter<T>(query, propertyInfo, propertyValue, op);
}
public static IQueryable<T> Filter<T>(IQueryable<T> query, PropertyInfo propertyInfo, string propertyValue, string op)
{
ParameterExpression e = Expression.Parameter(typeof(T), "e");
MemberExpression m = Expression.MakeMemberAccess(e, propertyInfo);
UnaryExpression c = GetExpressionByType(propertyValue, propertyInfo.PropertyType);
BinaryExpression b = null;
if (op == "=")
{
b = Expression.Equal(m, c);
}
else if (op == "!=")
{
b = Expression.NotEqual(m, c);
}
else if (op == ">")
{
b = Expression.GreaterThan(m, c);
}
else if (op == "<")
{
b = Expression.LessThan(m, c);
}
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(b, e);
return query.Where(lambda);
}
The approach I was envisioning was something like this:
foreach (var crit in report.CustomReportCriterias)
{
var objectName = crit.PropertyType;
var value = crit.CriteriaValue;
var type = crit.CriteriaType;
var field = crit.CriteriaField;
var op = crit.Operator;
// how to dynamically get a generic collection ?
var queryable = _context.Set<objectName>();
var results = Filter<objectName>(queryable, field, value, op);
// would then do joins if needed
}
But, I'm struggling with the initial steps of retrieving a queryable from the db based on a string, and then I'm lost as how to join these results after.
Using Entity Framework you can execute a query directly and have the results map to an entity like this.
var results = db.ExecuteStoreQuery<YourEntity>(
"SELECT * FROM YourEntities WHERE SomeColumn = #param1",
param1);
Have a look at https://dynamiclinq.codeplex.com/releases/view/109593. This library extends the Linq library with a System.Linq.Dynamic namespace that provides string-based overloads to common query methods, so you can dynamically build string-based criteria for filtering and grouping. In addition, the one in my link is one better than the version available through NuGet, because it allows you to define the entire query, including the record type to retrieve, in Linq syntax as one string (see the Documentation tab for one example; it uses a static typeof expression to provide a Type to parse the results into, but you can reflectively create generic types with Type.MakeGenericType()).
The problem's going to be getting back to a statically-defined type; once you go dynamic, you tend to have to stay dynamic, using GetProperties() to enumerate data fields and GetValue/SetValue to manipulate them. There are a few tricks, typically requiring the use of a set of concretely-typed overloads that force the runtime to examine the true type of the object, then once the object's passed into one of those overloads you're back in static world again.

Passing expression into lambda

I have such class:
public class SomeClass
{
public string Text1 { get; set; }
public string Text2 { get; set; }
public int Number { get; set; }
}
And I have list of this classes objects:
List<SomeClass> myList = new List<SomeClass>();
I want to query this list using LINQ (lambda syntax):
var result = myList.Where(obj => obj.Text1 == "SomeString");
Is there any way to pass property(eg. by string name), by which I want this LINQ query to be performed? In this example, I search by Text1 property, but let's say I want to invoke this search dynamically on Text1 or Text2 (determined in runtime). I want to be able to pass property name, on which this search is performed, and check whether this property is string, so that I'm sure this search CAN be performed first.
Is that possible? I know Reflections and Expressions have something to do about it, but I don't know them very well.
Thanks
The approach using Reflection:
var result = myList.Where(obj => obj.GetType()
.GetProperty("Text1")
.GetValue(obj)
.Equals("SomeString"));
With this way you can change from "Text1" to "Text2" property.
Another approach you can use dynamic linq:
var result = myList.AsQueryable().Where("Text1=#0", "SomeString");
Dynamic LINQ is also available via nuget.
You could use expression-trees?
string memberName = "Text1", value = "SomeString";
var p = Expression.Parameter(typeof(SomeClass), "obj");
var predicate = Expression.Lambda<Func<SomeClass, bool>>(
Expression.Equal(
Expression.PropertyOrField(p, memberName),
Expression.Constant(value,typeof(string))
), p);
var result = myList.AsQueryable().Where(predicate);
or alternative for the last line:
var result = myList.Where(predicate.Compile());

How to apply a filter on LINQtoSQL results?

With the ListBox control it is possible to feed it a DataSource, name a DisplayMember and a ValueMember and through some magic it will display a field from the DataSource and return a selected ValueMember. It can work wit a linq-to-sql result without even knowing anyting specific about the table it is feed with.
Isn't Reflection and Attributes doing some magic? How does it work!
I have a need to do something similar but I do not know where to start. I'm a beginner for LINQtoSQL.
This is what I want to do.
I have a source table that I want to filter. The source table can be anything but will be originating from some DataContext.
var MySourceTable =
from MyRecord in Context.GetTable<MySourceTable>()
select new
{
Value = MyRecord.ID,
Display = MyRecord.Name,
FilterValue = MyRecord.Value
};
In my control I want to be able to filter MySourceTable on some given value. The control does not know what table is used (MySourceTable in the example above) and the control does only know the three names, ID, Name and Value of the fields in the record it should use.
The filter query should look like the example below.
var MyTable
from Record in MySourceTable
where FilterValue == GivenValue
select new
{
Value = Record.ID,
Display = Record.Name,
};
Can somebody advise me on where to start?
It looks like what you are missing is in the where condition on your query. It should look like this:
var MyTable =
from Record in MySourceTable
where Record.FilterValue == GivenValue
select new
{
Value = Record.ID,
Display = Record.Name,
};
GivenValue is presumably a local variable or property containing whatever you want to compare FilterValue against. But FilterValue is a property of the anonymous type that you created in your first query that created MySourceTable. In your second query, Record is an instance of that anonymous type, and you have to use that reference to the instance in all other parts of the query to reference the instance that you are checking for the where clause or selecting for the select clause. If you just put FilterValue there, then it has no idea what you mean.
I wrote a filter engine that takes in a Property and Value as a string, and is able to use that as a where clause.
IQueryable<T> FilterFunction<T>(IQueryable<T> query)
{
ParameterExpression p = Expression.Parameter(typeof(T), "notused");
Expression<Func<T, bool>> wherePredicate =
Expression.Lambda<Func<T, bool>>(
Expression.Equal(
Expression.Call(Expression.Property(p, FilterProperty), "ToString", new Type[0]),
Expression.Constant(FilterValue)), p);
return query.Where(wherePredicate);
}
You should be able to pass in an Expression<Func<T, TResult>> built in a similar way into query.Select()
If I am understanding your question correctly, I believe this will work:
string DisplayProperty = "Name";
string ValueProperty = "ID";
IQueryable<Record> SelectRecordProperties<T>(IQueryable<T> query)
{
ParameterExpression p = Expression.Parameter(typeof(T), "notused");
MethodInfo ctorMethod = typeof(Record).GetMethod("Create");
Expression<Func<T, Record>> selectPredicate =
Expression.Lambda<Func<T, Record>>(
Expression.Call(ctorMethod,
Expression.PropertyOrField(p, DisplayProperty),
Expression.PropertyOrField(p, ValueProperty)), p);
return query.Select(selectPredicate);
}
class Record
{
public static Record Create(string display, string value)
{
return new Record() { Display = display, Value = value };
}
public object Display { get; set; }
public object Value { get; set; }
}
So for your full function you'd need to combine these two ideas so that your filtering works.
By the way, there are many possible ways to build the expression tree for this, there was some tool I've found at one point which would show you the expression tree I think, so you could manually write the linq query and see how .Net builds the expression, then modify this code to build it based on that to possibly get a more efficient expression tree.
I found a way, it works but is not a completely satisfying method.
The 'problem' (compared to my original question) is that it does not use linq-to-sql for filtering the source. But it works and that is at the moment fine for me.
Recap:
At a high level module I prepare a LINQtoSQL statement which will result in some IQueriable<Object>, effectively an IQueriable object. This object is given to a lower level module together with three names, one for the ID, one for the Display and one for filtering.
When I need more control over the source, e.g. for unfiltered data that will result in huge results or for complex LINQtoSQL queries I will use a delegate function. That will give me all the control I want.
At a low level I have a IQueriable<Object> and no knowledge at all about the Object, except for wat Reflection can tell me. I generate a table of results that I want to use in a control-specific format. (the Record class). For anything more complex that my standard code cannot handle I offer a delegate that must result in some list of object in which the object must have at least a property named 'Display' and a property named 'Value'. Other properties are possible but will not be used.
This is the solution that I finaly got to work:
public partial class MySelector : UserControl
{
class Record
{
public object Display { get; set; }
public object Value { get; set; }
}
....
public string MyDisplayMember { get; set; }
public string MyValueMember { get; set; }
public string MyExternalMember { get; set; }
....
static Object Filter(MySelector sender, Object criterium)
{
IQueryable source = sender.MySource as IQueryable;
if (source == null) return null;
List<Record> result = new List<Record>();
// drawback: this foreach loop will trigger a unfiltered SQL command.
foreach (var record in source)
{
MethodInfo DisplayGetter = null;
MethodInfo ValueGetter = null;
bool AddRecord = false;
foreach (PropertyInfo property in record.GetType().GetProperties())
{
if (property.Name == sender.MyDisplayMember)
{
DisplayGetter = property.GetGetMethod();
}
else if (property.Name == sender.MyValueMember)
{
ValueGetter = property.GetGetMethod();
}
else if (property.Name == sender.MyExternalMember)
{
MethodInfo ExternalGetter = property.GetGetMethod();
if (ExternalGetter == null)
{
break;
}
else
{
object external = ExternalGetter.Invoke(record, new object[] { });
AddRecord = external.Equals(criterium);
if (!AddRecord)
{
break;
}
}
}
if (AddRecord && (DisplayGetter != null) && (ValueGetter != null))
{
break;
}
}
if (AddRecord && (DisplayGetter != null) && (ValueGetter != null))
{
Record r = new Record();
r.Display = (DisplayGetter == null)
? null
: DisplayGetter.Invoke(record, new object[] { });
r.Value = (ValueGetter == null)
? null
: ValueGetter.Invoke(record, new object[] { });
result.Add(r);
}
}
return result;
}
}

C# Linq where clause according to property name

Let's say I have the following class :
public class Person {
public string FirstName { get; set; }
public string SurName { get; set; }
public int Age { get; set; }
public string Gender { get; set; }
}
Also, I have the following method and I am reaching out to person data via a repository.
public IEnumerable<Person> getPeople(string searchField, string searchTerm) {
//_repo.GetAll() returns IEnumerable<Person>
var model = _repo.GetAll();
//Need the logic here for filtering
return model;
}
As you can see I am getting two parameter for the method : searchField and searchTerm.
searchField is for the field name whose value will be used for filtering. searchTerm is the value which will be used to compare with retrived value (sorry if I am not clear here but this is the most I can come up with)
What I would normally do is as follows :
public IEnumerable<Person> getPeople(string searchField, string searchTerm) {
//_repo.GetAll() returns IEnumerable<Person>
var model = _repo.GetAll();
switch(searchField) {
case "FirstName":
model = model.Where(x => x.FirstName == searchTerm);
break;
case "SurName":
model = model.Where(x => x.SurName == searchTerm);
break;
//Keeps going
}
return model;
}
Which will work just fine. But if I make a change on my class, this code will have a change to break or be in lack of some functions if I add new properties this class.
What I am looking for is something like below :
NOTE :
This below code completely belongs to my imagination and there is no such a
thing exists.
model = model.Where(x => x.GetPropertyByName(searchField) == searchTerm);
Am I flying too high here if it is impossible or being complete idiot if there is already a built in way for this?
Looks like you need Dynamic Linq queries:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx
I use this extension method to achieve what you want.
public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> source, string propertyName, string value)
{
Expression<Func<TEntity, bool>> whereExpression = x => x.GetType().InvokeMember(propertyName, BindingFlags.GetProperty, null, x, null).ObjectToString().IndexOf(value, StringComparison.InvariantCultureIgnoreCase) >= 0;
return source.Where(whereExpression);
}
Note: ObjectToString is just another extension method that returns string.Empty if the Object passed in is NULL
For linq2Object You can use reflection as bellow(it's not very fast):
model.Where(x => x.GetType().GetProperty(propName).GetValue(x, null) == propVal);
but for linq2Entity I think this doesn't work, it works for linq2objects.
I think the following implementation looks an awful lot like what you originally intended, although changing this to a generic method likely makes more sense.
public IEnumerable<Person> getPeople(string searchField, string searchTerm) {
PropertyInfo getter=typeof(Person).GetProperty(searchField);
if(getter==null) {
throw new ArgumentOutOfRangeException("searchField");
}
return _repo.GetAll().Where(x => getter.GetValue(x, null).ToString()==searchTerm);
}
This should be type-safe:
public IEnumerable<T> Where<T,U>(Func<T,U> propertySelector, U value)
{
return model.Where(x => propertySelector(x) == value);
}
usage:
Where((MyClass x) => x.PropertyName, propertyValue);
Or:
public IEnumerable<T> Where<T>(Func<T,bool> entitySelector)
{
return model.Where(entitySelector);
}
usage:
Where<MyClass>(x => x.PropertyName == propertyValue && x.OtherProperty == otherValue);
Use Reflection
model = model.Where(x =>
((string)x.GetType().GetProperty("searchField").GetValue(0, null)) == searchTerm);
Rather than messing with reflection, custom expression trees, etc., when using Entity Framework, consider using the Builder Method extensions to the standard LINQ operators which take strings rather than lambdas. These are much easier to build for dynamic query requirements:
string filter = String.Format("it.{0} = #value", fieldName);
var model = context.People.Where(filter, new ObjectParameter("value", searchValue));
Of course, this would mean that you yould need to modify your repository to return IObjectSet rather than IEnumerable. It would perform better as well. By returning IEnumerable, you are hydrating every row in your database to your repository and then filtering via LINQ to Objects rather than applying the filter back in your database.
For more information about the Builder Methods in EF, see the BuilderMethodSamples.cs in http://archive.msdn.microsoft.com/EFQuerySamples/Release/ProjectReleases.aspx?ReleaseId=4422.

Categories