C# Linq generic search - c#

I am trying to write a method for a base repository service. The idea is I want a generic class that can be used for simple entity types hat can also be overridden for more complex entities. I am writing a search method with the idea being that for simple entities there will be a component which will have one or more properties with fields which match properties of the entity. if these are found then build a where linq statement to query it. Here's what I have so far:
public IQueryable<T> GetAll()
{
return entityRepository.GetAll();
}
public IQueryable<T> Search(IBaseComponent component)
{
IQueryable<T> all = GetAll();
Type type = typeof(T);
Type componentType = component.GetType();
foreach (var componentProperty in componentType.GetProperties())
{
foreach (var property in type.GetProperties())
{
if (property.Name.Equals(componentProperty.Name))
{
var value = componentProperty.GetValue(component);
ParameterExpression gpe = Expression.Parameter(property.DeclaringType, "a");
var selector = Expression.Equal(Expression.Property(gpe, property), Expression.Constant(value));
var keySelector = Expression.Lambda(selector, gpe);
var t = all.Where(keySelector);
break;
}
}
}
var test = all.ToArray();
return all;
}
Obviously this would only theoretically work for one property at the moment. The service itself has a type parameter (so its BaseService<T>).
The problem I am having is that this will not compile. The line all.Where(keySelector) gives the error:
'System.Linq.IQueryable<T>' does not contain a definition for 'Where' and the best extension method overload 'System.Linq.Enumerable.Where<TSource>(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource,int,bool>)' has some invalid arguments
I'm not sure what's wrong here, they types are all correct as far as I can see. What am I missing? Or am I trying to do something impossible?

Expression.Lambda return type is LambdaExpression even if the concrete type is Expression<Func<T, bool>> (LambdaExpression is the non generic base class for Expression<T>)
#Euphoric remark is spot on and the var keyword is the problem here as the reason for this error would have been obvious without it.
The correct code is something like :
var keySelector = (Expression<Func<T, bool>>)Expression.Lambda(selector, gpe);

Related

Use IQueryable.OrderBy for a composite key without loading all the items?

