Linq: IQueryable extension methods works on DBSet but not on ICollection - c#

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

Related

Pass linq to entities query as parameter and execute against different objects

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();
}

Why should I use IQueryable<T> over List<T> in LINQ to SQL [duplicate]

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.

Entity Framework - Load entities with child entity

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/

Entity Wrapper - Custom

I would like find a workaround to accomplish a simple solution in order to automatize certain operation through EF.
What I need it's takeover during saving and retrieving process to modifying query result, but this class will be able to make that work for any type entities.
Example: I have a MyTestDb. So in my C# project I create a new entity model (MyTEstDbModel.edmx), with relative POCO class generation.
Well, a point of interest could be implementing a new custom class like following:
class Example
{
private ObjectContext _context;
private Example(ObjectContext obj) { _context = obj; }
public void Store(ObjectSet<???generic???> os)
{
// problem here: I dont't know the type contained in ObjectSet
// but if I Knew its type, I could make a work like this:
// -> foreach every instance in objectSet to check if exist some property
// via reflection, if i found them, then I set always the same values.
// Why this? Because all my db contains some common filed
// like (createdByUser, TimeToUpdate, and so on..), so it wold be boring
// setting all those fileds from any point of program.
}
public void Retrive(ObjectSet<???generic???> os)
{
// here problem too: all my queries will be filtered by one or more value
// fileds, but I cannot use lambaExpression cos I don't Know the type
// contained in objectSet<..>
}
//....
finally, by any point of program, the code should appear like following:
Example obj = new Example(myEntityContext); //-> global
var result = myEntityContext.ObjectSetTyped.Where(..lambaExpression..condition)
result.someProperty = "...";
obj.Store(result); // store method will fill all other boring filed automatically.
Can anyone give me some tips, help, suggestion about my issue?
Thanks in advance...
Update
Now, just only another problem. I'd to filter my ObjectSet through retrieve method like following:
public void Retrieve<TEntity>(IQueryable<TEntity> ooo) where TEntity : IC
{
ooo = ooo.Where(p => p.Filed == "MyDefaultValue");
}
But, from external method, not objectSet result is affect by my filter.
How so..?
MyEntities ent = new...
MyWrapper wrap = new MyWrapper();
wrap.Retrieve(ent.Users);
//problem here -> users objectSet is always the same..
Define interfaces which will allow you to do this. For example:
public interface IEntity
{
DateTime CreatedAt { get; set; }
string CreatedBy { get; set; }
}
You need to "implement" this interface in your entities. You can for example either modify T4 template generating entities or implement it in partial class. Both properties must be already defined in the model so the implementation is only declarative:
public partial class MyEntity : IEntity // That's all
{ }
Now you can define Store like:
public void Store<TEntity>(TEntity entity) where TEntity : IEntity
{
...
}
Same can be done with query but you can for example define custom extension method:
public static IQueryable<TEntity> GetUserEntities(this IQueryable<TEntity> query, string user)
where TEntity : IEntity
{
return query.Where(e => e.CreatedBy == user);
}
You will simply define your query like:
var result = myEntityContext.MyEntities.GetUserEntities("someName");
Other approach is defining simply GetQuery on your custom context:
public IQueryable<T> GetQuery<T>() where T : IEntity
{
var query = GetObjectSetSomehow;
return query.ApplyGlobalConditions(); // Just another extension with your conditions
}
I'm not a big fan of the repository pattern but generally what you are trying to do is close to generic repository so check for example this post. It is just some example which can be further extended.
yeah, I just want a generic approach, likewise I realized with dataset and datatable.. but it seems impossible to achieve..
..ehmm..however, let me show yuo following code snippet, dynamic keyword looks like something to hopeful.... maybe I'm colse to solution..?
public ObjectSet<dynamic> Retrieve(string entity, string context)
{
Type myObjectContextType = Type.GetType(context);
ConstructorInfo cs = myObjectContextType .GetConstructor(new Type[] { });
dynamic myObjContext = cs.Invoke(new object[] { });
Type t = Type.GetType(entity);
ConstructorInfo xi = t.GetConstructor(new Type[] { });
dynamic UserEntity = xi.Invoke(new object[] { });
!problem here!
ObjectSet<?????> os = myObjContext.UserEntity.Where(...)
return ...
}
I'm very surprised..EF is a great instruments to develope more efficiently ways but too little "generalizalbe"

LinqToSql and abstract base classes

I have some linq entities that inherit something like this:
public abstract class EntityBase { public int Identifier { get; } }
public interface IDeviceEntity { int DeviceId { get; set; } }
public abstract class DeviceEntityBase : EntityBase, IDeviceEntity
{
public abstract int DeviceId { get; set; }
}
public partial class ActualLinqGeneratedEntity : DeviceEntityBase
{
}
In a generic method I am querying DeviceEnityBase derived entities with:
return unitOfWork.GetRepository<TEntity>().FindOne(x => x.DeviceId == evt.DeviceId);
where TEntity has a contraint that is it a DeviceEntityBase. This query is always failing with an InvalidOperationException with the message "Class member DeviceEntityBase.DeviceId is unmapped". Even if I add some mapping info in the abstract base class with
[Column(Storage = "_DeviceId", DbType = "Int", Name = "DeviceId", IsDbGenerated = false, UpdateCheck = UpdateCheck.Never)]
Wow, looks like for once I may be able to one-up #MarcGravell!
I had the same problem, then I discovered this answer, which solved the problem for me!
In your case, you would say:
return unitOfWork.GetRepository<TEntity>().Select(x => x).FindOne(x => x.DeviceId == evt.DeviceId);
and Bob's your uncle!
LINQ-to-SQL has some support for inheritance via a discriminator (here, here), but you can only query on classes that are defined in the LINQ model - i.e. data classes themselves, and (more perhaps importantly for this example) the query itself must be phrased in terms of data classes: although TEntity is a data class, it knows that the property here is declared on the entity base.
One option might be dynamic expressions; it the classes themselves declared the property (i.e. lose the base class, but keep the interface) - but this isn't trivial.
The Expression work would be something like below, noting that you might want to either pass in the string as an argument, or obtain the primary key via reflection (if it is attributed):
static Expression<Func<T, bool>> BuildWhere<T>(int deviceId) {
var id = Expression.Constant(deviceId, typeof(int));
var arg = Expression.Parameter(typeof(T), "x");
var prop = Expression.Property(arg, "DeviceId");
return Expression.Lambda<Func<T, bool>>(
Expression.Equal(prop, id), arg);
}
This kind of heirarchial mapping isnot possible with LinqToSql. The the mapping is setup it cannot map to properties in base classes. I went around on this for a couple of months when it first came out. The best solution is to use the entity framework. It gives you much more flexibility with creating your object model. It will allow you to do exactly what your trying to do here.
Here is some information on the entity framework: MSDN Article
Try .OfType<>() as posted here https://stackoverflow.com/a/17734469/3936440, it works for me having the exact same issue.

Categories

Resources