Add condition to linq query
I'm building a repository layer for nhibernate, in which I have the following find method:
public IList<TEntity> Find(Expression<Func<TEntity, bool>> query)
I would like to add a condition to the query in that method. The extra condition should limit rows to all where RemovedAt is larger than DateTime.Now.
Explanation using SQL:
Let's assume that I have the following SQL query:
开发者_如何学GoSELECT * FROM users WHERE first_name LIKE 'a%' OR last_name LIKE 'a%'
It would look like this after the modification:
SELECT * FROM users WHERE (first_name LIKE 'a%' OR last_name LIKE 'a%') AND created_at > '2010-09-22 19:31'
Can it be done on the linq query, or in nhibernate?
Edit
Sorry, forgot to mention that all entities do not have a RemovedAt method. The interface is declared as:
public interface IRepository<TEntity, TKey> where TEntity : class, new()
I'm changing CreatedAt/RemovedAt/UpdatedAt (in their respective methods) by looking for a property with that name and using reflection to update the value.
First off, I am not sure why you just don't return IQueryable and let the user of the method determine if they want it as a List, etc. Because IQueryables are not executed until actually needed, you can keep adding to the expression tree until you actually need it.
In this case, when you change it to a List of objects, that is when the query will actually get executed against the databse when it is turned into a list.
If you really want to keep the interface as an IList instead of an IQueryable, just add an additional expression to the expression tree before you compile.
Since I have had limited work with the root level expression trees, I would likely give the syntax to you wrong, so here is a different type of example that may give you enough information as to what I am talking about:
var query = something.Where( n => n.FirstName.StartsWith("N")) ;
query = query.Where(n => n.created_at > DateTime.Now);
return query.ToList();
I hope that makes sense. You can just continue to add criteria to the expression tree until it gets compiled and executed.
I would still recommend passing arround IQueryable though, instead. In my example, you would just return the query instead of calling ToList first. It makes it easier for fluent syntax and will perform better with nHibernate because nHibernate will try to take all of the criteria into account before calling the database. If you only needed an aggragate or a count, for example, that would be handled at the database instead of pulling back all of the rows into a list and then iterating over it to get the aggregate or count.
For those using a Business Logic Layer on top of their repository along with a tool like AutoMapper to map between data transfer objects and Entity models, use Predicate Builder from LinqKit to allow your MVC/API Controllers to add additional System.Linq.Expressions.Expression
filters to the default filters you set on your Business Components.
Using a predicate builder will allow you to dynamically modify your IQueryable
before sending it to AutoMapper for flattening i.e. bringing the list into memory.
精彩评论