I'm implementing the service \ repository pattern in a new project. I've got a base interface that looks like this. Everything works great until I need to use the GetMany method. I'm just not sure how to pass a LINQ expression into the GetMany method. For example how would I simply sort a list of objects of type name?
nameRepository.GetMany( ? )
public interface IRepository<T> where T : class
{
void Add(T entity);
void Update(T entity);
void Delete(T entity);
void Delete(Expression<Func<T, bool>> where);
T GetById(long Id);
T GetById(string Id);
T Get(Expression<Func<T, bool>> where);
IEnumerable<T> GetAll();
IEnumerable<T> GetMany(Expression<Func<T, bool>> where);
}
public virtual IEnumerable<T> GetMany(Expression<Func<T, bool>> where)
{
return dbset.Where(where).ToList();
}
Assuming that you had an implementation of IRepository<MyClass>, you would make a call to GetMany like so:
IRepository<MyClass> repository = ...;
IEnumerable<MyClass> filtered = repository.GetMany(
mc => true);
Note the mc => true lambda expression. mc in this case is the parameter (in this case, of type MyClass) passed to the Expression<Func<T, bool>> which will be evaluated (one would assume through an IQueryable<T>) and the mc.SomeProperty == someValue is an expression which returns true, which is the second type parameter of the Expression<Func<T, bool>>.
Once you have the filtered, you can use the order by clause (or the OrderBy extension method, they're the same) to sort the results, like so:
var filteredAndOrdered = filtered.OrderBy(mc => mc.MyProperty);
Note that GetMany returns an IEnumerable<T>, not an IQueryable<T>; this is important if your result set is large, as the ordering will happen on the client, and not be sent to the server.
For an OrderBy operation, this can be expensive because it has to go through the entire sequence (to order) before returning the first result.
Related
While somewhat powerful, the System.Linq.Dynamic library has a surprising lack of documentation, especially in regards to what conventions must be followed for more complex queries.
In a query I'm working on, it contains a FirstOrDefault call, but I can't seem to get it to work.
Here's the whole (unworking) expression:
"Locations.FirstOrDefault(x => x.IsPrimaryLocation).Address1 as Address"
Can I write this FirstOrDefault expression to work with Dynamic linq?
What is the correct way to write this expression?
Extending the dynamic library is certainly an option as already suggested.
an alternative given the Where in Dynamic Linq returns an Iqueryable
public static class DynamicQueryable {
public static IQueryable<T> Where<T>(this IQueryable<T> source, string predicate, params object[] values) { return (IQueryable<T>) Where((IQueryable) source, predicate, values); }
public static IQueryable Where(this IQueryable source, string predicate, params object[] values) {
using a DYnamic Object for the context or repository "locations".
Then use a where which could contain dynamic string predicate and follow with firstOrDefault.
(catch or test for null not considered)
DynamicLocations.Where(x => x.IsPrimaryLocation).FirstOrDefault( ).Address1 as Address;
or dynamic where if needed
DynamicLocations.Where("IsPrimaryLocation",new string[]).FirstOrDefault( ).Address1 as Address;
Details:
You can expose on a method on a generic repository Class which you instantiate as a dynamic
public virtual IQueryable<TPoco> DynamicWhere(string predicate, params object[] values) {
return AllQ().Where(predicate, values);
}
Dynamic Generic Repository instantiation Sample
public class RepositoryFactory<TPoco> where TPoco : BaseObject,new() {
public IRepositoryBase<TPoco> GetRepository(DbContext context) {
// get the Pocotype for generic repository instantiation
var pocoTypes = new[] {typeof (TPoco)}; // but supports <T,U>
Type repBaseType = typeof (RepositoryBase<>);
IRepositoryBase<TPoco> repository = InstantiateRepository(context, repBaseType, pocoTypes);
return repository;
}
private IRepositoryBase<TPoco> InstantiateRepository(DbContext context, Type repType, params Type[] args) {
Type repGenericType = repType.MakeGenericType(args);
object repInstance = Activator.CreateInstance(repGenericType, context);
return (IRepositoryBase<TPoco>)repInstance;
}
}
I am trying to apply additional server-side filtering to entities exposed by an asp.net OData service, using Linq to Entities.
So far so good, but the Linq query that I build for the filter can sometimes get to an enormous length, causing a StackOverflowException.
As a workaround, I want to execute the query in a separate thread, giving that thread a bigger stack size limit. To achieve this, I implemented an IQueryable and IQueryProvider wrappers, so that when the IQueryable's Execute method is called, it would be executed in a separate thread.
To keep it simple and focused on my problem, I will omit the threading part in the following code samples, as this is not the issue here.
Here's the relevant parts of the code:
public class MyQueryable<T> : IQueryable<T> {
private IQueryable<T> _Inner { get; set; }
private IQueryProvider _Provider { get; set; }
public MyQueryable(IQueryable<T> inner) {
_Inner = inner;
_Provider = new MyQueryProvider(inner.Provider);
}
public Type ElementType {
get { return _Inner.ElementType; }
}
public Expression Expression {
get { return _Inner.Expression; }
}
public IQueryProvider {
get { return _Provider; }
}
/* ... Implementations of GetEnumerator ... */
}
public class MyQueryProvider : IQueryProvider {
private IQueryProvider _Inner { get; set; }
public MyQueryProvider(IQueryProvider inner) {
_Inner = inner;
}
public IQueryable<T> CreateQuery<T>(Expression expression) {
return new MyQueryable<T>(_Inner.CreateQuery<T>(expression));
}
public T Execute<T>(Expression expression) {
// The problem occurs on the following line.
return _Inner.Execute<T>(expression);
}
/* ... Implementation of the non-generic versions of CreateQuery, Execute ... */
}
When I run this, I get an InvalidOperationException in MyQueryProvider.Execute method:
Cannot compare elements of type 'System.Collections.Generic.ICollection`1'.
Only primitive types (such as Int32, String, and Guid) and entity types are supported.
This is how I build the original IQueryable that is being passed to MyQueryable:
I get the DbSet from my entities context, apply OData filter query option on it, and call Count(). Something along the lines of:
((odataQueryOptions.Filter.ApplyTo(
context.Entities.AsQueryable(), new ODataQuerySettings()))
as IQueryable<Entity>).Count()
I partially identified that the problem is that the odata filter query option contains a nested 'any' filter, something like:
Entities/$filter=RelatedEntities/any(entity: (entity/ID eq 1))
which adds a check to the IQueryable's Where expression whether the RelatedEntities collection is null. This check is suppostedly what causes the above exception.
What I cannot understand, is why the original IQueryable fails when I try to delegate the Execute method's execution to it from the MyQueryable.Execute method, but it all works fine when I use the original IQueryable directly.
Any help on this would be highly appreciated. Thanks in advance.
I'm trying to do some eager loading on an EF Entity.
so, if the entity is called Orders .. then I guess i would do the following...
_someContext.Orders.Include("Whatever") ....
But the problem is, I have a method like the following ...
public IQueryable<Order> Find(Expression<Func<Order, bool>> predicate)
{
return CurrentContext.Orders.Where(predicate);
}
which works great .. but can i leverage the Expression predicate to include the Include("whatever") in there, instead of having to add another method parameter?
I don't think so. Since the predicate and ObjectQuery.Where Method in genral has nothing to do with eager loading by Include. You can create a extension method though, but that does not save you from having yet another parameter for specifying the include:
public IQueryable<Order> Find(Expression<Func> predicate, string include) {
return CurrentContext.Orders.Where(predicate, include);
}
public static ObjectQuery<T> Where<T>(this ObjectQuery<T> entity, Expression<Func<T, bool>> predicate, string include) {
return (ObjectQuery<T>)entity.Include(include).Where<T>(predicate);
}
I'm with an irritating problem. It might be something stupid, but I couldn't find out.
I'm using Linq to NHibernate, and I would like to count how many items are there in a repository. Here is a very simplified definition of my repository, with the code that matters:
public class Repository {
private ISession session;
/* ... */
public virtual IQueryable<Product> GetAll() {
return session.Linq<Product>();
}
}
All the relevant code in the end of the question.
Then, to count the items on my repository, I do something like:
var total = productRepository.GetAll().Count();
The problem is that total is 0. Always. However there are items in the repository. Furthermore, I can .Get(id) any of them.
My NHibernate log shows that the following query was executed:
SELECT count(*) as y0_ FROM [Product] this_ WHERE not (1=1)
That must be that "WHERE not (1=1)" clause the cause of this problem.
What can I do to be able .Count() the items in my repository?
Thanks!
EDIT: Actually the repository.GetAll() code is a little bit different... and that might change something! It is actually a generic repository for Entities. Some of the entities implement also the ILogicalDeletable interface (it contains a single bool property "IsDeleted"). Just before the "return" inside the GetAll() method I check if if the Entity I'm querying implements ILogicalDeletable.
public interface IRepository<TEntity, TId> where TEntity : Entity<TEntity, TId> {
IQueryable<TEntity> GetAll();
...
}
public abstract class Repository<TEntity, TId> : IRepository<TEntity, TId>
where TEntity : Entity<TEntity, TId>
{
public virtual IQueryable<TEntity> GetAll()
{
if (typeof (ILogicalDeletable).IsAssignableFrom(typeof (TEntity)))
{
return session.Linq<TEntity>()
.Where(x => (x as ILogicalDeletable).IsDeleted == false);
}
else
{
return session.Linq<TEntity>();
}
}
}
public interface ILogicalDeletable {
bool IsDeleted {get; set;}
}
public Product : Entity<Product, int>, ILogicalDeletable
{ ... }
public IProductRepository : IRepository<Product, int> {}
public ProductRepository : Repository<Product, int>, IProductRepository {}
Edit 2: actually the .GetAll() is always returning an empty result-set for entities that implement the ILogicalDeletable interface (ie, it ALWAYS add a WHERE NOT (1=1) clause.
I think Linq to NHibernate does not like the typecast.
It looks like you've got a soft delete model going on here, and you're trying to filter these out from being returned by the GetAll() method. I agree with your analysis that NHibernate.Linq doesn't properly process the typecast, but you may want to try replacing this with a query filter.
public virtual IQueryable<TEntity> GetAll()
{
if (typeof(ILogicalDeletable).IsAssignableFrom(typeof(TEntity)))
{
return session.Linq<TEntity>().OfType<ILogicalDeletable>()
.Where(x => !x.IsDeleted).Cast<TEntity>();
}
return session.Linq<TEntity>();
}
Try that.
In my code I'd like to make my repositories IQueryable. This way, the criteria for selection will be a linq expression tree.
Now if I want to mock my repository in theorie this is very easy : just implement the interface of my repository (which is also a IQueryable object).
My mock repository implementation would be only a in memory collection, but my question is : Do you know an easy way to implement the IQueryable interface of my mock, to transfer the query to my in-memory collection (IEnumerable) ?
Thanks for your response,
Some precision
The client object of my repository will use my repository this way :
var result = from entry in MyRepository where entry.Product == "SomeProduct" select entry;
What does ToList or AsEnumerable is to execute the query and return the result as a List or as a IEnumerable. But I have to implement the IQueryable interface on my repository, with a IQueryProvider which transform the expression in a call to a IEnumerable object.
Solution
The implementation of the solution is delegating call to IQueryable to my inmemory collection with AsQueryable.
public class MockRepository : IQueryable<DomainObject>
{
private List<DomainObject> inMemoryList = new List<DomainObject>();
#region IEnumerable<DomainObject> Members
public IEnumerator<DomainObject> GetEnumerator()
{
return inMemoryList.GetEnumerator();
}
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return inMemoryList.GetEnumerator();
}
#endregion
#region IQueryable Members
public Type ElementType
{
get
{
return inMemoryList.AsQueryable().ElementType;
}
}
public Expression Expression
{
get
{
return inMemoryList.AsQueryable().Expression;
}
}
public IQueryProvider Provider
{
get
{
return inMemoryList.AsQueryable().Provider;
}
}
#endregion
}
Use AsQueryable on your mocks. Now they're queryable and you can treat them like any other queryable.
Can you use the extension method
.ToList<>