I am retrieving some tuples from a database that are mapped to entity classes by means of Entity Framework.
For these entities, I have a key selector function (supplied at runtime by other developers) that I would like to pass to Queryable.OrderBy. The key selector function is provided upon "registration" of the entity type in my system - which happens by means of a method that looks roughly like this:
public void RegisterEntity<TEntity, TKey>(string entityName, TKey defaultKey, Func<TEntity, TKey> keySelectorFunc)
I would like to execute this OrderBy call before materializing the results to entity objects (i.e. in such a way that the OrderBy call still gets translated to SQL under the hood).
The problem is that the entities have composite keys, and thus, the key selector function will return a custom object instantiated in the function. You can imagine it like this:
var keySelectorFunc = e => new CustomKey(e.Value1, e.Value2);
As usual, Entity Framework does not like this (the usual "Only parameterless constructors and initializers are supported in LINQ to Entities" error).
Is there any way to use such a custom key selector function to return a custom key? Do I have to resort to anonymous classes? Or should I move the OrderBy call to a place after I have left the LINQ-to-Entities world?
In this particular case it would be easy to use Sort method of Generic List.
https://msdn.microsoft.com/en-us/library/3da4abas(v=vs.110).aspx
Sort method requires the type of the list to implement IComparable interface and it uses the implementation of CompareTo method from IComparable interface. Otherwise implementation of IComparer also can be passed to this method.
So if your entity class is already implemeting IComparable interface then this should surely work for you. You will have to to .ToList() on the IQueryable result of course before you can call the Sort method on it.
public class Category : IComparable<Category>
{
public int CategoryId { get; internal set; }
public string CategoryName { get; internal set; }
public int CompareTo(Category x)
{
return String.Compare(x.CategoryName, this.CategoryName, StringComparison.InvariantCulture);
}
}
List<Category> categories = new List<Category>();
categories.Add(new Category {CategoryName = "Cate1"});
categories.Add(new Category {CategoryName = "Cate2"});
categories.Sort();
foreach (var cat in categories)
{
Console.WriteLine(cat.CategoryName);
}
This displays me category names in reverse order based on the comparison logic I have written in the CompareTo method of Category Class.
In this case I think the best way is use a custom ExtensionMethod to avoid any overhead of coding or unnecessary complexity to do that.
See if it implementation can help you.
First we create your customkey class that is responsable to create the statement expressions:
class CustomKey
{
public CustomKey(params string[] value)
{
if(!value.Any())
throw new InvalidOperationException("Select at least one Property for this operation");
Values = new List<string>();
Values.AddRange(value);
}
private List<string> Values { get; set; }
// this method run throughout all property configured to create the expressions
public void ForEachProperty<TSource, TKey>(Action<Expression<Func<TSource, TKey>>, bool> method)
{
bool firstItem = true;
Values.ForEach(f =>
{
var expression = CreateExpression<TSource, TKey>(f);
method(expression, firstItem);
firstItem = false;
});
}
// this method is responsable to create each expression
Expression<Func<TSource, TKey>> CreateExpression<TSource, TKey>(string property)
{
var parameter = Expression.Parameter(typeof(TSource), "x");
var member = typeof(TSource).GetMember(property).FirstOrDefault();
Expression body = Expression.MakeMemberAccess(parameter, member);
return Expression.Lambda<Func<TSource, TKey>>(Expression.Convert(body, typeof(object)), parameter);
}
}
After that we create your custom ExtesionMethod, somethink like that:
public static class OrderByExtensionClass
{
// instead of try passing an expression, we pass our CustomKey object with the columns to sort.
// than this method create the apropriate OrderBy Expression statement
public static IOrderedQueryable<TSource> OrderBy<TSource>(this IQueryable<TSource> source, CustomKey customKey)
{
// the parameter isFirst is just to control where we are to build the expression
customKey.ForEachProperty<TSource, object>((expression, isFirst) =>
{
if (isFirst)
source = source.OrderBy(expression);
else
source = ((IOrderedQueryable<TSource>)source).ThenBy(expression);
});
return ((IOrderedQueryable<TSource>)source);
}
}
After that we just do:
CustomKey custom = new CustomKey("Name", "Age");
myEntityContext.People.OrderBy(custom).ToList()
I hope it can help you.
Part of the problem, I think, is that OrderBy wouldn't know what to do with a complex type. SQL Server knows how to order by primitive types, but that's about it. You would have to do something like ...OrderBy(x=>x.Field1).ThenBy(x=>x.Field2). You could write an extension method that takes the key, extracts the property names from the key, and builds the .OrderBy().ThenBy() expression, as long as you know what the key will be before executing the query. Otherwise yeah, you may have to materialize the results before ordering.

Use reflection and lambda expression with linq

I need to filter a collection of items by verifing the value of a flag named deletion_date
public List<T> GetAll()
{
if (context == null) context = new ajtdevEntities();
return context.Set<T>().Where(p => p.GetType().GetProperty("deletion_date") == null).ToList();
}
I get an exception when I used this generic method
LINQ to Entities does not recognize the method ' System.Reflection.PropertyInfo GetProperty ( System.String )' , and the latter can not be translated into term store.
How can I fix this method?
Instead of reflection, you can build the filter expression manually using System.Linq.Expressions like this:
public List<T> GetAll<T>()
{
var parameter = Expression.Parameter(typeof(T), "p");
var predicate = Expression.Lambda<Func<T, bool>>(
Expression.Equal(Expression.PropertyOrField(parameter, "deletion_date"), Expression.Constant(null)),
parameter);
if (context == null) context = new ajtdevEntities();
return context.Set<T>().Where(predicate).ToList();
}
Note that the above will throw exception if your type does not have property/field called "deletion_date" or the type of the property does not support null. But the same can be said for your reflection based implementation (if it worked).
An ORM will inspect the lambda and convert its parts to SQL. The Entity Framework team chose not to support reflection, and rightfully so. So it can't translate GetProperty() calls to SQL, hence the error you get.
It wouldn't work anyway, because GetProperty() gets a PropertyInfo instance, not the value. So if it were null, that would indicate that the type of p has no property named deletion_date.
The proper way would be to call GetValue() on the PropertyInfo (note that this will throw a NullReferenceException if there is no property named thusly):
p => p.GetType().GetProperty("deletion_date").GetValue(p)
But again, reflection is not supported in Entity Framework, so you want to use interfaces:
public interface IDeletable
{
DateTime? deletion_date { get; set; }
}
And apply that to your class or method as a generic constraint. Then you can use it in your lambda:
public class WhateverClass<T>
where T : IDeletable
{
public List<T> GetAll()
{
return context.Set<T>().Where(p => p.deletion_date == null).ToList();
}
}

