.NET ORM 的 “SOD蜜”--零基礎入門篇


PDF.NET SOD框架不僅僅是一個ORM,但是它的ORM功能是獨具特色的,我在博客中已經多次介紹,但都是原理性的,可能不少初學的朋友還是覺得復雜,其實,SOD的ORM是很簡單的。下面我們就采用流行的 Code First的方式,一步步來了解下。

注意:也可以參考筆者撰寫的新書:《SOD框架企業級應用數據架構實戰》 來了解本篇文章的內容。

一、准備工作

1.1,添加SOD包引用

首先建立一個控制台項目(支持.NET2.0的項目),並使用程序包管理器添加PDF.NET SOD的程序引用:

PM> Install-Package PDF.NET.SOD

更多詳細使用信息說明,請參考nuget 網站說明 https://www.nuget.org/packages/PDF.NET/

1.2,配置數據連接

新建一個控制台項目,添加一個應用程序配置文件,增加一個數據庫連接配置:

<?xml version="1.0" encoding="utf-8" ?> <configuration> <connectionStrings> <add name="local" connectionString="Data Source=.;Initial Catalog=LocalDB;Integrated Security=True" providerName="SqlServer" /> </connectionStrings> </configuration>

上面的連接字符串要求你本地已經安裝SqlServer,框架兼容2000以上所有版本,有一個數據庫名字是 LocalDB。當然你也可以修改成你實際的連接字符串。

之后,我們的查詢示例,都將采用這個連接配置。

注意:最新版本的SOD框架,如果使用的是SqlServer,並且連接字符串指定了數據庫名字但實際上沒有這個數據庫,框架可以自動創建數據庫,此功能需要SOD的Code First功能支持,請參考下面“1.5 ,創建數據庫表”的功能。

SOD框架最基本的配置,僅需要這一個地方,這比起EF來說要簡單。

如果是SqlServer +EF Code First方式的連接配置,SOD框架也可以使用它這個連接字符串的。

1.3,定義實體類

我們將使用最常見的用戶登錄來舉例,這里需要一個用戶實體類,假設它的定義是下面這個樣子的:

public class User : EntityBase
    {
        public User()
        {
            TableName="Tb_User";
            IdentityName = "UserID";
            PrimaryKeys.Add("UserID");
        }

        public int ID
        {
            get { return getProperty<int>("UserID"); }
            set { setProperty("UserID", value); }
        }

        public string Name
        {
            get { return getProperty<string>("Name"); }
            set { setProperty("Name", value, 50); }
        }

        public string Pwd
        {
            get { return getProperty<string>("Pwd"); }
            set { setProperty("Pwd", value, 50); }
        }

        public DateTime RegistedDate
        {
            get { return getProperty<DateTime>("RegistedDate"); }
            set { setProperty("RegistedDate", value); }
        }

    }
View Code

在上面的定義中,構造函數內指明了 IdentityName = "UserID";這表示當前實體類對應的表有一個叫做 UserID的自增列,每當插入實體類后,可以通過該自增列對應的屬性獲取到新插入的自增ID的值。

另外,我們發現每個實體類的屬性中,都是這樣的寫法:

    public int ID
        {
            get { return getProperty<int>("UserID"); }
            set { setProperty("UserID", value); }
        }

屬性內 getProperty ,setProperty 方法內的第一個參數,就是屬性對應的字段名字。
所以,這樣的屬性,SOD框架稱為“持久化屬性”。

可以看到,SOD實體類還是比較簡單的,它沒有使用特性來申明數據庫信息,這意味着你可以在運行時修改實體類影射的主鍵,自增字段,表名稱等數據庫元數據,並且不需要反射,這些特性構成了SOD框架簡單而強大的基礎。

1.4,添加查詢對象的數據上下文

在項目中添加一個 LocalDbContext.cs 文件,文件中添加如下代碼,以便檢查表 Tb_User 是否存在,如果不存在,則自動創建一個:

/// <summary>
 /// 用來測試的本地SqlServer 數據庫上下文類
 /// </summary>
  public class LocalDbContext : DbContext
  {
      public LocalDbContext()
          : base("local") 
      {
          //local 是連接字符串名字
      }

      #region 父類抽象方法的實現

      protected override bool CheckAllTableExists()
      {
          //創建用戶表
          CheckTableExists<User>();
          return true;
      }

      #endregion
   }

本例中使用SqlServer 作為查詢示例,程序會自動使用 SqlServerDbContext 密封類來進行內部協作處理,但這取決於你的AdoHelper實例類型,如果是Oracle,則內部使用的是OracleDbContext 密封類。

