Reputation: 312
I am working on generic implementation of querying and paging data using ef core and linq in c#.
I'm aware that not everything can be translated from linq to sql, but I still have a feeling that I'm missing something and what I'm trying to achieve is actually possible.
I have a base class for all entities with QueryProperty
public class EntityBase
{
public abstract string QueryProperty { get; }
}
Each entity overrides this property and guides us to the property which we would like to search by.
public class ChildEntity : EntityBase
{
public string Name { get; set; }
public override string QueryProperty => Name;
}
This is the method I'm using to query and paginate
private IQueryable<TEntity> Paginate<TEntity>(IQueryable<TEntity> queryable, PaginationArguments arguments) where TEntity : EntityBase
{
return queryable
.Where(q => q.QueryProperty.Contains(arguments.Query))
.Skip((arguments.Page - 1) * arguments.PerPage).Take(arguments.PerPage);
}
This kind of implementation results in The LINQ expression could not be translated.
exception. The full exception:
System.InvalidOperationException: The LINQ expression 'DbSet<ChildEntity>.Where(l => l.QueryProperty.Contains("new"))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
Does anyone know what am I missing? Or is it impossible to query data this way? If there is any other way to achieve the desired result, I'll be grateful for pointing it out.
Upvotes: 2
Views: 650
Reputation: 205639
It's not possible to query this way.
Because all EF Core query translator sees at runtime is this
public abstract string QueryProperty { get; }
and there is no way to see this (the implementation)
=> Name;
because it has no access to the source code. All it has is the reflection to find property definition (thus name and type), but not the implementation - you could try that yourself.
Remember that query translation does not create entity instances (thus executing code) - it just uses the metadata from the class, data annotations and fluent mapping to produce a server side query (SQL).
You have to find another way to provide that information, not using entity OOP techniques. It could be a separate class describing TEntity
, or some property marker (with custom attribute) which at the end should give you Expression<Func<TEntity, string>>
or just string
property name to be used in the search. In the former case you would dynamically (using Expression
class) compose expression
q.{Actual_Property_Name}.Contains(arguments.Query)
and it the later you would use the specially provided EF.Property
method
EF.Property<string>(q, Actual_Property_Name).Contains(arguments.Query)
Upvotes: 3