Isard
Isard

Reputation: 312

Generic implementation of querying - LINQ expression could not be translated

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

Answers (1)

Ivan Stoev
Ivan Stoev

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

Related Questions