如果你不需要ORM的 Code First功能,這個 DbContext 實現類是不需要寫的。

1.5,創建數據庫表

OK,基本准備就緒,現在Main 方法里面可以寫下面的代碼開始工作了:

LocalDbContext context = new LocalDbContext();//自動創建表

准備工作全部完成,運行這一句代碼,之后查看SqlServer管理工具,發現表 Tb_User 已經創建了。

二、ORM之增,刪,改

SOD框架的ORM功能跟通常的ORM框架不同,SOD框架的實體類上並沒有數據查詢和持久化的方法,所以SOD的實體類是“非常純粹的”實體類,你可以把它看作是一個數據容器,或者用來當作DTO,ViewModel使用,有關這個話題更詳細的闡述,請看這篇文章:《DataSet的靈活,實體類的方便,DTO的效率:SOD框架的數據容器,打造最適合DDD的ORM框架》。

在進行真正的數據查詢之前,得先有數據,所以我們先測試數據的增刪改。

2.1,刪除數據

首先刪除之前的測試數據,可以采用OQL進行批量數據刪除:

int count = 0;

//刪除 測試數據-----------------------------------------------------
User user = new User();
OQL deleteQ = OQL.From(user)
                .Delete()
                .Where(cmp => cmp.Comparer(user.ID, ">", 0)) //為了安全,不帶Where條件是不會全部刪除數據的
             .END;
count= EntityQuery<User>.ExecuteOql(deleteQ, context.CurrentDataBase);

這里將用戶ID 大於0的數據全部刪除了,框架內置了數據安全機制,不會隨意刪除全部數據,所以為了清除全部數據,采用了上面的方法。

2.2,增加數據

方式1,采用DbContext 的Add 方法:

            count = 0;
            User zhang_san = new User() { Name = "zhang san", Pwd = "123" };
            count += context.Add<User>(zhang_san);//采用 DbContext 方式插入數據

方式2,采用OQL:

            User li_si = new User() { Name = "li si", Pwd = "123" };
            OQL insertQ= OQL.From(li_si)
                            .Insert(li_si.Name);//僅僅插入用戶名,不插入密碼

OQL還需EntityQuery 對象配合,才可以執行,繼續看下面的代碼:

 AdoHelper db = MyDB.GetDBHelperByConnectionName("local");
 EntityQuery<User> userQuery = new EntityQuery<User>(db);
 count += userQuery.ExecuteOql(insertQ);

下面是結果圖:

方式3,直接EntityQuery 插入實體類:

 User zhang_yeye = new User() { Name = "zhang yeye", Pwd = "456" };
 count += EntityQuery<User>.Instance.Insert(zhang_yeye);//采用泛型 EntityQuery 方式插入數據

2.3,修改數據:

方式1,采用 DbContext 方式更新數據

 li_si.Pwd = "123123";
 count = context.Update<User>(li_si);//采用 DbContext 方式更新數據

方式2,采用OQL方式更新指定的數據

 li_si.Pwd = "123456";
OQL updateQ= OQL.From(li_si)
                .Update(li_si.Pwd) //僅僅更新密碼
             .END;
count += EntityQuery<User>.Instance.ExecuteOql(updateQ);//采用OQL方式更新指定的數據

方式3,采用泛型 EntityQuery 方式修改數據

  li_si.Pwd = "888888";
  count += EntityQuery<User>.Instance.Update(li_si);//采用泛型 EntityQuery 方式修改數據

 三、ORM之數據查詢

前面增刪改數據完成了,現在有了測試數據,我們可以來進行ORM的數據查詢測試了,這里使用用戶登錄的例子來測試,框架提供了6種數據查詢方式。

 3.1,最簡單的方法

假設前端直接傳遞了一個 User 實體類對象,中間設置了用戶名和密碼,現在有一個登錄方法使用該對象,該方法詳細內容如下所示:

       /// <summary>
        /// 使用用戶對象來登錄,OQL最簡單最常見的使用方式
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public bool Login(User user)
        {
            OQL q = OQL.From(user)
                .Select()
                .Where(user.Name, user.Pwd) //以用戶名和密碼來驗證登錄
            .END;

            User dbUser =q.ToEntity<User>();//ToEntity,OQL擴展方法 
            return dbUser != null; //查詢到用戶實體類,表示登錄成功
        }

這里我們使用了SOD框架的ORM查詢語言--OQL,它的結構非常類似於SQL,你可以認為OQL就是對象化的SQL語句。

