Passing type IQueryable for an interface in Linq for objects

I have the following method in my general class:

// This is the class declaration public abstract class BaseService<TEntity, TKey> : IBaseService<TEntity, TKey> where TEntity : class, IEntity<TKey> // The Method public IQueryable<TEntity> GetActive() { if (typeof(IActivable).IsAssignableFrom(typeof(TEntity))) { return this.repository.Get().Cast<IActivable>() .Where(q => q.Active) .Cast<TEntity>(); } else { return this.Get(); } } 

This is the interface:

 public interface IActivable { bool Active { get; set; } } 

Basically, TEntity is an Entity (POCO) class that can implement IActivable if they have an Active property. I want the method to return all records whose Active is true. However, I have this error:

Cannot enter type "WebTest.Models.Entities.Product" to enter type 'Data.IActivable. LINQ to Entities only supports EDM casting of primitive or enumerated types.

I understand why this error occurs. But articles on SO have no valid solution for my case. Is this achievable with Cast or any other way? Note. I do not want to convert to IEnumerable , I want to keep IQueryable .

+5
source share
3 answers

The EF expression parser will work without casting, however, you cannot compile C # code without casting (C # will complain that it does not know that TEntity has the Active property). The solution is: cast for the C # compiler, not for the expression for the EF expression parser.

So, if you are sure (you check it if , so that you are) that the object implements IActivable , you can create a cast expression (for compilation) and then remove the casts at runtime (which are not needed) for EF. For your specific case:

 public IQueryable<TEntity> GetActive() { if (typeof(IActivable).IsAssignableFrom(typeof(TEntity))) { Expression<Func<TEntity, bool>> getActive = x => ((IActivable)x).Active; getActive = (Expression<Func<TEntity, bool>>)RemoveCastsVisitor.Visit(getActive); return this.repository.Get().Where(getActive); } else { return this.Get(); } } 

The expression user looks like this:

 internal class RemoveCastsVisitor : ExpressionVisitor { private static readonly ExpressionVisitor Default = new RemoveCastsVisitor(); private RemoveCastsVisitor() { } public new static Expression Visit(Expression node) { return Default.Visit(node); } protected override Expression VisitUnary(UnaryExpression node) { if (node.NodeType == ExpressionType.Convert && node.Type.IsAssignableFrom(node.Operand.Type)) { return base.Visit(node.Operand); } return base.VisitUnary(node); } } 

It simply checks if any casting is required: if the actual value already implements the type it refers to, it will simply remove the conversion from the expression and EF will select it correctly.

+7
source

I currently have an alternative to using the extension method. However, the disadvantage is that my IBaseService cannot declare the GetActive method, because specific classes do not actually implement it.

 public static class BaseServiceExtension { public static IQueryable<TEntity> GetActive<TEntity, TKey>(this IBaseService<TEntity, TKey> service) where TEntity : class, IEntity<TKey>, IActivable { return service.Get().Where(q => q.Active); } } 
0
source

The trick is to output the entire IQueryable <TEntity> object to an IQueryable <IActivable> instead of the first actor:

 if (typeof(IActivable).IsAssignableFrom(typeof(TEntity))) { return ((IQueryable<IActivable>)(this.repository.Get())) .Where(q => q.Active) .Cast<TEntity>(); } 
0
source

Source: https://habr.com/ru/post/1240049/


All Articles