Nhibernate Linq Get<T> object by name not by Id

public T FindById(object id)
{
return session.Get<T>(id);
}
I want to get object by Name
Something like this, but I am not able to write a query with type T
session.Get<T>().Where(x => x.something == something).SingleOrDefault();
or is there any alternative to this approach?
You can use Expression trees and build something like:
protected T Get<T, TValue>(string propertyName, TValue value) where T : class
{
var par = System.Linq.Expressions.Expression.Parameter(typeof(T), "x");
var eq = System.Linq.Expressions.Expression.Equal(System.Linq.Expressions.Expression.Property(par, propertyName), System.Linq.Expressions.Expression.Constant(value));
var lambda = System.Linq.Expressions.Expression.Lambda<Func<T, bool>>(eq, par);
return session.QueryOver<T>().Where(lambda).SingleOrDefault();
}
Note that I'm using the full namespace + classname because NHibernate has another Expression class.
There is a native solution in NHiberante:
Chapter 15. Criteria Queries
This Criteria API (which is there from the beginning) is really what we need here. For example the QueryOver API - is built on top of that.
The biggest benefit is, it does work natively with "strings". This would be very simple, easy to maintain piece of code:
public T GetByProperty<T>(string propertyName, object value)
where T: class
{
var session = ... // get a session
return session.CreateCriteria<T>()
.Add(Restrictions.Eq(propertyName, value))
.SetMaxResults(1)
.List<T>()
.FirstOrDefault();
}
There is a complete doc
You have to restrict T to something with a particular property. You could, for instance, create a superclass called NamedObject which has a property Name and make all nameable objects inherit from it. Restricting T to a NameableObject or descendant would then allow you to access the Name property in your query.
Not particularly elegant but it should work.
I think you should use reflection for query a generic type, just like this:
session.Get<T>().Where(P => typeof(P).GetPropery("PropertyName").GetValue(P).ToString() == "Something").SingleOrDefault();
In this code, typeof(oObject) returns de a Type intance of the object, then the GetProperty("PropertyName") method returns you a System.Reflection.PropertyInfo with that you can get the property value with GetValue(P) method that receive an instance of the object that contain the property in this case is [P] create in Lamba expression.
Im not sure what you mean by "I am not able to write a query with type T".
But you can call with expression where refactor is supported.
public T GetByExpression<T>(Expression<Func<T, bool>> restriction) where T : class
{
return Session.QueryOver<T>().Where(restriction).SingleOrDefault();
}

Converting Expression to Func<Type, bool> in c#?

I'm working on a service base application. At the service side, I have to convert Expression to Func<TypeOfEntity,bool> for using in an EntityFramework query.
Type typeofEntity;// we just have type of entity and I could get it from entity name and assembly name
//ExpressionSerializer is in Serialize.Linq
ExpressionSerializer expressionSerializer = new ExpressionSerializer(new JsonSerializer());
Expression expr = expressionSerializer.DeserializeText(stringFromClient);//It's Ok until here
Func<?,bool> func = ?//How can I create Func of typeofEntity and bool
var result = Entities.Something.Where(func);
How can we convert Expression (not Expression<Func<T,bool>>) to Func<T,bool>?
You can use the dynamic type to have T resolved at runtime. A class such as this
static class ExpressionRunner
{
public static IList<T> Run<T>(
Context context,
Expression<Func<T, bool>> expression)
where T : class
{
var result = context
.Set<T>()
.Where(expression);
return result.ToList();
}
}
Can run the Expression against the Context
Expression expr = expressionSerializer.DeserializeText(stringFromClient);
var result = ExpressionRunner.Run(Entities, expr as dynamic);
Note that the return type is also dynamic so it would be advisable to have this Run method at the top of the call chain to maximise performance - i.e. Run should return void and all the processing should be nested inside of Run.

Accessing properties through Generic type parameter

