Don't client-side large objects [Possible 3.x regression]
See original GitHub issueThis might be a 3.x regression. The code below was developed in 2.x and I’m 90% confident that it did what I want. We recently noticed a serious perf regression and observed the (new?) behavior described here.
Take a model that maps a DB blob into a .net byte[]
.
class File
{
public int Id { get; set; }
public string Name { get; set; }
public byte[] Data { get; set; } // blob in DB
}
This query attempts to fetch the size of the first 20 records:
var q = from f in db.Files select new { f.Name, Length = f.Data.Length };
q.Take(20).ToList();
The SQL looks like this:
select Name, Data
from Files
where rownum <= 20
My gripe here is that linq2db fetches all of Data
and chooses to evaluate Data.Length
on the client-side.
Generally speaking, I love how linq2db evaluates as much as possible client-side. It gives access to all .net features that dbs don’t have and the results are consistent with other c# code (there’s always minor discrepancies in the implementation of “similar” functions).
In this specific instance, I don’t love it as much because Data
is a heavy, costly object. Just imagine each blob is on average 5Mo. That query moves 100Mo over the network!!
It’s especially nasty because it still “works”. We noticed the problem after significant perf. degradation.
I would suggest that byte[].Length
is special-cased to always be server-side evaluated.
I.e. above query should generate:
select Name, LENGTH(Data)
from Files
where rownum <= 20
Side note: I couldn’t find a built-in Sql.Length()
that takes byte[]
, so working around this requires a bit of knowledge (I created my own length function).
Environment details
linq2db version: 3.1.4 Database Server: Oracle 12 Database Provider: Managed Operating system: Windows .NET Framework: Core 2.1
Issue Analytics
- State:
- Created 3 years ago
- Comments:5 (5 by maintainers)
@MaceWindu
It’s not so easy to fix…
Adding
to Expressions does not work.
I then get this Exception: (Of cause, I added the Function before to SQL Class)
I’ll look if i can fix 😃
I cannot find byte.Length mapping, so I’m pretty sure it is not regression. Anyway, we need to add
Sql.Length
function forbyte[]
(we have it only forBinary
for some reason) and mapbyte[].Length
/Binary.Length
to it.