What I am trying is to load all the A entities in the database together with B entities. The relationship between them is Every A entitiy has one B entitiy.
The project I am working on has a repository pattern and it has an All() method as below.
public class EFRepository<T> : IRepository<T> where T : class, IObjectWithChangeTracker
{
private IObjectSet<T> objectset;
private IObjectSet<T> ObjectSet
{
get
{
if (objectset == null)
{
objectset = UnitOfWork.Context.GetObjectSet<T>();
}
return objectset;
}
}
public virtual IQueryable<T> All()
{
return ObjectSet.AsQueryable();
}
}
Is there any way that I can force B entities to eager load. What I've found is there there is no Include method on the IQueryable that returns from the All() method. I am happy to add a new member to repositroy so it can allow the client to use eager loading. But how can I do it?
The problem is that IQueryable is agnostic of what's backing it, and Include is an Entity Framework feature. You could have an IQueryable that uses LINQ to Objects instead, and Include wouldn't make sense there. The easiest thing would be to change the type of All() to an IObjectSet, from which you should have access to an Include extension method.
If you can't change the return type of All, you will have to structure your queries in such a way that they pull in the child elements eagerly.
IList<Parent> parentsWithEagerLoadedChildren = parentRepo.All()
.Select(p => new {p, p.Children}).ToList() // EF will attach the children to each parent
.Select(p => p.p).ToList(); // Now that everything is loaded, select just the parents
You can create your own extension method which will allow you to use Include(path) against any IQueryable<T>:
public static IQueryable<TSource> Include<TSource>
(this IQueryable<TSource> source, string path)
{
var objectQuery = source as ObjectQuery<TSource>;
if (objectQuery != null)
{
return objectQuery.Include(path);
}
return source;
}
There's a full explanation on Julie Lerman's blog: http://thedatafarm.com/blog/data-access/agile-entity-framework-4-repository-part-5-iobjectset/
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
As stated here EntityFramework (at least in version 6) does not support user types comparison (and thus, entities comparison) which is not practical for an ORM.
I have a scenario where I have a lot of code that does entities comparison that should be translated to SQL.
All my entities have an Id field of type int.
class Entity
{
public int Id { get; set; }
}
For queries where I want to compare entities in a Where clause, I would like to be able the compiler to detect if trying to perform an invalid comparison.
Considere the following classes :
class SomeEntity : Entity
{
public RelationEntity_Id int{ get; set; }
public RelationEntity Relation { get; set; }
}
class RelationEntity : Entity
{
}
With the following syntax there is such a check on the types:
public IQueryable<SomeEntity> SearchByRelation(RelationEntity relation)
{
return CreateQueryable().Where(e => s.Relation == relation);
}
With the following one, we are only comparing int and this could be error prone :
public IQueryable<SomeEntity> SearchByRelation(RelationEntity relation)
{
return CreateQueryable().Where(e => s.Relation.Id == relation.Id);
}
Because EntityFramework needs to be told how the comparison on objects should be done, I'm looking for a way create a generic Expression returning an expression Comparing the Id of a SomeEntity with the Id of a RelationEntity.
Something like this :
public IQueryable<SomeEntity> SearchByRelation(RelationEntity relation)
{
return CreateQueryable().Where(e => AreEquals(s.Relation, relation));
}
AreEquals would be adapting the Expression Tree for the SQL generated correctly comparing on entity Ids.
I have found this post which seems to be a good start, but I can't get it to append the '.Id' part of the Expression.
Any idea of how I could achieve this, or what could be the good way to go ?
Here you go.
The helper function (inside some static class):
public static IQueryable<T> WhereEquals<T>(this IQueryable<T> source, T target)
where T : Entity
{
var item = Expression.Parameter(typeof(T), "item");
var body = Expression.Equal(
Expression.Property(item, "Id"),
Expression.Property(Expression.Constant(target), "Id"));
var predicate = Expression.Lambda<Func<T, bool>>(body, item);
return source.Where(predicate);
}
and the usage:
public IQueryable<SomeEntity> SearchByRelation(RelationEntity relation)
{
return CreateQueryable().WhereEquals(relation);
}
I am using the following class to wrap some DocumentDB access which allows me to store multiple entities in the same collection:
public class TypedEntity<T> {
public string Type { get; set; }
public T Item { get; set; }
public TypedEntity(T item) {
Id = Guid.NewGuid().ToString();
Item = item;
Item.Id = Id;
Type = typeof (T).FullName;
}
}
The usage of this class is encapsulated inside a repository class. I'm trying to build the API of the repository class such that the consumer doesn't need to know about the usage of TypedEntity<T> and can instead treat it as a source for just <T>. For example, the repository has a method with this signature:
public async Task<IQueryable<T>> WhereAsync(Func<T, bool> predicate)
In order to actually retrieve this data, the predicate needs to be combined/converted with one that interacts with TypedEntity<T>. This is the pseudo code that I'm picturing in my head for what I'd ultimately like to achieve:
public async Task<IQueryable<T>> WhereAsync(Func<T, bool> predicate) {
// remembering that dataSource is talking to a backing store of TypedEntity<T>
var queryable = dataSource.Where(x => x.Type == typeof(T).FullName && predicate(x.Item));
// ... other business logic stuff
}
This actually builds but ultimately results in an Expression that uses .Invoke around the passed in predicate (which DocumentDb is unable to understand). Is there some way that I can combine the type part with the passed in Func to build up the Expression manually?
You'll want to take in an Expression<> rather than just a Func<>. Then, it should be fairly easy to apply it in a Where() clause after you've performed a Select() projection:
public async Task<IQueryable<T>> WhereAsync(Expression<Func<T, bool>> predicate) {
// remembering that dataSource is talking to a backing store of TypedEntity<T>
var typeName = typeof(T).FullName;
var queryable = dataSource.Where(x => x.Type == typeName)
.Select(x => x.Item)
.Where(predicate);
// ... other business logic stuff
}
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();
}
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.