-
Notifications
You must be signed in to change notification settings - Fork 2
Expressions
LINQ expressions inside Where, Select, and other methods are translated directly to SQL. The framework stays close to the shape of the LINQ query and does not wrap the result in an extra subquery just to make a method work. When a method does not have a clean SQL equivalent, you get a clear NotSupportedException rather than a hidden rewrite. When SQLite has no built-in for a method but the same shape can be expressed inline (often as a CASE WHEN ... block), the framework uses that. This page covers what is supported beyond basic comparisons.
The standard arithmetic operators all work:
var results = await db.Table<Book>()
.Where(b => (b.Price * 1.1) + 5 > 20)
.ToListAsync();
var discounted = await db.Table<Book>()
.Select(b => new { b.Title, Sale = b.Price * 0.9 })
.ToListAsync();
var evens = await db.Table<Book>()
.Where(b => b.Id % 2 == 0)
.ToListAsync();Supported operators: +, -, *, /, %.
| C# | SQL |
|---|---|
Math.Abs(x) |
ABS(x) |
Math.Round(x) |
ROUND(x) |
Math.Round(x, digits) |
ROUND(x, digits) |
Math.Round(x, digits, MidpointRounding.AwayFromZero) |
ROUND(x, digits) |
Math.Floor(x) |
FLOOR(x) |
Math.Ceiling(x) |
CEIL(x) |
Math.Truncate(x) |
TRUNC(x) |
Math.Pow(x, y) |
POWER(CAST(x AS REAL), y) |
Math.Sqrt(x) |
SQRT(x) |
Math.Cbrt(x) |
CASE WHEN x >= 0 THEN POWER(x, 1.0/3.0) ELSE -POWER(-x, 1.0/3.0) END |
Math.Exp(x) |
EXP(x) |
Math.Log(x) |
LOG(x) |
Math.Log(x, base) |
LOG(x) / LOG(base) |
Math.Log10(x) |
LOG10(x) |
Math.Log2(x) |
LOG2(x) |
Math.Sign(x) |
CASE WHEN x > 0 THEN 1 WHEN x < 0 THEN -1 ELSE 0 END |
Math.Max(x, y) |
CASE WHEN x > y THEN x ELSE y END |
Math.Min(x, y) |
CASE WHEN x < y THEN x ELSE y END |
Math.Clamp(x, min, max) |
CASE WHEN x < min THEN min WHEN x > max THEN max ELSE x END |
Math.Sin(x) |
SIN(x) |
Math.Cos(x) |
COS(x) |
Math.Tan(x) |
TAN(x) |
Math.Asin(x) |
ASIN(x) |
Math.Acos(x) |
ACOS(x) |
Math.Atan(x) |
ATAN(x) |
Math.Atan2(y, x) |
ATAN2(y, x) |
Math.Sinh(x) |
SINH(x) |
Math.Cosh(x) |
COSH(x) |
Math.Tanh(x) |
TANH(x) |
Math.Asinh(x) |
ASINH(x) |
Math.Acosh(x) |
ACOSH(x) |
Math.Atanh(x) |
ATANH(x) |
var results = await db.Table<Book>()
.Where(b => Math.Abs(b.Price - 10) < 1)
.ToListAsync();
var rounded = await db.Table<Book>()
.Select(b => new { b.Title, Price = Math.Round(b.Price, 2) })
.ToListAsync();| C# | SQL |
|---|---|
s.Length |
LENGTH(s) |
s.ToUpper() / s.ToUpperInvariant()
|
UPPER(s) |
s.ToLower() / s.ToLowerInvariant()
|
LOWER(s) |
s.Trim() |
TRIM(s) |
s.TrimStart() |
LTRIM(s) |
s.TrimEnd() |
RTRIM(s) |
s.Contains(value) |
s LIKE '%value%' ESCAPE '\' |
s.StartsWith(value) |
s LIKE 'value%' ESCAPE '\' |
s.EndsWith(value) |
s LIKE '%value' ESCAPE '\' |
s.Equals(value) |
s = value |
s.Replace(old, new) |
REPLACE(s, old, new) |
s.Substring(start, length) |
SUBSTR(s, start + 1, length) |
s[index] |
SUBSTR(s, index + 1, 1) |
s.IndexOf(value) |
INSTR(s, value) - 1 |
s.LastIndexOf(value) |
CASE WHEN LENGTH(value) = 0 THEN LENGTH(s) ELSE COALESCE((WITH RECURSIVE find_pos(pos, rem) AS (SELECT 0, s UNION ALL SELECT pos + INSTR(rem, value), SUBSTR(rem, INSTR(rem, value) + 1) FROM find_pos WHERE INSTR(rem, value) > 0) SELECT MAX(pos) - 1 FROM find_pos WHERE pos > 0), -1) END |
s.Insert(index, value) |
SUBSTR(s, 1, index) || value || SUBSTR(s, index + 1) |
s.Remove(start) |
SUBSTR(s, 1, start) |
s.Remove(start, count) |
SUBSTR(s, 1, start) || SUBSTR(s, start + count + 1) |
s.PadLeft(n) |
CASE WHEN LENGTH(s) >= n THEN s ELSE (SELECT SUBSTR(REPLACE(HEX(ZEROBLOB((n - LENGTH(s)) / 2 + (n - LENGTH(s)) % 2)), '00', ' '), 1, n - LENGTH(s)) || s) END |
s.PadLeft(n, c) |
CASE WHEN LENGTH(s) >= n THEN s ELSE (SELECT SUBSTR(REPLACE(HEX(ZEROBLOB(n - LENGTH(s))), '00', c), 1, n - LENGTH(s)) || s) END |
s.PadRight(n) |
CASE WHEN LENGTH(s) >= n THEN s ELSE (s || (SELECT SUBSTR(REPLACE(HEX(ZEROBLOB((n - LENGTH(s)) / 2 + (n - LENGTH(s)) % 2)), '00', ' '), 1, n - LENGTH(s)))) END |
s.PadRight(n, c) |
CASE WHEN LENGTH(s) >= n THEN s ELSE (s || (SELECT SUBSTR(REPLACE(HEX(ZEROBLOB(n - LENGTH(s))), '00', c), 1, n - LENGTH(s)))) END |
s + other |
s || other |
string.Concat(a, b, ...) |
a || b || ... |
string.Join(sep, values) |
val1 || sep || val2 || ... |
string.Compare(a, b) |
CASE WHEN a = b THEN 0 WHEN a < b THEN -1 ELSE 1 END |
s.CompareTo(other) |
CASE WHEN s = other THEN 0 WHEN s < other THEN -1 ELSE 1 END |
string.IsNullOrEmpty(s) |
(s IS NULL OR s = '') |
string.IsNullOrWhiteSpace(s) |
(s IS NULL OR TRIM(s, ' ') = '') |
Pass StringComparison.OrdinalIgnoreCase to Contains, StartsWith, or EndsWith to make the match case insensitive:
var results = await db.Table<Book>()
.Where(b => b.Title.Contains("test", StringComparison.OrdinalIgnoreCase))
.ToListAsync();The ?? operator translates to COALESCE:
var results = await db.Table<Book>()
.Select(b => new { b.Id, Notes = b.Notes ?? "No notes" })
.ToListAsync();| C# | SQL |
|---|---|
char.ToLower(c) |
LOWER(c) |
char.ToUpper(c) |
UPPER(c) |
char.IsWhiteSpace(c) |
TRIM(c) = '' |
char.IsAsciiDigit(c) |
c >= '0' AND c <= '9' |
char.IsAsciiLetter(c) |
(c >= 'a' AND c <= 'z') OR (c >= 'A' AND c <= 'Z') |
char.IsAsciiLetterOrDigit(c) |
(c >= '0' AND c <= '9') OR (c >= 'a' AND c <= 'z') OR (c >= 'A' AND c <= 'Z') |
char.IsAsciiLetterLower(c) |
c >= 'a' AND c <= 'z' |
char.IsAsciiLetterUpper(c) |
c >= 'A' AND c <= 'Z' |
You can read individual components of a DateTime column directly:
var recent = await db.Table<Order>()
.Where(o => o.PlacedAt.Year == 2024 && o.PlacedAt.Month == 12)
.ToListAsync();
var years = await db.Table<Order>()
.Select(o => o.PlacedAt.Year)
.ToListAsync();Supported properties: Year, Month, Day, Hour, Minute, Second, Millisecond, Ticks, DayOfWeek, DayOfYear, Date, TimeOfDay.
var shifted = await db.Table<Order>()
.Select(o => new { o.Id, Due = o.PlacedAt.AddDays(30) })
.ToListAsync();Supported methods: Add, AddYears, AddMonths, AddDays, AddHours, AddMinutes, AddSeconds, AddMilliseconds, AddMicroseconds, AddTicks.
You can read individual components of a DateOnly column the same way as DateTime:
var recent = await db.Table<Order>()
.Where(o => o.Date.Year == 2024 && o.Date.Month == 12)
.ToListAsync();Supported properties: Year, Month, Day, DayOfWeek, DayOfYear.
var shifted = await db.Table<Order>()
.Select(o => new { o.Id, Due = o.Date.AddDays(30) })
.ToListAsync();Supported methods: AddYears, AddMonths, AddDays.
Supported properties: Year, Month, Day, Hour, Minute, Second, Millisecond, Ticks, DayOfWeek, DayOfYear, Date, TimeOfDay.
Supported methods: Add, AddYears, AddMonths, AddDays, AddHours, AddMinutes, AddSeconds, AddMilliseconds, AddMicroseconds, AddTicks.
Supported properties: Hour, Minute, Second, Ticks.
Supported methods: Add, AddHours, AddMinutes.
Supported properties: Days, TotalDays, Hours, TotalHours, Minutes, TotalMinutes, Seconds, TotalSeconds, Milliseconds, TotalMilliseconds, Ticks.
var results = await db.Table<Order>()
.Where(o => o.Duration.Subtract(TimeSpan.FromHours(1)).TotalHours > 5)
.ToListAsync();Supported methods: Add, Subtract, Negate, Duration.
You can also call the static TimeSpan creation methods inside an expression:
var results = await db.Table<Order>()
.Where(o => o.Duration == TimeSpan.FromHours(o.Id))
.ToListAsync();Supported static methods: FromDays, FromHours, FromMinutes, FromSeconds, FromMilliseconds, FromMicroseconds, FromTicks.
Guid columns support equality comparisons:
Guid id = Guid.NewGuid();
var result = await db.Table<Order>()
.Where(o => o.TrackingId == id)
.FirstOrDefaultAsync();