OQL表達式采用 .From.....END 的語法,對象的鏈式方法調用,只要敲點出來的就是正確的,這樣沒有SQL基礎的同學也可以很快掌握該查詢語法,也能馬上作數據開發。

注意:在本例中,使用了OQL的擴展方法,因此需要引用下面的名字空間:

using PWMIS.Core.Extensions;

如果不使用擴展方法,可以采用泛型EntityQuery 的方法,請看下面的示例。

3.2,使用 OQLCompare 對象的 EqualValue 相等比較方式

        /// <summary>
        /// 使用用戶對象來登錄,但是使用 OQLCompare 對象的 EqualValue 相等比較方式 
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public bool Login1(User user)
        {
            OQL q = OQL.From(user)
                 .Select(user.ID) //僅查詢一個屬性字段 ID
                 .Where(cmp => cmp.EqualValue(user.Name) & cmp.EqualValue(user.Pwd))
              .END;

            User dbUser = EntityQuery<User>.QueryObject(q);
            return dbUser != null; //查詢到用戶實體類,表示登錄成功
        }

跟例1一樣,這里也要求user 對象的Name和Pwd屬性必須事先有值。本例沒有使用OQL的擴展方法。

3.3, EntityQuery 泛型查詢方法

本例只是對例子1做了下改進,重點在於登錄方法的參數不是用戶對象,而是名字和密碼參數。

       /// <summary>
        /// 使用用戶名密碼參數來登錄,采用 EntityQuery 泛型查詢方法
        /// </summary>
        /// <param name="name"></param>
        /// <param name="pwd"></param>
        /// <returns></returns>
        public bool Login2(string name, string pwd)
        {
            User user = new User()
            {
                Name = name,
                Pwd = pwd
            };

            OQL q = OQL.From(user)
                .Select(user.ID)
                .Where(user.Name, user.Pwd)
            .END;
            User dbUser = EntityQuery<User>.QueryObject(q);

            return dbUser != null; //查詢到用戶實體類,表示登錄成功
        }

3.4,使用OQLConditon 對象為查詢條件

       /// <summary>
        /// 使用用戶名密碼參數來登錄,使用早期的實例化OQL對象的方式,並使用OQLConditon 對象為查詢條件
        /// </summary>
        /// <param name="name"></param>
        /// <param name="pwd"></param>
        /// <returns></returns>
        public bool Login3(string name, string pwd)
        {
            User user = new User();
            OQL q = new OQL(user);
            q.Select(user.ID).Where(q.Condition.AND(user.Name, "=", name).AND(user.Pwd, "=", pwd));

            User dbUser = EntityQuery<User>.QueryObject(q);
            return dbUser != null; //查詢到用戶實體類,表示登錄成功
        }

這是OQL早期的條件查詢方式,缺點是沒法構造復雜的查詢條件。

 3.5,操作符重載

OQLCompare 的操作符重載可以簡化比較條件,如下所示:

       /// <summary>
        /// 使用用戶名密碼參數來登錄,並使用操作符重載的查詢條件比較方式
        /// </summary>
        /// <param name="name"></param>
        /// <param name="pwd"></param>
        /// <returns></returns>
        public bool Login4(string name, string pwd)
        {
            User user = new User();

            OQL q = OQL.From(user)
                  .Select()
                  .Where( cmp => cmp.Property(user.Name) == name 
                               & cmp.Property(user.Pwd)  == pwd  )
               .END;

            User dbUser = EntityQuery<User>.QueryObject(q);
            return dbUser != null; //查詢到用戶實體類,表示登錄成功
        }

 3.6,使用泛型OQL查詢(GOQL)

使用泛型OQL查詢(GOQL),對於單實體類查詢最簡單的使用方式,缺點是不能進行“連表查詢”,即多個實體類聯合查詢。

        /// <summary>
        /// 使用用戶名密碼參數來登錄,使用泛型OQL查詢(GOQL),對於單實體類查詢最簡單的使用方式。
        /// </summary>
        /// <param name="name"></param>
        /// <param name="pwd"></param>
        /// <returns></returns>
        public bool Login5(string name, string pwd)
        {
            User dbUser = OQL.From<User>()
                 .Select()
                 .Where((cmp, user) => cmp.Property(user.Name) == name 
                                     & cmp.Property(user.Pwd)  == pwd  )
            .END
            .ToObject();

            return dbUser != null; //查詢到用戶實體類,表示登錄成功
        }


3.7,使用實體類主鍵來查詢

