I'm trying to filter a LINQ-to-entities query in a generic way, but I keep getting an error. Here is a piece of code:
private IQueryable<T> FilterDeletedEntities<T>(IQueryable<T> entities)
{
if (typeof(IDeletable).IsAssignableFrom(typeof(T)))
{
var deletableEntities = (IQueryable<IDeletable>)entities;
deletableEntities = deletableEntities.Where(entity => !entity.Deleted);
entities = (IQueryable<T>)deletableEntities;
}
return entities;
}
Basically I'm trying to filter out deleted entities (i.e. 'Deleted' field is 'true'), if and only if the entity is IDeletable (i.e. it has the 'Deleted' field). The problem is that I can't cast IQueryable< IDeletable > back to IQueryable< T >.
Any ideas on how to fix this? And before you ask: yes, this method has to be generic.
Thanks in advance!
But you can use Cast<T>() to convert it.
entities = deletableEntities.Cast<T>();
You could also use it to case to IDeletable as well, for example,
private IEnumerable<T> FilterDeletedEntities<T>(IQueryable<T> entities)
{
if (typeof(IDeletable).IsAssignableFrom(typeof(T)))
{
return entities.ToList()
.Cast<IDeletable>()
.Where( e => !e.Deleted )
.Cast<T>();
}
return entities.ToList();
}
I was able to solve my problem by doing this:
private IQueryable<T> FilterDeletedEntities<T>(IQueryable<T> entities)
{
if (typeof(IDeletable).IsAssignableFrom(typeof(T)))
{
var deletableEntities = (IQueryable<IDeletable>)entities;
return deletableEntities.Where(entity => !entity.Deleted).Cast<T>();
}
return entities;
}
Thanks to tvanfosson for the inspiration.
If you can assume that no one will need to call this method with T that does not implement IDeletable, you can restrict T:
private IQueryable<T> FilterDeletedEntities<T>(IQueryable<T> entities) where T : IDeletable
As a bonus, you won't need to cast anything or use reflection to test for IDeletable.
Related
I have a large model that I'm trying to obtain from the database and am using the .Include(string) extension method to load all the entities that I need. It is getting very messy and I now have 18 rows that are also long in horizontal length where I have to chain them together.
Example:
var myModel = repository.Queryable()
.Include("Entity1")
.Include("Entity1.Entity2")
.Include("Entity1.Entity2.Entity3")
.Include("Entity1.Entity2.Entity3.Entity4")
.Include("Entity1.Entity2.Entity3.Entity4.Entity5")
and so on!
There must be a better way of doing this? I'm struggling to find any help on a better way. I also then have a fair few condition I need to apply on each table for example removed flags on tables need to be checked. I'm wondering whether it would just be easier to get this from the database using another method.
If you write .Include("Entity1.Entity2.Entity3.Entity4.Entity5") all related entities are eager loaded, not just the last. So you can write just
repository.Queryable().Include("Entity1.Entity2.Entity3.Entity4.Entity5");
and you will have loaded Entity3 as well as Entity5. Check http://msdn.microsoft.com/en-gb/data/jj574232#eagerLevels for more details, especially
Note that it is not currently possible to filter which related entities are loaded. Include will always being in all related entities.
Also I think that much better is type safe variant of extension method Include. It is more robust to properties renaming etc. than string variant.
repository.Queryable().Include(x => x.Entity1.Entity2.Entity3.Entity4.Entity5);
Here is an example of generic repository with the option to include the navigation properties of an entity :
public class Repository<T> : IRepository<T> where T : class, IEntity
{
...
public virtual T GetOne(Expression<Func<T, bool>> predicate = null, Expression<Func<T, object>>[] includeProperties = null)
{
var set = SetWithIncludes(includeProperties);
return predicate != null ? set.FirstOrDefault(predicate) : set.FirstOrDefault();
}
protected IQueryable<T> SetWithIncludes(IEnumerable<Expression<Func<T, object>>> includes)
{
IQueryable<T> set = DbSet;
if (includes != null)
{
foreach (var include in includes)
{
set = set.Include(include);
}
}
return set;
}
}
and the actual usage :
_entityRepository.GetOne(c => c.Id == id, new Expression<Func<Entity, object>>[] { c => c.SubEntityOrEntityCollection.SubSubEntityOrEntityCollection });
I'm using NHibernate in a repository pattern with DI to keep things flexible. I don't want the associated interfaces to expose anything vendor specific like ICriterion. But, I want my query class to accept a delegate that I can put into the .Where clause.
I need something like:
public IEnumerable<MyClass> Execute(Func<MyClass, bool> selector)
{
return session
.QueryOver<MyClass>()
.Where(selector)
.....
}
Is there a way to achieve this or am I going about it in the wrong way?
You are not going the wrong way. I think your code works ; your problem being that you retrieve all your table in memory where filtering is done.
I guess this should enable filtering at DB level for criteria which can be translated to SQL :
public IEnumerable<MyClass> Execute(Expression<Func<MyClass, bool>> selector)
{
return session.Query<MyClass>().Where(selector).ToList();
}
and I bet this should work too :
public IEnumerable<MyClass> Execute(Expression<Func<MyClass, bool>> selector)
{
return session.QueryOver<MyClass>().Where(selector).ToList();
}
Hope this will help
In my DB I have tables who have an attribute int DeleteState. I want a generic method to query those tables. In other words a method who does this: Context.Table.Where(x => x.DeleteState == 0).
I thought I could do this:
public static class Extensions
{
public static IQueryable<T> Exists<T>(this IQueryable<T> qry) where T : IDeletable
{
return qry.Where(x => x.DeleteState == 0);
}
}
Where IDeletable is this:
public interface IDeletable
{
int DeleteState { get; set; }
}
Now I only have to add the IDeletable in the EF model:
public partial class Table : EntityObject, IDeletable { ... }
I did this with the templating mechanism.
Unfortunately, it doesn't work :( It compiles fine, but throws at runtime:
Unable to cast the type 'Table' to type 'IDeletable'. LINQ to Entities only supports casting Entity Data Model primitive types
if I call it like that:
Context.Table.Exists();
How can I solve this problem? Could you think of a fix or a different method to achieve similar results? Thx
The problem you have is that the Entity Framework can only work with an Expression Tree. Your function executes a query directly instead of building an Expression Tree.
A simpler solution would be to add a Model Defined Function.
A model defined function can be called directly on an instance of your context.
Maybe:
public static IQueryable<T> Exists<T>(this IQueryable<T> qry)
{
return qry.Where(x => (!typeof(IDeletable).IsAssignableFrom(x.GetType()) || typeof(IDeletable).IsAssignableFrom(x.GetType()) && ((IDeletable)x).DeleteState == 0));
}
Tsss, this is the answer: Linq Entity Framework generic filter method
I forgot about the class here:
... where T : class, IDeletable
Have you tried converting your objects to IDeletable before you actually query? e.g.
public static IQueryable<T> Exists<T>(this IQueryable<T> qry)
{
return qry.Select<T, IDeletable>(x => x).Where(x => x.DeleteState == 0).Cast<T>();
}
I haven't tested this code, however, the error rings a bell and I remember I had to do something similar.
I've got a few tables that all have the same column domainID which basically just controls what data gets displayed on which website, as they share a database.
So when I go to databind a table to a control I would need to create a large switch to handle the different LINQ queries. I would like to create a utility method which takes the table type as a parameter and then return a where clause based on a column in passed table.
public static IEnumerable<T> ExecuteInContext<T>(
IQueryable<T> src)
{
int domain = 1;//hard coded for example
return src.Where(x => x.DomainID == domain);//Won't work, has to be a way to do this.
}
I'm stuck on the return code. You can't simply construct a where clause like I currently am because it doesn't know what table i'm talking about.
I'm trying to call that first method like this:
using (DataClasses1DataContext db = new DataClasses1DataContext())
{
var q = Utility.ExecuteInContext(db.GetTable<item>());
Repeater1.DataSource = q;
Repeater1.DataBind();
}
I hope this explains what I'm trying to do.
Edit: BrokenGlass's answer solved my problem. I would like to add that you need to open up your .dbml.cs file and extend the table/class with your interface. I also wanted to point out that the project wouldn't build if my column was nullable, it said it wasn't the same return type as my interface.
You have to restrict your T to a class that has a property of DomainID - you can add these interface implementations in partial classes that extend your data model.
public interface IFoo
{
int DomainId { get; set; }
}
..
public static IQueryable<T> ExecuteInContext<T>(IQueryable<T> src) where T: IFoo
{
int domain = 1;//hard coded for example
return src.Where(x => x.DomainID == domain);
}
Expression pe = Expression.Parameter(typeof(T));
Expression prope = Expression.Property(pe, "DomainID");
Expression ce = Expression.Equals(prope,
Expression.Constant((int)1);
Expression<Func<T,bool>> exp =
Expression.Lambda<Func<T,bool>>(
ce, pe);
return query.Where(exp);
You should be able to cast your generic parameter to the intended type...
public static IEnumerable<T> ExecuteInContext<T>(IQueryable<T> src)
{
int domain = 1;//hard coded for example
return src.Where(x => ((T)x).DomainID == domain);
}
But you realize you've created a generic method that assumes its type parameter will always expose a specific property? If you're going to do that, you should apply a generic type constraint such that T is always derived from a type that has that property...
For example:
public static IEnumerable<T> ExecuteInContext<T>(IQueryable<T> src) where T : IMyDomainObject
I'm not sure if I understand what you mean, but maybe you want to add a where clause:
public static IEnumerable<T> ExecuteInContext<T>(IQueryable<T> src)
where T: MyType //MyType exposing your DomainId
{
int domain = 1;//hard coded for example
return src.Where(x => x.DomainID == domain);//Won't work, has to be a way to do this.
}
public IQueryable<T> All()
{
var session = _sessionFactory.GetCurrentSession();
return FilterByClientId(from r in session.Query<T>() select r);
}
public IQueryable<T> FilterByClientId(IQueryable<T> queryable)
{
return queryable.Where(row => _clientIds.ClientIds.Contains<long>(row.ClientId) );
}
Can I make use of Custom Attribute on the method to handle the decoration? the resulting code would look something like this. call to All method with the ClientFilter would automatically decorate the result.
[ClientFilter]
public IQueryable<T> All()
{
var session = _sessionFactory.GetCurrentSession();
return from r in session.Query<T>() select r;
}
You're looking for PostSharp, which allows you to modify method behavior using attributes.
However, it will add tremendous complexity and probably isn't worth it for something this simple.
If I understand what you are asking, then the answer is probably yes, but the complication of using attributes isn't worth it. Wouldn't it be simpler to make your second code sample simply be as follows?
// Edited to make more sense, but see below...
public IQueryable<T> FilterByClientId()
{
return All().Where(row => _clientIds.ClientIds.Contains<long>(row.ClientId) );
}
EDIT: Based on your comment, try defining FilterByClientId as an extension method with a generic constraint:
public static IQueryable<T> FilterByClientId(this IQueryable<T> queryable) where T : IHasClientId
{
return queryable.Where(row => _clientIds.ClientIds.Contains<long>(row.ClientId) );
}