I'm looking to implement a cache so that I check to see if specific queries have been executed already, and if they have, return the data using the in memory cache. Rather than implement the cache itself, I can make use of the in memory view that EntityFramework exposes through the Local property of the DBSet.
So, I want to pass the entire query to a method that will run it against the database and/or the in memory cache. However, I'm getting my Expression/Func/IQueryable mixed up and can't see out how to pass the query/expression:
So the base class would be something like this (this obviously isn't working yet...):
public abstract class ServiceBase<TEntity> where TEntity : class
{
Entities _context; //instantiated in the constructor
protected List<TEntity> GetData<TEntity>(Expression<Func<TEntity, TEntity>> query)
{
if (!HasQueryExecuted(query))
{
_context.Set<TEntity>().Select(query).Load();
AddQueryToExecutedList(query);
}
return _context.Set<TEntity>().Local.AsQueryable().Select(query).ToList();
}
}
And I would have multiple classes that define their queries something like this:
public class CustomersService : ServiceBase<Customer>
{
public List<Customer> GetCustomersWithOrders()
{
return GetData(c => c.Where(c => c.OrderCount > 0));
}
public List<Customer> GetLargestCustomersByOrder(int TopCount)
{
return GetData(c => c.OrderBy(c=>c.OrderCount).Take(TopCount));
}
}
public class ProductsService : ServiceBase<Product>
{
public List<Customer> GetAllProducts()
{
return GetData(p => p);
}
public List<Customer> GetMostOrderedProducts(int minimumOrders)
{
return GetData(p => p.Where(p=> p.OrderCount > minimumOrders)
.OrderByDescending(p=>p.OrderCount));
}
public List<Customer> GetAllProducts()
{
return GetData(p => p);
}
}
These are just contrived examples, but the point is the queries could be varied, not just limited to a where clause but making use of all the standard extension methods on IQueryable to query the underlying data.
First of all, is this possible? Can the GetData method define a parameter that can take any query? With the above example, I declared the query parameter to accept the same type as specified for the IQueryable extension Select method, but when I try and call it with a simple Where clause or OrderBy clause, I get various compilation errors such as :
Cannot implicitly convert type 'bool' to 'Entities.Customer
or
Cannot implicitly convert type 'System.Linq.IOrderedEnumerable' to 'Entities.Customer'
And yet, it compiles just fine if I run the same query directly against the context. So what am I doing wrong?
Your issue is this Expression<Func<TEntity, TEntity>> query
Simply put you are saying that your expression is a function type that takes a TEntity and returns a TEntity. Since your class is typed with Entities.Customer it will expect a function that takes an Entities.Customer and returns an Entities.Customer.
If you look at your services classes p => p will work fine but p => p.Where() will not because Where returns an IEnumerable.
I think you should take a different approach and take advantage of lazy-loading.
public IQueryable<TEntity> GetData<TEntity>() where TEntity : class
{
return _context.Set<TEntity>();
}
public IQueryable<Customer> GetAllProducts()
{
return GetData();
}
public IQueryable<Customer> GetMostOrderedProducts(int minimumOrders)
{
return GetData().Where(p => p.OrderCount > minimumOrders)
.OrderByDescending(p=>p.OrderCount));
}
You don't need to build the query into GetData because the query can be built at any point up until it's evaluated. If you really wanted you could return List<T> from here but you shouldn't really have to.
Otherwise this should do the trick for your current code.
protected List<TEntity> GetData<TEntity>(Func<IEnumerable<TEntity>,
IEnumerable<TEntity>> query) where TEntity : class
{
if (!HasQueryExecuted(query))
{
AddQueryToExecutedList(query);
return query(_context.Set<TEntity>()).ToList();
}
return query(_context.Set<TEntity>().Local).ToList();
}
Related
I've a project with the package
EntityFramework 6.1.0
and I'm working with DB-First Model
Some model entities has been extended in this way:
public interface IVersionable{
int VersionId{get;set;}
}
public interface IEditable{
bool IsEditable{get;set;}
}
public interface IFullFeatures:IVersionable,IEditable{}
public partial EntityOne:IFullFeatures{
//This is the extension partial class for the auto-generated model class EntityOne that already has interface properties
}
public partial EntityTwo:IFullFeatures{
//This is the extension partial class for the auto-generated model class EntityTwo that already has interface properties
}
Autogenerated classes EntityOne and EntityTwo has all the properties required by IFullFeatures, and for EntityTwo auto-generated file we've this ICollection:
public virtual ICollection<EntityOne> EntityOne {get;set;}
Finally I've got the extension method:
public static class FeaturesExtensionMethod{
public static IQueryable<T> FilterEditable<T>(this IQueryable<T> source) where T:class,IEditable{
return source.Where(s=>s.IsEditable);
}
public static IQueryable<T> FilterVersion<T>(this IQueryable<T> source, int versionId) where T:class,IVersionable{
return source.Where(s=>s.VersionId==versionId);
}
public static IQueryable<T> FullFilter<T>(this IQueryable<T> source, int versionId) where T:class,IVersionable{
return source.FilterEditable().FilterVersion(versionId);
}
}
Then, when at runtime I execute this:
var everyEntitiTwo=ctx.EntityTwo.FullFilter(4).ToList();
there's no problem, it works fine and it filters... but when at runtime I execute this instead:
var test= ctx.EntityTwo.Include("EntityOne").Select(et=>et.EntityOne.AsQueryAble().FullFilter(4)).ToList()
I get this error:
LINQ to Entities does not recognize the method 'FullFilter' method, and this method cannot be translated into a store expression.
So the question is: what is wrong on my Extension methods? Why I get this error on the second case and not even in first one?
Thanks.
UPDATE
Thanks to Jon Hanna I got inspired to this alternative way to reach the same result:
I made a "proxy class" to get the filter, because the Expression < Func < entityOne,bool> > it's strongly typed, and I needed something more generic:
public static FilterProxies{
public static GetProxiedFilter<T>(int versionId, bool onlyEditable) where T: class, IFullFeatures{
Expression<Func<T,bool>> filteredExp
if(onlyEditable){
filteredExp=(iff=>iff.VersioneId==versionId&&iff.IsEditable);
}
else{
filteredExp=(iff=>iff.VersioneId==versionId);
}
return filteredExp;
}
}
Then, in usage:
var filter=FilterProxies.GetProxiedFilter<EntityOne>(4,true);
var test= ctx.EntityTwo.Include("EntityOne").Select(et=>et.EntityOne.AsQueryAble().Where(filter)).ToList()
Hoping to be helpful updating this post, thanks to Jon for inspiring me to apply this solution
ctx.EntityTwo.FullFilter(4).ToList();
This gets immediately turned into
ctx.EntityTwo.Where(s => s.IsEditable).Where(s => s.VersionId == 4).ToList();
Which of course is something Entity Framework can handle.
var test= ctx.EntityTwo.Include("EntityOne").Select(et=>et.EntityOne.AsQueryAble().FullFilter(4)).ToList()
Because the use of your queryable extensions are within the query and acting on a different type of queryable, the methods calls are part of the expression passed to Entity Framework, and it doesn't know what FullFilter() does, and chokes on that point.
EF is not able to translate the method FullFilter to SQL, as you are using LINQ to Entities. You might need to use Linq to Objects
ctx.EntityTwo.ToList() before your method so it first gets the list of your object and then execute your method using that list, by doing that you are doing LINQ To Objects
Ok I have a ton of questions asked of this nature on here but none that actually solve this problem either fully or correctly.
Lets assume I have the following code ...
public interface IHaveRoles {
ICollection<Role> Roles { get;set; }
}
public class Foo : IHaveRoles {
public ICollection<Role> Roles { get;set; }
}
public class Bar { }
... then I have method like this ...
public override IQueryable<T> GetAll()
{
return base.GetAll();
}
in to that method I want to add a check that may result in me dynamically adding a simple where clause to my IQueryable ...
if(typeof(IHaveRoles<Role>).IsAssignableFrom(typeof(T))) {
return base.GetAll()
.Where(i => i.Roles.Any(r => r.Users.Any(u => u.Id == User.Id)));
}
...
It's a simple enough where clause in it's own right and if I knew what T was at design time then this would be a non issue.
However, casting result to an IQueryable<IHaveRoles> is not an option because when i'm done appending my clause I can no longer cast it back to an IQueryable<T> as IHaveRoles is not a sub type of T
So how do we solve this problem whilst retaining the ability to return an IQueryable<T> and without illegal casts as shown in some of the answers given in other questions like these ...
Cast Entity to Implemented Interface in a Generic Method Using LINQ for Entity Framework
LINQ-to-entities casting issue
... and in a way that avoids the problem with EF not supporting not EDM primitive types ...
LINQ to Entities only supports casting EDM primitive or enumeration types with IEntity interface
EDIT: Some tested implementations ...
public override IQueryable<T> GetAll()
{
var result = base.GetAll();
if (typeof(IHaveRoles).IsAssignableFrom(typeof(T)) && !AuthInfo.SignatureIsValid)
{
// tried implementations that don't work ...
// InvalidCastException (CLR can't cast an IQueryable<IHaveRoles> to a IQueryable<T>
var queryableRoleSecured = ((IQueryable<IHaveRoles>)result);
result = (IQueryable<T>)queryableRoleSecured
.Where(i => i.Roles.Any(r => User.Roles.Contains(r)));
// NotSupportedException (EF won't accept this kind of casting)
result = result
.Where(i => ((IHaveRoles)i).Roles.Any(r => r.Users.Any(u => u.Id == User.Id)));
}
return result;
}
Wow, I never seem to get that dynamic linq has seemingly no limits but here we are again.
Here's what I came up with ...
public override IQueryable<T> GetAll()
{
var result = base.GetAll();
if (typeof(IHaveRoles).IsAssignableFrom(typeof(T)) && !AuthInfo.SignatureIsValid)
result = result.Where("Roles.Any(Users.Any(Id == #0))", User.Id);
return result;
}
because dynamic is evaluated at runtime the same design time rules don't apply, meaning I can apply the exact same linq I wanted (with a little bit of an overhead cost of course).
I have the following:
public IQueryable<T> GetQueryable()
{
var results = _repository.Table;
if (typeof(IStoreScopedEntity).IsAssignableFrom(typeof(T)))
{
results = results.Where(e => ((IStoreScopedEntity)e).Stores.Select(s => s.Id).Contains(EngineContext.Current.StoreScopeId));
}
return results;
}
and am getting the error in the post title. I know I could call ToList() on the entities so they are retrieved from the database and then cast but I'd like to avoid bring back data from the database when I don't need it.
Is there any way I can get this to work without loading the entire list of items from the database into memory and then selecting?
A bit tricky, but doable.
First we need a helper generic constrained function. Since from your definition looks like the GetQueryable function is part of a generic class, let put the helper function in a separate class
public static class StoreScopedEntity
{
public static Expression<Func<T, bool>> IdPredicate<T>(int id)
where T : IStoreScopedEntity
{
return e => e.Stores.Select(s => s.Id).Contains(id);
}
}
I assume StoreScopeId is of type int, but you can change it to actual type if it's different.
Now the only remaining is how to call that function. There are several ways of doing that, here I'll use pure reflection
public IQueryable<T> GetQueryable()
{
var results = _repository.Table;
if (typeof(IStoreScopedEntity).IsAssignableFrom(typeof(T)))
{
results = results.Where((Expression<Func<T, bool>>)
typeof(StoreScopedEntity)
.GetMethod("IdPredicate", BindingFlags.Public | BindingFlags.Static)
.MakeGenericMethod(typeof(T))
.Invoke(null, new object[] { EngineContext.Current.StoreScopeId }));
}
return results;
}
In LightSwitch I have a PreprocessQuery event as follows:
partial void ValidOrders_PreprocessQuery(ref IQueryable<Order> query)
{
query = query.Where(order => OrderIsValid(order));
}
public bool OrderIsValid(Order order)
{
return true;
}
This fails with a message (on the HTMLClient side !) "method cannot be null".
But this works fine:
partial void ValidOrders_PreprocessQuery(ref IQueryable<Order> query)
{
query = query.Where(order => true);
}
Can someone see why?
Thanks,
Paul
The query provider is only shown the method OrderIsValid, and as that method has already been compiled down to IL it can no longer "look into it" to see it's implementation, as it would need to to create Expression objects to represent it.
There are a number of options that you have, ranging from inlining the method as you did yourself, or having the method itself return an expression, rather than doing the work:
public Expression<Func<Order, bool>> OrderIsValid()
{
return order => true;
}
This would let you write:
partial IQueryable<Order> ValidOrders_PreprocessQuery(IQueryable<Order> query)
{
return query.Where(OrderIsValid());
}
As a side note, I would strongly advise you to not pass the query by reference, but rather return a new query instead; that would be the more idiomatic approach.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
To return IQueryable<T> or not return IQueryable<T>
I have LINQ to SQL repository implemented as follows. The GetAll method is returing a generic List instead of IQueryable. However in most of the examples and tutorials pit is shown to return IQueryable. What is the advantage of returing IQueryable ?
using System.Linq;
namespace RepositoryLayer
{
public interface IRepository<T> where T : class
{
//System.Linq.IQueryable<T> GetAll();
System.Collections.Generic.List<T> GetAll();
}
public class Repository<T> : IRepository<T> where T : class
{
public System.Data.Linq.DataContext Context
{
get;
set;
}
//public virtual System.Linq.IQueryable<T> GetAll()
//{
// //GetAll is returning generic Queryable<T>.
// System.Linq.IQueryable<T> allItems = Context.GetTable<T>();
// return allItems;
//}
public virtual System.Collections.Generic.List<T> GetAll()
{
System.Linq.IQueryable<T> allItems = Context.GetTable<T>();
return allItems.ToList();
}
}
}
Business Layer
namespace BusinessLayerProject
{
public class AccountBusiness
{
//IRepository<T>
RepositoryLayer.IRepository<RepositoryLayer.Account> accountRepository;
public AccountBusiness(RepositoryLayer.IRepository<RepositoryLayer.Account> repo)
{
accountRepository = repo;
}
//public List<RepositoryLayer.Account> GetAllAccounts()
//{
// //LibraryManagementClassesDataContext context = new LibraryManagementClassesDataContext();
// //List<RepositoryLayer.Account> accontList = context.Accounts.ToList();
// System.Linq.IQueryable<RepositoryLayer.Account> acc = accountRepository.GetAll();
// return acc.ToList();
//}
public List<RepositoryLayer.Account> GetAllAccounts()
{
List<RepositoryLayer.Account> acc = accountRepository.GetAll();
return acc;
}
}
}
READING
Advantage of creating a generic repository vs. specific repository for each object?
Using IQueryable let LINQ move some additional work into DB by creating different SQL queries. eg. when you try something like GetAll().Where(condition) and use List all items are queried from DB and where condition is checked application-side. When you use IQueryable it can be moved to DB and proper items are returner directly from there.
IQueryable extends IEnumerable. Both do not project/inflate their data until being iterated, whereas IList objects pull all their data and are populated when assigned to.
So it's a "lazy-load" vs. "eager-load" distinction.
Becasue IList is - ah - not smart?
Here we go:
IIF yo uexport IQueryable - on the Get method it is the only method you ever need. All parameters go into the IQueryable and BECAUSE OF DELAYED EXECUTION END UP IN YOUR SQL LAYER.
Export IList and you GET ALL and THEN filter - in memory, which is as much a perversion of LINQ as it gets.
The real trick here is that if I makcal your Get method and then .Where, OrderBy, that gets into the sql statement in the database.