I have a situation where I need to dynamically build up a list of filters to apply to a list of objects. Those objects can be anything that implements an interface which contains all of the properties I need to filter on.
public interface IStuff
{
bool SuitableForSomething { get; set; }
bool SuitableForSomethingElse { get; set; }
}
public class SomeStuff : IStuff
{
...
}
public class SomeOtherStuff : IStuff
{
...
}
I have a list of criteria defined like so ...
public List<Expression<Func<IStuff, bool>>> Criteria { get; private set; }
and add criteria like so ...
Criteria.Add(x => x.SuitableForSomething);
Criteria.Add(x => x.SuitableForSomethingElse);
I then apply the criteria to my query like so ...
var stuff= _stuffCache
.GetAll()
.AsQueryable()
.ApplyCriteria(Criteria);
which uses the following extension method ...
public static IQueryable<T> ApplyCriteria<T>(this IQueryable<T> stuff, List<Expression<Func<IStuff, bool>>> criteria)
where T : IStuff
{
foreach (var expression in criteria)
{
stuff = Queryable.Where(stuff, expression);
}
return stuff;
}
The compiler is telling me ...
cannot convert from
'System.Linq.Expressions.Expression<System.Func<IStuff,bool>>'
to
'System.Linq.Expressions.Expression<System.Func<T,int,bool>>'
When I hover over the red line under the error in the IDE it's saying it cannot resolve method between
IQueryable<IStuff> Where<IStuff>(this IQueryable<IStuff>, Expression<Func<IStuff, bool>>) in class Queryable
and
IQueryable<T> Where<T>(this IQueryable<T>, Expression<Func<T,int,bool>>) in class Queryable
If I try casting the expression to Expression<Func<T, bool>>, which ought to work as T is constrained to implement the IStuff interface. I get
Cannot cast expression of type 'Expression<Func<IStuff, bool>>' to type 'Expression<Func<T, bool>>'
EDIT
Thanks to Raphaƫl's answer I fixed the extension method and eventually found the real problem I had, which was a casting problem when I was calling the code. Easily fixed by adding a .Cast<SomeStuff>() after the ApplyCriteria call.
Before
var stuff= _stuffCache
.GetAll()
.AsQueryable()
.ApplyCriteria(Criteria);
After
var stuff= _stuffCache
.GetAll()
.AsQueryable()
.ApplyCriteria(Criteria)
.Cast<SomeStuff>();
change second parameter type to List<Expression<Func<T,bool>>> (T instead of IStuff)
public static IQueryable<T> ApplyCriteria<T>(this IQueryable<T> stuff, List<Expression<Func<T, bool>>> criteria)
where T : IStuff
{
foreach (var expression in criteria)
{
stuff = Queryable.Where(stuff, expression);
//or stuff = stuff.Where(expression), as Where is an Extension method;
}
return stuff;
}
and your method can be (thx resharper), rewritten to
public static IQueryable<T> ApplyCriteria<T>(this IQueryable<T> stuff, List<Expression<Func<T, bool>>> criteria)
where T : IStuff
{
return criteria.Aggregate(stuff, (current, expression) => current.Where(expression));
}
Related
i have a generic method like this
public IList<T> Get(KendoGridFilterSort.FilterContainer filter)
{
List<T> list = null;
IQueryable<T> dbQuery = _entities.Set<T>();
list = dbQuery
.ToList<T>();
return list;
}
when in inititate the class of this method with specific object i got the full list of records.
but i want to pass some filter thorough parameters in this method to filter records accordingly . for this i have class name KendoGridFilterSort.FilterContainer that contain all the filter with field name, operator and values
i want to use that filters with this generic method , my problem is i cannot use
this statement for example
persons.Where(p => p.Name == name && p.Surname == surname).ToList();
so is there any possibility that i could apply my filter on this generic list ?
You should force your T class implement IEntity interface with properties, that you want to filter:
public interface IEntity
{
string Name {get;set;}
string Surname {get;set;}
}
public class Repository<T> where T : class, IEntity
{
public IList<T> Get(KendoGridFilterSort.FilterContainer filter)
{
return _entities.Set<T>().Where(x => x.Name == filter.Name && x.Surname == filter.Surname).ToList();
}
}
You could pass in a filter as an Expression tree as you're using IQueryable<T>
public IList<T> Get(Expression<Func<T, bool>> filter)
{
List<T> list = null;
IQueryable<T> dbQuery = _entities.Set<T>();
dbQuery = dbQuery.Where(filter);
list = dbQuery.ToList<T>();
return list;
}
Although its unclear what KendoGridFilterSort.FilterContainer is doing
I'm trying to implement a custom LinQ Count() method. Basically what I'm trying to achieve here is before calling the Count method, I want to filter out all elements that have the property IsDeleted set to true. So, I created an extension class and I added these methods:
public static int Count2<T>(this IEnumerable<T> source, Func<T, bool> selector)
where T : Model
{
return source.Where(x => !x.IsDeleted).Count(selector);
}
public static int Count2<T>(this IQueryable<T> source, Expression<Func<T, bool>> selector)
where T : Model
{
return source.Where(x => !x.IsDeleted).Count(selector);
}
public static int Count2<T>(this IEnumerable<T> source)
where T : Model
{
return source.Count(x => !x.IsDeleted);
}
public static int Count2<T>(this IQueryable<T> source)
where T : Model
{
return source.Count(x => !x.IsDeleted);
}
This works just find for local collections, but when executing this command for instance:
ListOfModels.Sum(x => x.PropertyThatIsAList.Count2())
and ListOfModels is an instance of IQueryable, i.e. it has to be executed in the database, it gives me this error:
The LINQ expression 'Sum()' could not be translated and will be evaluated locally.
I looked around on the web and I saw some answers saying I have to implement the IQueryableProvider but I think there is no need to go into such complicated path since the Sum() and Count() are translatable, I only need to count conditionally. Is it possible, and if it is, can anyone give me a clue on how to do it?
I suggest you instead of customizing all LinQ methods use an extended method like Validate():
public static IEnumerable<T> Validate<T>(this IEnumerable<T> list) where T: IDeleteable
{
return list.Where(w => !w.IsDeleted);
}
That IDeleteable interface is like this:
public interface IDeleteable
{
bool IsDeleted { get; set; }
}
Then use it before other methods.
i want to create a method that can be used with lambda in that way:
return Method<MyClass>(x => x.PropName1, x.PropName2,...);
inside it i have to use tha propName to eager load those reference field via nhibernate:
return session.Query<MyClass>()
.Fetch(c => c.PropName1)
.Fetch(c => c.PropName2).ToList();
i look into linq source code to find some similar and went here:
public static void ListEager<TEntity>(IEnumerable<Func<TEntity, TKey>> fields)
but it's simply not correct.
how can it be done?
You can do like this, implement IGeneric interface and Generic class, with generic method GetList, i use this generic method and working very well.
public interface IGenericDataRepository<T> where T : class
{
IList<T> GetList(Func<T, bool> where, params Expression<Func<T, object>>[] navigationProperties);
}
public class GenericDataRepository<T> : IGenericDataRepository<T> where T : class
{
public virtual IList<T> GetList(Func<T, bool> where,
params Expression<Func<T, object>>[] navigationProperties)
{
List<T> list;
using (var dbQuery = new session.Query<T>())
{
//Apply eager loading
foreach (Expression<Func<T, object>> navigationProperty in navigationProperties)
dbQuery = dbQuery.Fetch<T, object>(navigationProperty);
list = dbQuery
.AsNoTracking()
.Where(where)
.ToList<T>();
}
return list;
}
}
To use it you need create repository class for any entity, here is example with my ProductRepository class
public interface IProductRepository:IGenericDataRepository<Product>
{
////
}
public class ProductRepository:GenericDataRepository<Product>,IProductRepository
{
////
}
i switch to queryover to get more power :D
public IEnumerable<TEntity> List(params Expression<Func<TEntity, object>>[] eagerFields)
{
var query = _session.QueryOver<TEntity>();
query = AddReferenceFetch(query, eagerFields);
return query.TransformUsing(Transformers.DistinctRootEntity).List();
}
private IQueryOver<TEntity, TEntity> AddReferenceFetch(IQueryOver<TEntity, TEntity> query, params Expression<Func<TEntity, object>>[] eagerFields)
{
foreach (Expression<Func<TEntity, object>> field in eagerFields)
query = query.Fetch(field).Eager;
return query;
}
in this way i can manage reference or hasmany without problem
i left #mww as accepted answer because the main idea is his
I'm using EntityFramework and LinqKit to build expressions trees which get translated into SQL. Also we use the specification pattern to organize our queries.
There is a need in almost all of our domain objects to execute a query by description, but in some of those classes the property is called "Name", in others "Description", etc.
So initially was defined a interface as follow:
public interface IDescritible
{
string Description { get; }
}
The problem appeared when I tried to use it implemented explicitly in a class and made a generic query upon that:
public class Foo : IDescritible
{
public string Name { get; set; }
string IDescritible.Description
{
get { return this.Name; }
}
}
public class DescriptionMatch<T> : AbstractSpecification<T>
where T : IDescritible
{
public string Query { get; set; }
public DescriptionMatch(string query)
{
this.Query = query;
}
public override Expression<Func<T, bool>> IsSatisfiedBy()
{
return x => x.Description.Contains(Query);
}
}
When run, the EntityFramework is unable to translate the property get (a method call) into a SQL expression.
Then I tried to define a Expression in the class as follows:
public interface IDescritible<T> where T: IDescritible<T>
{
Expression<Func<T, string>> DescriptionExpression { get; }
}
public class Foo : IDescritible<Foo>
{
public string Name { get; set; }
public Expression<Func<Foo, string>> DescriptionExpression
{
get { return x => x.Name; }
}
}
public class DescriptionMatch<T> : AbstractSpecification<T> where T : IDescritible<T>
{
public string Query { get; set; }
public DescriptionMatch(string query)
{
this.Query = query;
}
public override Expression<Func<T, bool>> IsSatisfiedBy()
{
Expression<Func<T, bool>> combinedExpression = x => x.DescriptionExpression.Invoke(x).Contains(this.Query);
return combinedExpression.Expand();
}
}
But when it executes the .Expand() a exception is thrown: Unable to cast object of type 'System.Linq.Expressions.PropertyExpression' to type 'System.Linq.Expressions.LambdaExpression'.
Then I found out that LinqKit only support expanding local defined expressions (as stated on this question) and that there are some unoficial code write to solve those issues (answers on this question).
But when I modified the LinqKit library to try either one of the proposed solutions in second question it started to throw: variable 'x' of type 'Foo' referenced from scope '', but it is not defined.
Maybe that was caused by the fact that I don't have a instance of Foo yet? Only got the generic type parameter, and that's why I can't define my expression on a local variable. Maybe there is a way to define as a static expression somehow "attached" into class? But then I would not be able to use it in a generic query, there will be no interface...
There is a way to modify LinqKit so I can build expressions defined on a explicit implementation of a interface? Or there is another way to make a generic query upon properties explicitly implemented? Or any other solution or things to look into?
There is no need to use neither LinqKit or specification pattern for that, but EntityFramework is mandatory, what is important is to have a interface/any other way that support "pointing out" which property is the description property and make a generic Expression upon that.
Thank you in advance!
The easiest way to do it is to make the following change
public class DescriptionMatch<T> : AbstractSpecification<T> where T : IDescritible<T>, new()
Then you can get an instance by simply newing up a T (which EF), using the expression and otherwise discarding the object
public interface IDescritible<T> where T : IDescritible<T>
{
Expression<Func<T, string>> DescriptionExpression { get; }
}
public class Foo : IDescritible<Foo>
{
public string Name { get; set; }
public Expression<Func<Foo, string>> DescriptionExpression
{
get { return x => x.Name; }
}
}
public abstract class AbstractSpecification<T>
{
public abstract Expression<Func<T, bool>> IsSatisfiedBy();
}
public class DescriptionMatch<T> : AbstractSpecification<T>
where T : IDescritible<T>, new()
{
public string Query { get; set; }
public DescriptionMatch(string query)
{
this.Query = query;
}
public override Expression<Func<T, bool>> IsSatisfiedBy()
{
Expression<Func<T, string>> lambda = new T().DescriptionExpression;
return Expression.Lambda<Func<T, bool>>(
Expression.Call(
lambda.Body,
"Contains",
Type.EmptyTypes,
Expression.Constant(
this.Query,
typeof(string)
)
),
lambda.Parameters
);
}
}
public class ExpressionReplacer : ExpressionVisitor
{
private readonly Expression _toBeReplaced;
private readonly Expression _replacement;
public ExpressionReplacer(Expression toBeReplaced, Expression replacement)
{
if (toBeReplaced.Type != replacement.Type)
{
throw new ArgumentException();
}
this._toBeReplaced = toBeReplaced;
this._replacement = replacement;
}
public override Expression Visit(Expression node)
{
return Object.ReferenceEquals(node, this._toBeReplaced) ? this._replacement : base.Visit(node);
}
}
How can I cast from
Expression<Func<T, bool>> predicate
to
Expression<Func<SomeType, bool>> predicate
?
Couldn't find a way so far. Or at least create a new Expression<Func<SomeType, bool>> by using the first one's string representation of the predicate.
If it helps, T is limited to types implementing ISomeInterface, and SomeType implements it.
LE: further clarification
The interface is something like:
public interface ICacheable
{
List<T> GetObjects<T>(Expression<Func<T, bool>> predicate) where T : ICacheable;
}
then you have
public partial class Video : ICacheable
{
public List<T> GetObjects<T>(Expression<Func<T, bool>> predicate) where T : ICacheable
{
// implementation here that returns the actual List<Video>
// but when I try to query the dbcontext I can't pass a predicate with type T, I have to cast it somehow
List<Video> videos = db.Videos.Where(predicate).ToList(); // not working
}
}
then you have:
public class RedisCache
{
public List<T> GetList<T>(Expression<Func<T, bool>> predicate) where T : ICacheable
{
List<T> objList = // get objects from cache store here
if(objList == null)
{
List<T> objList = GetObjects<T>(predicate);
// cache the result next
}
return objList;
}
}
I use the above from any class like so:
// If the list is not found, the cache store automatically retrieves
// and caches the data based on the methods enforced by the interface
// The overall structure and logic has more to it.
List<Video> videos = redisCache.GetList<Video>(v => v.Title.Contains("some text"));
List<Image> images = redisCache.GetList<Image>(v => v.Title.Contains("another text"));
And I would extend this to any type of object I need to be cachable, with methods that will allow the Cache store to automatically retrieve an entity or list of entities if they are not found in cache. I might be doing this completely wrong though.
I'm not up to scratch on my Entity Framework, but I know that the DatabaseContext within LINQ has a GetTable<T> which returns the table based on the generic. If "GetTable equivalent for ObjectContext" is anything to go by, it's also available in EF?
To make your statement truly generic, you could try this:
public MyBaseObject<T>
{
public List<T> GetObjects<T>(Expression<Func<T, bool>> predicate) where T : ICacheable
{
return db.CreateObjectSet<T>().Where(predicate).ToList();
}
}
public partial class Image : MyBaseObject<Image>, ICacheable
{
}
public partial class Video : MyBaseObject<Video>, ICacheable
{
}
Here is something (extremely) basic which, I hope, might help you in the process of caching using generics.
// ICacheable interface is used as a flag for cacheable classes
public interface ICacheable
{
}
// Videos and Images are ICacheable
public class Video : ICacheable
{
public String Title { get; set; }
}
public class Image : ICacheable
{
public String Title { get; set; }
}
// CacheStore will keep all objects loaded for a class,
// as well as the hashcodes of the predicates used to load these objects
public class CacheStore<T> where T : ICacheable
{
static List<T> loadedObjects = new List<T>();
static List<int> loadedPredicatesHashCodes = new List<int>();
public static List<T> GetObjects(Expression<Func<T, bool>> predicate)
{
if (loadedPredicatesHashCodes.Contains(predicate.GetHashCode<T>()))
// objects corresponding to this predicate are in the cache, filter all cached objects with predicate
return loadedObjects.Where(predicate.Compile()).ToList();
else
return null;
}
// Store objects in the cache, as well as the predicates used to load them
public static void StoreObjects(List<T> objects, Expression<Func<T, bool>> predicate)
{
var hashCode = predicate.GetHashCode<T>();
if (!loadedPredicatesHashCodes.Contains(hashCode))
{
loadedPredicatesHashCodes.Add(hashCode);
loadedObjects = loadedObjects.Union(objects).ToList();
}
}
}
// DbLoader for objets of a given class
public class DbStore<T> where T : ICacheable
{
public static List<T> GetDbObjects(Expression<Func<T, bool>> predicate)
{
return new List<T>(); // in real life, load objects from Db, with predicate
}
}
// your redis cache
public class RedisCache
{
public static List<T> GetList<T>(Expression<Func<T, bool>> predicate) where T:ICacheable
{
// try to load from cache
var objList = CacheStore<T>.GetObjects(predicate);
if(objList == null)
{
// cache does not contains objects, load from db
objList = DbStore<T>.GetDbObjects(predicate);
// store in cache
CacheStore<T>.StoreObjects(objList,predicate);
}
return objList;
}
}
// example of using cache
public class useRedisCache
{
List<Video> videos = RedisCache.GetList<Video>(v => v.Title.Contains("some text"));
List<Image> images = RedisCache.GetList<Image>(i => i.Title.Contains("another text"));
}
// utility for serializing a predicate and get a hashcode (might be useless, depending on .Equals result on two equivalent predicates)
public static class PredicateSerializer
{
public static int GetHashCode<T>(this Expression<Func<T, bool>> predicate) where T : ICacheable
{
var serializer = new XmlSerializer(typeof(Expression<Func<T, bool>>));
var strw = new StringWriter();
var sw = XmlWriter.Create(strw);
serializer.Serialize(sw, predicate);
sw.Close();
return strw.ToString().GetHashCode();
}
}