I'm trying to create a generic repository for my models. Currently i've 3 different models which have no relationship between them. (Contacts, Notes, Reminders).
class Repository<T> where T:class
{
public IQueryable<T> SearchExact(string keyword)
{
//Is there a way i can make the below line generic
//return db.ContactModels.Where(i => i.Name == keyword)
//I also tried db.GetTable<T>().Where(i => i.Name == keyword)
//But the variable i doesn't have the Name property since it would know it only in the runtime
//db also has a method ITable GetTable(Type modelType) but don't think if that would help me
}
}
In MainViewModel, I call the Search method like this:
Repository<ContactModel> _contactRepository = new Repository<ContactModel>();
public void Search(string keyword)
{
var filteredList = _contactRepository.SearchExact(keyword).ToList();
}
Solution:
Finally went with Ray's Dynamic Expression solution:
public IQueryable<TModel> SearchExact(string searchKeyword, string columnName)
{
ParameterExpression param = Expression.Parameter(typeof(TModel), "i");
Expression left = Expression.Property(param, typeof(TModel).GetProperty(columnName));
Expression right = Expression.Constant(searchKeyword);
Expression expr = Expression.Equal(left, right);
}
query = db.GetTable<TModel>().Where(Expression.Lambda<Func<TModel, bool>>(expr, param));
Interface solution
If you can add an interface to your object you can use that. For example you could define:
public interface IName
{
string Name { get; }
}
Then your repository could be declared as:
class Repository<T> where T:class, IName
{
public IQueryable<T> SearchExact(string keyword)
{
return db.GetTable<T>().Where(i => i.Name == keyword);
}
}
Alternate interface solution
Alternatively you could put the "where" on your SearchExact method by using a second generic parameter:
class Repository<T> where T:class
{
public IQueryable<T> SearchExact<U>(string keyword) where U: T,IName
{
return db.GetTable<U>().Where(i => i.Name == keyword);
}
}
This allows the Repository class to be used with objects that don't implement IName, whereas the SearchExact method can only be used with objects that implement IName.
Reflection solution
If you can't add an IName-like interface to your objects, you can use reflection instead:
class Repository<T> where T:class
{
static PropertyInfo _nameProperty = typeof(T).GetProperty("Name");
public IQueryable<T> SearchExact(string keyword)
{
return db.GetTable<T>().Where(i => (string)_nameProperty.GetValue(i) == keyword);
}
}
This is slower than using an interface, but sometimes it is the only way.
More notes on interface solution and why you might use it
In your comment you mention that you can't use an interface but don't explain why. You say "Nothing in common is present in the three models. So i think making an interface out of them is not possible." From your question I understood that all three models have a "Name" property. In that case, it is possible to implement an interface on all three. Just implement the interface as shown and ", IName" to each of your three class definitions. This will give you the best performance for both local queries and SQL generation.
Even if the properties in question are not all called "Name", you can still use the nterface solution by adding a "Name" property to each and having its getter and setter access the other property.
Expression solution
If the IName solution won't work and you need the SQL conversion to work, you can do this by building your LINQ query using Expressions. This more work and is significantly less efficient for local use but will convert to SQL well. The code would be something like this:
class Repository<T> where T:Class
{
public IQueryable<T> SearchExact(string keyword,
Expression<Func<T,string>> getNameExpression)
{
var param = Expression.Parameter(typeof(T), "i");
return db.GetTable<T>().Where(
Expression.Lambda<Func<T,bool>>(
Expression.Equal(
Expression.Invoke(
Expression.Constant(getNameExpression),
param),
Expression.Constant(keyword),
param));
}
}
and it would be called thusly:
repository.SearchExact("Text To Find", i => i.Name)
Ray's method is quite good, and if you have the ability to add an interface definitely the superior however if for some reason you are unable to add an interface to these classes (Part of a class library you can't edit or something) then you could also consider passing a Func in which could tell it how to get the name.
EG:
class Repository<T>
{
public IQueryable<T> SearchExact(string keyword, Func<T, string> getSearchField)
{
return db.GetTable<T>().Where(i => getSearchField(i) == keyword);
}
}
You'd then have to call it as:
var filteredList = _contactRepository.SearchExact(keyword, cr => cr.Name).ToList();
Other than these two options you could always look into using reflection to access the Name property without any interface, but this has the downside that there's no compile-time check that makes sure the classes you're passing actually DO have a Name property and also has the side-effect that the LINQ will not be translated to SQL and the filtering will happen in .NET (Meaning the SQL server could get hit more than is needed).
You could also use a Dynamic LINQ query to achieve this SQL-side effect, but it has the same non type-safe issues listed above.

Categories