Rafy 框架 - 使用 SqlTree 查詢

本文介紹如何使用 Rafy 框架中的 Sql Tree 查詢:html

 

除了開發者經常使用的 Linq 查詢,Rafy 框架還提供了 Sql 語法樹的方式來進行查詢。git

這種查詢方式下,開發者不須要直接編寫真正的 Sql 語句,而是轉而使用一套中間 Sql 語法樹對象。這隔離了與具體數據庫的耦合,使得開發者編寫的查詢能夠跨越多種不一樣的數據庫運行,甚至能夠在非關係型數據庫中運行。同時,框架還結合託管屬性,提供了方便開發者使用的 API,並儘可能保持與傳統 Sql 相近的語法,使得開發者能夠快速理解並編寫。github

本文包含如下章節:數據庫

  • 快速示例
  • 使用場景
  • 代碼段
  • 更多示例

 

快速示例框架


SqlTree 查詢是直接以一種相似於 Sql 語法的格式,並結合實體託管屬性 IManagedProperty 來進行查詢的查詢模式。以下:性能

[RepositoryQuery]
public virtual ChapterList GetBy(string name, PagingInfo pi)
{
    var f = QueryFactory.Instance;

    var t = f.Table<Chapter>();

    var q = f.Query(
        selection: f.SelectAll(),//查詢全部列
        from: t,//要查詢的實體的表
        where: t.Column(Chapter.NameProperty).Contains(name)//where 條件,
        orderBy: new List<IOrderBy> {//排序
            f.OrderBy(source.Column(Chapter.NameProperty), OrderDirection.Ascending)
        }
    );

    return (ChapterList)this.QueryData(q, pi);
}

能夠看到,SqlTree 語法很是簡單:單元測試

  • 經過 QueryFactory.Instance 類型的單例對象來定義整個 SqlTree 查詢對象。
  • 查詢中使用的是實體類型(Chapter)和實體的託管屬性(Chapter.NameProperty)來定義表和字段。

更多的查詢語法示例,見本節後面的更多示例。學習

 

使用場景測試


當您處於如下場景時,須要使用 SqlTree 查詢:this

  1. Linq 查詢沒法支持的一些場景。
    Linq 查詢目前只支持有限的一些操做符的解析,以及不太複雜的關係的分析。因此當您的查詢較爲複雜,已經沒法使用 Linq 查詢來實現時,能夠考慮使用 SqlTree 查詢。
  2. 須要更精確地控制 Sql 語句。
    若是想要更加精確地控制最終生成的 Sql 語句,也須要使用 SqlTree。
    例如,Linq 查詢中須要兩個實體有確切的實體關係纔會最終生成 Join 語句;可是 SqlTree 則與 Sql 語句無異,開發者能夠隨意將兩個實體對應的表進行 Join 操做。
  3. 須要更好的性能。
    SqlTree 查詢是 Rafy 框架查詢數據(表格、實體)的核心實現。在框架底層,Linq 查詢也都是徹底是基於 SqlTree 查詢來實現的。當開發者在使用 Linq 查詢時,編譯器實際上是生成一組對象來表示一棵表達式樹,而 Rafy 框架會解析這棵樹,生成更加底層的 SqlTree 對象,才交給執行引擎去生成真正的 Sql 語句並最終執行。因此,直接使用 SqlTree 則節約了表達式樹的生成(大量反射與對象)與解析的性能消耗。
    一樣,Rafy 沒有象 Hibernate 框架定義一套新的基於字符串的查詢語法(如 hql),也是由於開發者編寫 hql,不但沒法獲得編譯時的語法支持,並且性能上也須要消耗對 hql 進行解析並生成 SqlTree,不如直接使用更直接的 SqlTree。
    固然,Rafy 在 SqlTree 的基礎上再推出 Linq 查詢的緣由,是由於 SqlTree 自己須要必定的學習週期才能使用,而開發者則更熟悉使用 Linq 語法進行查詢,基本能夠認爲是上手即用,因此支持 Linq 查詢能夠簡化大部分的簡單開發場景。
  4. 但願編寫更通用的查詢。
    倉庫基類 EntityRepository 中自帶的 GetAll、GetById 等方法,都是面向全部實體類型的很是通用的查詢。對於基於 Rafy 的上層框架的開發者而言,除了直接使用這些自帶的通用查詢,不少時候是須要自行編寫一些相似的通用查詢的。
    Linq 的 Labmda 語法中的屬性表達式(e.Name)須要綁定具體的實體類型(Book e),這致使了必須使用反射去生成表達式樹,才能編寫通條蟻。可是,SqlTree 的語法是基於託管屬性框架的,它不須要使用確切的實體屬性表達式,只須要使用託管屬性的運行時對象 IManagedProperty 便可(Book.NameProperty)。這使得開發人員能夠更加方便地編寫通用查詢。例如,倉庫基類 EntityRepository 中的全部查詢方法,都是直接經過使用實體的託管屬性來實現的,例如:GetById、GetByParentId、GetAll 等。
  5. 能夠爲擴展屬性編寫查詢。
    因爲擴展屬性寫在額外的程序集插件中的,因此當沒法經過 Linq 表達式進行查詢。這時就不得不經過託管屬性 IManagedProperty 來定義 SqlTree 完成查詢了。
    關於擴展屬性,參見:擴展屬性
  6. 支持多個數據庫。
    上述的場景中,其實還能夠直接編寫 Sql 語句來進行查詢。可是這樣就很難保證開發者編寫的 Sql 語句可以在多個數據庫上可以正確運行。
  7. 查詢須要支持倉庫數據層的擴展點。
    因爲 Rafy 的查詢核心都是基於 SqlTree 來實現的,因此內部的全部擴展點都是要依賴 SqlTree的。若是開發者直接編寫 Sql 語句來查詢,那麼這些許多的擴展點都將無效,沒法對開發者編寫的這條 Sql 語句進行擴展。
    例如:當使用 幽靈插件 對全部幽靈數據進行自動過濾時,若是開發者使用手工編寫的 Sql 語法進行查詢,那麼自動過濾功能無效,須要開發者本身進行幽靈數據的過濾。

 