SOD實體類的“主鍵”字段是可以修改的,這樣你可以隨時修改它,就像實體類本來的主鍵一樣,用它來填充數據,本例就是判斷是否填充成功當前實體類來判斷用戶是否可以登錄。

       /// <summary>
        /// 使用用戶名密碼參數來登錄,但是根據實體類的主鍵來填充實體類並判斷是否成功。
        /// </summary>
        /// <param name="name"></param>
        /// <param name="pwd"></param>
        /// <returns></returns>
        public bool Login6(string name, string pwd)
        {
            User user = new User();
            user.PrimaryKeys.Clear();
            user.PrimaryKeys.Add("Name");
            user.PrimaryKeys.Add("Pwd");

            user.Name = name;
            user.Pwd = pwd;
            bool result= EntityQuery<User>.Fill(user);//靜態方法,使用默認的連接對象
            return result;
        }


3.8,查詢多條數據

前面的例子都只是查詢一條數據,如果需要查詢多條數據也很簡單,參見下面的例子,如何查詢所有姓 zhang 的用戶:

        /// <summary>
        /// 模糊查詢用戶,返回用戶列表,使用OQLCompare 委托
        /// </summary>
        /// <param name="likeName">要匹配的用戶名</param>
        /// <returns>用戶列表</returns>
        public List<User> FuzzyQueryUser(string likeName)
        {
            User user = new User();
            OQL q = OQL.From(user)
              .Select()
              .Where(cmp => cmp.Comparer(user.Name, "like", likeName+"%") )
            .END;

            List<User> users = EntityQuery<User>.QueryList(q);
            return users;
        }

前端調用:

//查詢列表
var users=service.FuzzyQueryUser("zhang");
Console.WriteLine("模糊查詢姓 張 的用戶,數量:{0}",users.Count );

所以,查詢多條數據,僅需要使用泛型 EntityQuery對象的QueryList 方法即可。同樣,框架也為你提供了OQL對象擴展方法來直接查詢列表數據。

3.9,實體類聯合查詢

這里不再舉例,我的博客文章也多次說明過,請參考下面這個系列文章:

ORM查詢語言(OQL)簡介--高級篇(續):廬山真貌 深藍醫生 2013-07-30 16:54 閱讀:4497 評論:41  
 
ORM查詢語言(OQL)簡介--高級篇:脫胎換骨 深藍醫生 2013-07-26 17:26 閱讀:3274 評論:28  
 
ORM查詢語言(OQL)簡介--實例篇 深藍醫生 2013-04-01 14:56 閱讀:5108 評論:16  
 
ORM查詢語言(OQL)簡介--概念篇 深藍醫生 2012-10-06 00:58 閱讀:4657 評論:25  
 

四、SOD框架的設計原則

 SOD框架的整個設計都采用了“條條道路通羅馬”的原則,即多模式解決方案,對數據的開發不管是SQL-MAP ,ORM,Data Control 哪種模式都可以“殊途同歸”,對於OQL,這個也得到了充分的體現,比如上面的用戶登錄功能,使用了7 種方式來實現,實際上還有3中查詢方式本篇文章沒有做成說明;而對於實體類的增,刪,改,分別又提供了DbContext,OQL,泛型EntityQuery 等多種方式。

所以,SOD框架的使用非常靈活,你可以根據你的偏好,習慣,環境,來靈活使用,而且也容易擴展,因此,相對於EF這樣的ORM框架來,SOD框架的ORM功能沒有任何束縛,它自由,靈活,而且輕量,容易擴展,但不妨礙它的強大,比如對於分表分庫的查詢,數據的批量更新插入修改,數據庫鎖的直接支持等這些“企業級”數據開發需求的支持。

五、相關資源

本篇文章的源碼,已經在框架的源碼里面。

有關框架更詳細而完整的入門指引,請參考下面這篇文章:

PDF.NET SOD 開源框架紅包派送活動 && 新手快速入門指引

更多完整而詳細的信息,請看框架官網地址:

http://www.pwmis.com/sqlmap

或者參考筆者撰寫的新書:《SOD框架企業級應用數據架構實戰

框架已經完全開源,參看這篇文章:

一年之計在於春,2015開篇:PDF.NET SOD Ver 5.1完全開源

另外,  網友 廣州-銀古 寫了一篇《SOD讓你的舊代碼煥發青春》,講述了如何改造老式僵化項目的過程,推薦大家看看。

六、最新源碼資源地址


CodePlex online : http://pwmis.codeplex.com/SourceControl/latest [注意:已經過時]

CodePlex SVN : https://pwmis.codeplex.com/svn [注意:已經過時]

GitHub : https://github.com/znlgis/SOD

OSChina : https://gitee.com/znlgis/sod

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM