Some LINQ queries throws InvalidQueryException

In some cases a LINQ query throws InvalidQueryExceptions when it detects a possibly bad performance query. The main purpose is to indicate limitations in the LINQ query provider and force the developer to rewrite the query.

LINQ introduces a vareity of functionality that a developer can use but not all queries are possible to translate to a server executeable query. From a developer perspective, it can look like the whole LINQ query is processed on the server and the limited result is returned to the client, but those parts that cannot be translated to a server query is processed locally after the server processing has been performed.

If the LINQ query only supplies local filters to the query, it can end up reurning the complete asset list from ImageVault that would be costly from a performance perspective.

As a general rule, always specify a Take operation to limit the amount of data requested from the server.

Mixed server and local filters with Skip/Take

If you create a LINQ expression that mixes local and server filters and uses Skip/Take, this will throw an InvalidQueryException. Since the whole filter cannot be executed on the server, the Skip/Take can not be executed until the whole filter has been processed. The exception is an indication on a possibly incorrect written LINQ query that could cause bad performance.

Example: If you have a custom class that has a metadata mapping, the metadata mapping cannot be evaluated on the server.

public class MyMedia : MediaItem
{
    [Metadata(Name = "title")]
    public string Title { get; set; }
}

If you then mix filters (take all assets in vault with id 10 (server filter) and where title is "Flower" (local filter)) with Skip/Take...

var flowers = client.Query<MyMedia>()
    .Where(m => m.VaultId == 1 && m.Title == "Flower")
    .Take(10)
    .ToList();

...you will get an exception that warns you for a potential performance trap.

To work around this limit, just separate the server and local filters with a ToList() to force the server execution.

var flowers = client.Query<MyMedia>()
    .Where(m => m.VaultId == 1)
    .ToList()
    .Where(m => m.Title == "Flower")
    .Take(10)
    .ToList();

Note: This behavior was introduced in ImageVault 5.4.

Order by local fields

Not all sorting are supported by the server and sorting on other fields will cause sorting to be executed locally.

Example: If you use the same custom class as above and orders by the Title field in combination with Skip/Take, it will throw an InvalidQueryException.

var flowers = client.Query<MyMedia>()
    .Where(m => m.VaultId == 1)
    .OrderBy(m => m.Title)
    .Take(10)
    .ToList();

The workaround is quite simple, just add a .ToList() to force server execution before sorting the data.

var flowers = client.Query<MyMedia>()
    .Where(m => m.VaultId == 1)
    .ToList()
    .OrderBy(m => m.Title)
    .Take(10)
    .ToList();

, prior to that, the InvalidQueryException was thrown if you order by a local field in combination with Skip/Take._

comments powered by Disqus