代碼段


RafySDK 中提供了兩個代碼段,來輔助開發者生成基本的 SqlTree 查詢結構:Rafy_Query、Rafy_Query_TableQueryContent。

詳情見:代碼段

 

更多示例


下面將會列出一些常見的 SqlTree 查詢示例。經過這些代碼,您將學習到如何在各類查詢需求下使用 SqlTree。

 

基礎查詢:

[RepositoryQuery]
public virtual ChapterList GetBy(string name, PagingInfo pi)
{
    var f = QueryFactory.Instance;

    var t = f.Table<Chapter>();

    var q = f.Query(
        //selection: f.SelectAll(),//沒有 selection,則默認表示查詢全部列
        from: t,//要查詢的實體的表
        where: t.Column(Chapter.NameProperty).Contains(name)//where 條件
    );

    return (ChapterList)this.QueryData(q, pi);
}

 

表格數據查詢:

[RepositoryQuery]
public virtual LiteDataTable GetBy(string name, PagingInfo pi)
{
    var f = QueryFactory.Instance;

    var t = f.Table<Chapter>();

    var q = f.Query(
        from: t,
        where: t.Column(Chapter.NameProperty).Contains(name)
    );

    return this.QueryTable(q, pi);//由查詢實體變爲查詢數據表格,只是更換了這一行代碼。
}


兩個列的條件進行比較:
var table = f.Table(this);//使用當前的倉庫來表示當前的表
var q = f.Query(
    from :table,
    where: table.Column(Chapter.NameProperty).Equal(table.Column(Chapter.CodeProperty))//兩個列相等
);


使用 And、Or:
var table = f.Table(this);
var q = f.Query(
    from :table,
    where: f.And(
        table.Column(Chapter.NameProperty).Equal(name),
        f.Or(
            table.Column(Chapter.IdProperty).LessEqual(10),
            table.Column(Chapter.IdProperty).GreaterEqual(1000)
        )
    )
);

 

Join(SerialNumberValueRepository 中的真實代碼):

/// <summary>
/// 獲取某個規則下最新的一個值。
/// </summary>
/// <param name="autoCodeName"></param>
/// <returns></returns>
[RepositoryQuery]
public virtual SerialNumberValue GetLastValue(string autoCodeName)
{
    var f = QueryFactory.Instance;
    var t = f.Table<SerialNumberValue>();
    var t2 = f.Table<SerialNumberInfo>();
    var q = f.Query(
        from: t.Join(t2),//因爲 SerialNumberValue 有一個 SerialNumberInfo 的引用屬性,則在使用 Join 時,不須要給出 Join 的條件。
        where: t2.Column(SerialNumberInfo.NameProperty).Equal(autoCodeName),
        orderBy: new List<IOrderBy> { f.OrderBy(t.Column(SerialNumberValue.LastUpdatedTimeProperty), OrderDirection.Descending) }
    );

    return (SerialNumberValue)this.QueryData(q);
}

 

使用完整的 Join:

var t = f.Table<SerialNumberValue>();
var t2 = f.Table<SerialNumberInfo>();
var q = f.Query(
    from: t.Join(t2, t.Column(SerialNumberValue.SerialNumberInfoIdProperty).Equal(t2.Column(SerialNumberInfo.IdProperty)), JoinType.Inner),//不但能夠給出具體的 Join 條件,還能夠給出 Join 類型。
    where: t2.Column(SerialNumberInfo.NameProperty).Equal(autoCodeName),
    orderBy: new List<IOrderBy> { f.OrderBy(t.Column(SerialNumberValue.LastUpdatedTimeProperty), OrderDirection.Descending) }
);

 

Exists:

var bookTable = f.Table(this);
var chapterTable = f.Table<Chapter>();
var q = f.Query(
    from: bookTable,
    where: f.Exists(f.Query(
        from: chapterTable,
        where: chapterTable.Column(Chapter.BookIdProperty).Equal(bookTable.IdColumn)
    ))
);

 

Not Exists:

var book = f.Table(this);
var chapter = f.Table<Chapter>();
var q = f.Query(
    from: book,
    where: f.Not(f.Exists(f.Query(
        from: chapter,
        where: f.And(
            f.Constraint(chapter.Column(Chapter.BookIdProperty), book.IdColumn),
            f.Constraint(chapter.Column(Chapter.NameProperty), PropertyOperator.NotEqual, chapterName)
        )
    )))
);

 

更多示例,請參照源碼中單元測試的 ORMTest 中的 TableQuery 相關方法。

 

PS:該文已經歸入《 Rafy 用戶手冊》中。

相關文章
相關標籤/搜索