FreeSql是一個支持.NET Core 2.1+、.NET Framework 4.0+ 以及 Xamarin的ORM(Object Relational Mapping)對象關系映射的組件
支持豐富的表達式函數及類型映射,但還是有不少開發者需要執行自定義SQL。
我一般會推薦他們使用
List<T> list = fsql.Ado.Query<T>("select * from t1");
等類似的操作,IAdo下有大量的ADO.NET基礎的調用操作。但開發者還想使用類似Page,Skip,OrderBy等方法。鑒於fsql可以生成SQL,可以將不同的SQL組合,實現更加復雜的功能。
- 引用包
dotnet add packages FreeSql
dotnet add packages FreeSql.Provider.Sqlite
- .NET Core 單例 Startup.cs
public void ConfigureServices(IServiceCollection services)
{
IFreeSql fsql = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=db1.db")
.UseAutoSyncStructure(true) //自動同步實體結構到數據庫,FreeSql不會掃描程序集,只有CRUD時才會生成表。
.Build();
services.AddSingleton<IFreeSql>(fsql);
}
可使用以下方式實現
WithSql 自定義SQL
定義實體類
public class TestClass
{
[Column(Name = "ID", IsPrimary = true)]
public string No { get; set; }
public int? Age { get; set; }
public string Name { get; set; }
[Column(Name = "BIRTH_DAY")]
public DateTime? Birthday { get; set; }
public decimal Point { get; set; }
public Sex? Sex { get; set; }
}
public class TestClssDto
{
public string ID { get; set; }
public int? Age { get; set; }
}
不同的查詢方式。
- 返回
DataTable
- 返回
List<Tuplue>
即List<(string,string)>
元組 - 返回
List<object>
且能支持分頁 - 返回
List<TestClassDto>
且能支持分頁
1.返回DataTable
DataTable dt1 = _fsql.Select<object>()
.WithSql("select * from TestClass ")
.ToDataTable("ID,Age");
SELECT ID,Age
FROM(select * from TestClass ) a
2.返回DataTable
DataTable dt2 = _fsql.Select<object>()
.WithSql("select * from TestClass ")
.ToDataTable("*");
SELECT *
FROM ( select * from TestClass ) a
3.返回List<Tuplue>
即List<(string,string)>
元組
List<(string,string)> list1 = _fsql.Select<object>()
.WithSql("select * from TestClass ")
.ToList<(string, string)>("ID,Age");
SELECT ID, Age
FROM(select * from TestClass ) a
4.返回List<object>
var list2 = _fsql.Select<object>()
.WithSql("select * from TestClass ")
.ToList<object>("*");
SELECT *
FROM(select * from TestClass ) a
5.返回List<object>
且能支持分頁
var list3 = _fsql.Select<object>().WithSql("select * from TestClass ")
.WhereIf(true, "1=1")
.Page(1, 10).OrderBy("ID DESC").ToList<object>("ID,Age");
SELECT ID, Age
FROM(select * from TestClass ) a
WHERE(1 = 1)
ORDER BY ID DESC
limit 0,10
6.返回List<TestClassDto>
且能支持分頁
var list4 = _fsql.Select<object>().WithSql("select * from TestClass ")
.WhereIf(true, "1=1")
.Page(1, 10)
.OrderBy("ID DESC")
.ToList<TestClssDto>("ID,Age");
SELECT ID, Age
FROM(select * from TestClass ) a
WHERE(1 = 1)
ORDER BY ID DESC
limit 0,10
通過 WithSql+ ToSQL實現 Union ALL 查詢方法
1、二次 ISelect 查詢:WithSql 使用多次,等於 UNION ALL 查詢
WithSql 使用多次為 UNION ALL 查詢,所以我們可以利用 ISelect.ToSql(FieldAliasOptions.AsProperty) 得到生成的 SQL,如下:
var sql1 = fsql.Select<Topic>()
.Where(a => a.Title.Contains("xxx"))
.ToSql();
var sql2 = fsql.Select<Topic>()
.Where(a => a.Title.Contains("yyy"))
.ToSql();
fsql.Select<Topic>()
.WithSql(sql1)
.WithSql(sql2)
.ToList();
SELECT * from (SELECT a.`Id`, a.`Clicks`, a.`TypeGuid`, a.`Title`, a.`CreateTime`
FROM ( SELECT a.`Id`, a.`Clicks`, a.`TypeGuid`, a.`Title`, a.`CreateTime`
FROM `tb_topic` a
WHERE ((a.`Title`) LIKE '%xxx%') ) a) ftb
UNION ALL
SELECT * from (SELECT a.`Id`, a.`Clicks`, a.`TypeGuid`, a.`Title`, a.`CreateTime`
FROM ( SELECT a.`Id`, a.`Clicks`, a.`TypeGuid`, a.`Title`, a.`CreateTime`
FROM `tb_topic` a
WHERE ((a.`Title`) LIKE '%yyy%') ) a) ftb
2、跨分表查詢:AsTable 相同實體多次操作,等於 Union ALL 查詢
var sql = fsql.Select<User>()
.AsTable((type, oldname) => "table_1")a
.AsTable((type, oldname) => "table_2")
.ToSql(a => a.Id);
select * from (SELECT a."Id" as1 FROM "table_1" a) ftb
UNION ALL
select * from (SELECT a."Id" as1 FROM "table_2" a) ftb
3、利用 ToSql 拼接新的 SQL,使用 IAdo 執行
var sql1 = fsql.Select<Topic>()
.Where(a => a.Id > 100 && a.Id < 200)
.ToSql(a => new { a.Id, a.Title }, FieldAliasOptions.AsProperty);
var sql2 = fsql.Select<Topic>()
.Where(a => a.Id > 1001 && a.Id < 1200)
.ToSql(a => new { a.Id, a.Title }, FieldAliasOptions.AsProperty);
fsql.Ado.CommandFluent($"{sql1} UNION ALL {sql2}")
.ExecuteDataTable();
分頁問題
Union All 之后 如果直接 分頁會有一個問題。請看具體示例
多次WithSql+Page存在問題:每個WithSql內都有一個Page分頁
var sql1 = fsql.Select<Topic>()
.Where(a => a.Title.Contains("xxx"))
.ToSql();
var sql2 = fsql.Select<Topic>()
.Where(a => a.Title.Contains("yyy"))
.ToSql();
fsql.Select<Topic>().WithSql(sql1).WithSql(sql2).Page(1, 20).ToList();
SELECT * from (SELECT a.`Id`, a.`Clicks`, a.`TypeGuid`, a.`Title`, a.`CreateTime`
FROM ( SELECT a.`Id`, a.`Clicks`, a.`TypeGuid`, a.`Title`, a.`CreateTime`
FROM `tb_topic` a
WHERE ((a.`Title`) LIKE '%xxx%') ) a
limit 0,20) ftb
UNION ALL
SELECT * from (SELECT a.`Id`, a.`Clicks`, a.`TypeGuid`, a.`Title`, a.`CreateTime`
FROM ( SELECT a.`Id`, a.`Clicks`, a.`TypeGuid`, a.`Title`, a.`CreateTime`
FROM `tb_topic` a
WHERE ((a.`Title`) LIKE '%yyy%') ) a
limit 0,20) ftb
多個sql union all使用withsql,直接Page分頁,會導致每個子表都生效,子表都生成分頁。
WithSql 可以和 AsTable 實現分表的功能。
分表跨表查詢的時候,分頁是要向每個子表(即每個WithSql中的SQL分頁)都生效。
解決方案
多次withsql,如需分頁,需要按下面的二步操作
- 第一步:通過witsql,將二個sql組成一個sql。
var sql = fsql.Select<Topic>()
.WithSql("SELECT * FROM tb_topic where id > 11")
.WithSql("SELECT * FROM tb_topic where id < 10")
.ToSql("*")
如上生成的UOION ALL的sql
SELECT * from (SELECT *
FROM ( SELECT * FROM tb_topic where id > 11 ) a) ftb
UNION ALL
SELECT * from (SELECT *
FROM ( SELECT * FROM tb_topic where id < 10 ) a) ftb
- 第二步:之后 調用Page則是通過Union ALL后的結果上分頁
var sql2 = g.mysql.Select<Topic>()
.WithSql(sql)
.Page(2, 10)
.ToSql();
SELECT a.`Id`, a.`Clicks`, a.`TypeGuid`, a.`Title`, a.`CreateTime`
FROM ( SELECT * from (SELECT *
FROM ( SELECT * FROM tb_topic where id > 11 ) a) ftb
UNION ALL
SELECT * from (SELECT *
FROM ( SELECT * FROM tb_topic where id < 10 ) a) ftb ) a
limit 10,10