為什麼要寫
為什麼要寫,大概就是沉沒成本吧
只是從Source Generators出來開始,就打算以其研究是否能做 aop (現在已經有內建功能了),本來當年就想嘗試能否在 orm 做一些嘗試,可惜種種原因,自己都忘了這個打算了
直到今年7月份,才又想起了這個打算,現在精力不行了,本來研究一下原理和功能限制也算完了,
可是都寫了蠻久了,不寫完整點,感覺有點浪費,又花了不少時間才把 類似 DapperAOT 功能做的差不多
寫完了又覺得光復制複製功能 沒意思, 所以又把以前為了在公司內少寫一些比較重複性程式碼做的 查詢定製功能 在 sv.db 基礎上搞一把, 反正成本已經花了那麼多了
雖然這也不算多大創新, 類似想法老早就不少人搞了,比如最誇張的 graphql 這一套甚至讓大家的基礎設施都得搭一套,
不過越搞越多,都到國慶了,目前還只能算主體完成了,細節還有很多沒搞
很多轉換 解析都是手寫的,不是因為寫的nb,只是引入其他庫相容可能有點麻煩,場景不多,遞迴遍歷就夠了
nuget 什麼的也沒空搞了,bug 估計不少,等後面再完整點再搞,反正都是自娛自樂
真是年紀大了,做什麼都越來越慢了
sv.db 能做什麼
1. db對映到實體
像 DapperAOT 一樣,使用 Source Generators 在構建期間生成必要的程式碼,以幫助您更輕鬆地使用 sql。
理論上,您還可以進行 Native AOT 部署
其實沒什麼,舉個例子吧
public async Task<object> OldWay()
{
var a = factory.GetConnection(StaticInfo.Demo);
using var dd = await a.ExecuteReaderAsync("""
SELECT count(1)
FROM Weather;
SELECT *
FROM Weather;
""");
var t = await dd.QueryFirstOrDefaultAsync<int>();
var r = await dd.QueryAsync<string>().ToListAsync();
return new { TotalCount = t, Rows = r };
}
2. 讓查詢編碼簡單,並支援更復雜的條件
透過定義一些簡單的查詢規則,我們可以將 查詢轉換為 db / api / es 查詢語句 ....
目前只搞了 db 轉換,並且還沒空完整適配測試, es 、mongodb 什麼等後面有空把
不過理論上,我們可以這樣做:
http query string / body |------> select statement |------> db (sqlite / mysql/ sqlserver / PostgreSQL)
Expression code |------> |------> es
|------> mongodb
|------> more .....
中間 select statement
這一層已經定義好了,前面的轉換也有了, 後面的理論加上適配,什麼都可以做,
2.1 舉個 api 的栗子
Code exmples:
首先定義一個 實體配置, 列明哪些欄位可以查,可以排序,可以篩選
[Db("Demo")]
[Table(nameof(Weather))]
public class Weather
{
[Select, Where, OrderBy]
public string Name { get; set; }
[Select(Field = "Value as v"), Where, OrderBy]
public string V { get; set; }
[Select(NotAllow = true)]
public string Test { get; set; }
}
然後定義 查詢介面
[HttpGet]
public async Task<object> Selects() // 你可以自己做一些欄位,授權檢查 [FromQuery, Required] string name)
{
return await this.QueryByParamsAsync<Weather>();
}
接著你就可以讓使用者自己拼寫各種條件,讓她們自己滿足自己的場景,這樣自己可以多摸一會魚了
curl --location 'http://localhost:5259/weather?where=not (name like '%e%')&TotalCount=true'
Response
{
"totalCount": 1,
"rows": [
{
"name": "H",
"v": "mery!"
}
]
}
2.2 同樣可以讓查詢程式碼更簡單
Code exmples:
其實很多 orm 都提供用Expression 達到類似或更復雜效果的
這裡考慮工作量,restful api 和其他資料查詢實現 支援度不一,目前只做基礎filter 支援, join 什麼都不搞了,就算搞了多半又會被罵,直接寫sql 不更好嗎?
比如下面 query Weather which name no Contains 'e'
public async Task<object> DoSelects()
{
return await factory.ExecuteQueryAsync(From.Of<Weather>().Where(i => !i.Name.Like("e")).WithTotalCount());
}
這裡就非常簡單介紹一下,複雜的,怎麼實現的不寫,累了,寫不動了,要有感興趣的 可以在 gayhub 看原始碼 https://github.com/fs7744/sv.db
下面再列舉一下過濾運算子支援情況
Query in api
Both has func support use query string or body to query
body or query string will map to Dictionary<string, string>
to handle
operater
such filter operater just make api more restful (Where=urlencode(complex condition)
will be more better)
{{nl}}
is null- query string
?name={{nl}}
- body
{"name":"{{nl}}"}
- query string
{{eq}}
Equal =- query string
?name=xxx
- body
{"name":"xxx"}
- query string
{{lt}}
LessThan or Equal <=- query string
?age={{lt}}30
- body
{"age":"{{lt}}30"}
- query string
{{le}}
LessThan <- query string
?age={{le}}30
- body
{"age":"{{le}}30"}
- query string
{{gt}}
GreaterThan or Equal >=- query string
?age={{gt}}30
- body
{"age":"{{gt}}30"}
- query string
{{gr}}
GreaterThan >- query string
?age={{gr}}30
- body
{"age":"{{gr}}30"}
- query string
{{nq}}
Not Equal !=- query string
?age={{nq}}30
- body
{"age":"{{nq}}30"}
- query string
{{lk}}
Prefix Like 'e%'- query string
?name={{lk}}e
- body
{"name":"{{lk}}e"}
- query string
{{rk}}
Suffix Like '%e'- query string
?name={{rk}}e
- body
{"name":"{{rk}}e"}
- query string
{{kk}}
Like '%e%'- query string
?name={{kk}}e
- body
{"name":"{{kk}}e"}
- query string
{{in}}
in array (bool/number/string)- query string
?name={{in}}[true,false]
- body
{"name":"{{in}}[\"s\",\"sky\"]"}
- query string
{{no}}
not- query string
?age={{no}}{{lt}}30
- body
{"age":"{{no}}{{lt}}30"}
- query string
Func Fields:
Fields
return some Fields , no Fields orFields=*
is return all- query string
?Fields=name,age
- body
{"Fields":"name,age"}
- query string
TotalCount
return total count- query string
?TotalCount=true
- body
{"TotalCount":"true"}
- query string
NoRows
no return rows- query string
?NoRows=true
- body
{"NoRows":"true"}
- query string
Offset
Offset Rows index- query string
?Offset=10
- body
{"Offset":10}
- query string
Rows
Take Rows count, default is 10- query string
?Rows=100
- body
{"Rows":100}
- query string
OrderBy
sort result- query string
?OrderBy=name:asc,age:desc
- body
{"OrderBy":"name:asc,age:desc"}
- query string
Where
complex condition filter- query string
?Where=urlencode( not(name like 'H%') or name like '%v%' )
- body
{"Where":"not(name like 'H%') or name like '%v%'"}
- operaters
- bool
- example
true
orfalse
- example
- number
- example
12323
or1.324
or-44.4
- example
- string
- example
'sdsdfa'
or'sds\'dfa'
or"dsdsdsd"
or"fs\"dsf"
- example
= null
is null- example
name = null
- example
=
Equal- example
name = 'sky'
- example
<=
LessThan or Equal- example
age <= 30
- example
<
LessThan- example
age < 30
- example
>=
GreaterThan or Equal- example
age >= 30
- example
>
GreaterThan- example
age > 30
- example
!=
Not Equal- example
age != 30
- example
like 'e%'
Prefix Like- example
name like 'xx%'
- example
like '%e'
Suffix Like- example
name like '%xx'
- example
like '%e%'
Like- example
name like '%xx%'
- example
in ()
in array (bool/number/string)- example
in (1,2,3)
orin ('sdsdfa','sdfa')
orin (true,false)
- example
not
- example
not( age <= 30 )
- example
and
- example
age <= 30 and age > 60
- example
or
- example
age <= 30 or age > 60
- example
()
- example
(age <= 30 or age > 60) and name = 'killer'
- example
- bool
- query string