Hello,
I am totally losing faith in EF after getting absolutely nowhere with global filters and getting help over the internet.
I have a 4 base classes for my entities:
System Entity
Business Entity - inherists from system entity
Versioned Entity inherits from business
Published Entity inherits from business
I have a property bool Archived on the system and I want to always retrieve those with Archive=false
I also have a PublishLevel on the Published entity and I want to retrieve specific versions.
I created an interface that holds all the common fields across the entities and implemented the members on system entity. Some of the fields had to be marked as notmapped in order to avoid looking for publish level on entities that aren't supposed to have them.
I wrote to extension methods to IQueryable<T> where T is IEntity (my interface), class
The first one filters archives, the other one filters versioned entities using the publishlevel property depending on your role.
To keep the code clean and avoid 150+ retrieve methods one for each entity, I am attempting this using DbContext.Set<T>.TrimArchives().TrimVersions()
All that works great except I am no longer able to query by any other property other than those defined in the interface. That makes me a sad panda.
Comments: Hey, This is unfortunately a limitation of how EF6 (and earlier versions) reasons about types. It's not exactly simple... but you can workaround this by dynamically building expressions to pass to the LINQ query. Below is an example of the `TrimArchives()` method you mentioned. ~Rowan ``` using System; using System.Data.Entity; using System.Linq; using System.Linq.Expressions; namespace ConsoleApplication13 { class Program { static void Main(string[] args) { using (var db = new MyContext()) { if (!db.Customers.Any()) { db.Customers.Add(new Customer { FirstName = "Jane", LastName = "Doe", IsArchived = false }); db.Customers.Add(new Customer { FirstName = "John", LastName = "Doe", IsArchived = true }); db.SaveChanges(); } var query = db.Customers .TrimArchives() .OrderBy(c => c.FirstName) .ToList(); foreach (var item in query) { Console.WriteLine(item.FirstName); } } } } public static class Extensions { public static IQueryable<TEntity> TrimArchives<TEntity>(this IQueryable<TEntity> query) where TEntity : IEntity { var param = Expression.Parameter(typeof(TEntity), "e"); var body = Expression.MakeBinary( ExpressionType.Equal, Expression.Property(param, "IsArchived"), Expression.Constant(false)); return query.Where(Expression.Lambda<Func<TEntity, bool>>(body, param)); } } public class MyContext : DbContext { public DbSet<Customer> Customers { get; set; } } public class Customer : BaseEntity { public string FirstName { get; set; } public string LastName { get; set; } } public class BaseEntity : IEntity { public int Id { get; set; } public bool IsArchived { get; set; } } public interface IEntity { bool IsArchived { get; } } } ```
I am totally losing faith in EF after getting absolutely nowhere with global filters and getting help over the internet.
I have a 4 base classes for my entities:
System Entity
Business Entity - inherists from system entity
Versioned Entity inherits from business
Published Entity inherits from business
I have a property bool Archived on the system and I want to always retrieve those with Archive=false
I also have a PublishLevel on the Published entity and I want to retrieve specific versions.
I created an interface that holds all the common fields across the entities and implemented the members on system entity. Some of the fields had to be marked as notmapped in order to avoid looking for publish level on entities that aren't supposed to have them.
I wrote to extension methods to IQueryable<T> where T is IEntity (my interface), class
The first one filters archives, the other one filters versioned entities using the publishlevel property depending on your role.
To keep the code clean and avoid 150+ retrieve methods one for each entity, I am attempting this using DbContext.Set<T>.TrimArchives().TrimVersions()
All that works great except I am no longer able to query by any other property other than those defined in the interface. That makes me a sad panda.
Comments: Hey, This is unfortunately a limitation of how EF6 (and earlier versions) reasons about types. It's not exactly simple... but you can workaround this by dynamically building expressions to pass to the LINQ query. Below is an example of the `TrimArchives()` method you mentioned. ~Rowan ``` using System; using System.Data.Entity; using System.Linq; using System.Linq.Expressions; namespace ConsoleApplication13 { class Program { static void Main(string[] args) { using (var db = new MyContext()) { if (!db.Customers.Any()) { db.Customers.Add(new Customer { FirstName = "Jane", LastName = "Doe", IsArchived = false }); db.Customers.Add(new Customer { FirstName = "John", LastName = "Doe", IsArchived = true }); db.SaveChanges(); } var query = db.Customers .TrimArchives() .OrderBy(c => c.FirstName) .ToList(); foreach (var item in query) { Console.WriteLine(item.FirstName); } } } } public static class Extensions { public static IQueryable<TEntity> TrimArchives<TEntity>(this IQueryable<TEntity> query) where TEntity : IEntity { var param = Expression.Parameter(typeof(TEntity), "e"); var body = Expression.MakeBinary( ExpressionType.Equal, Expression.Property(param, "IsArchived"), Expression.Constant(false)); return query.Where(Expression.Lambda<Func<TEntity, bool>>(body, param)); } } public class MyContext : DbContext { public DbSet<Customer> Customers { get; set; } } public class Customer : BaseEntity { public string FirstName { get; set; } public string LastName { get; set; } } public class BaseEntity : IEntity { public int Id { get; set; } public bool IsArchived { get; set; } } public interface IEntity { bool IsArchived { get; } } } ```