上一期博客末尾給出了一個靜態類,四個靜態方法。這有什么用呢?在繼續進行其他內容前,我們先利用這些靜態方法為數據庫生成基礎數據。按照ADO.NET的常規方法,需要先建立連接,創建DataSet或DataReader對象,構造SQL語句,然后執行SQLCommand命令進行作業。現在有了EF,這些步驟全部省掉,EF的內部已經自動實現了這些步驟,我們所要做的只是去調用剛剛定義的幾個方法。
在項目上新建一個文件夾,命名為Presenter,用來包含一些與用戶交互操作的方法,以便Main函數進行調用。在Presenter文件中下新建一個類,命名為AddDataView.cs,該類就包含一個方法,用以向數據內插入數據:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using ProductEFDemo.Business; namespace ProductEFDemo.Presenter { public static class AddDataView { public static void AddAllData() { try { //添加單位數據 InsertData.AddProudctUnit(); Console.WriteLine("產品單位測試數據插入成功!"); //添加產品大類數據 InsertData.AddProductBigType(); Console.WriteLine("產品大類測試數據插入成功!"); //添加產品小類數據 foreach (var productBigType in SearchData.GetAllProductBigType()) { InsertData.AddProductSmallType(productBigType.ProductBigTypeId); } Console.WriteLine("產品小類測試數據插入成功!"); //添加產品數據 foreach (var productSmallType in SearchData.GetAllProductSmallType()) { foreach (var productUnit in SearchData.GetAllProductUnit()) { InsertData.AddProduct(productUnit.ProductUnitId, productSmallType.ProductSmallTypeId); } } Console.WriteLine("添加產品測試數據成功!"); } catch (System.Exception ex) { Console.WriteLine(String.Format("發生錯誤,錯誤信息為:{0}",ex.Message) ; } } } }
該類中調用的三個方法:GetAllProductBigType(),GetAllProductUnit(),GetAllProductSmallType()代碼如下,由於這些是查詢的內容,相關信息后面再詳細介紹。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using ProductEFDemo.Models; namespace ProductEFDemo.Business { public static class SearchData { public static List<ProductBigType> GetAllProductBigType() { using (var ctx = new ProductsEntities()) { return ctx.ProductBigType.ToList(); } } public static List<ProductUnit> GetAllProductUnit() { using (var ctx = new ProductsEntities()) { return ctx.ProductUnit.ToList(); } } public static List<ProductSmallType> GetAllProductSmallType() { using (var ctx = new ProductsEntities()) { return ctx.ProductSmallType.ToList(); } } } }
代碼沒有什么可介紹的,注意有關命名空間的引用。
現在開始調用,在項目的Program.cs的Main方法里調用剛剛定義的AddAllData方法:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using ProductEFDemo.Presenter; namespace ProductEFDemo { class Program { static void Main(string[] args) { AddDataView.AddAllData(); Console.ReadKey(); } } }
注意引用ProductEFDemo.Presenter命名空間。
生成並運行程序,
打開SqlSever Management Studio,展開表,發現數據已經全部插入了:
你猜猜使用上述代碼一共生成了多少條Product數據?到目前為止,一句SQL代碼都沒有寫,寫的代碼都是純粹的C#代碼,而且還有額外的IDE代碼提示,是不是很爽?強烈建議不要拷貝代碼,而是一行行輸入,這樣才能感受到書寫代碼的快樂。
5、數據查詢
現在有了數據,就可以隨心所欲地進行查詢了。這里先對前面調用的GetAllProductBigType(),GetAllProductUnit(),GetAllProductSmallType()作一個解釋:
前面三個方法是獲取集合性質數據的典型方法,使用的是ToList()擴展方法(關於擴展方法請自已去MSDN查去,我覺得內置的擴展方法就夠用了,暫時還不想去自定義!),就可以將實體類的數據進行序列化,成為List范型類,List范型類很好用,可以直接綁定為很多集合類控件的數據源,不管你是用WinForm,還是.Net MVC或是WPF,SIlverlight等,都會廣泛地用到,但這超出了我想表達內容的范圍,就不贅述了。下面就Linq查詢作一介紹。原來我對Linq很抵觸,在《C#入門經典》讀到這一章的時候,我選擇的是跳過,不去看它。后來有時間再翻閱書的時候,驚訝地發現我錯過了多么美麗的風景!太簡潔,太優雅了!真心感謝微軟,推出了這么好用的工具和語法。。。好了,不吹捧微軟了,我們還是先來了解一下Linq的基本知識:
所有的的Linq都是引用自System.Linq名稱空間的類,因此在使用前要檢查是否引用該名稱空間,實際上VS在創建類的模板里已經默認添加了這個引用,如果沒有請自行添加。
Linq用於查詢數據的語法有兩種,一種稱為查詢語法,有點類似於Sql語句,與Sql語句不同的地方是from在前,select在后,from,select,where,distinct,join(注意全部為小寫)現在已經成為C#的關鍵字,專門用於Linq查詢,這種方法一般是首選方法,因為它一般更容易理解,但是可惜的是,Linq的全部功能不能由查詢語法實現(比如前面的ToList方法在LInq里沒有對應關鍵字),需要借助於另一種語法,稱為方法語法,相比而言,方法語法使用起來稍微難理解一點,但是方法語法的優點是比較簡潔,代碼更優雅。下面兩種方法都介紹,可以先用查詢語法,熟練以后可以試試方法語法,其實也挺好的!
1)簡單查詢:場景是這樣的,需要從ProductBigType類中查詢出包含數字“7”的記錄,可以這樣寫:
using(var ctx=new ProductsEntities()) { var result=from c in ctx.ProductBigType where c.ProductBigTypeName.Contains("7") select c; return result.ToList(); }
如果采用方法語法則可以簡化為(為代碼簡潔起見,后面不再引用using字句):
return ctx.ProductBigType.Where(c => c.ProductBigTypeName.Contains("7")).ToList();
返回的記錄是一樣的,只不過方法更優雅;
2)數據排序
查詢語法使用orderby關鍵字,如果采用降序,可以添加descending關鍵字;下面示例的場景是這樣的,需要從查詢出的所有的ProductBigType中查詢出首個單詞為“產品”的記錄(其實就是全部記錄),按降序排序,代碼如下:
查詢語法:
var result=from c in ctx.ProductBigType where c.ProductBigTypeName .StartsWith("產品") orderby c.ProductBigTypeName descending select c; return result.ToList();
方法語法:
return ctx.ProductBigType.Where(c => c.ProductBigTypeName.StartsWith("產品")).OrderByDescending(c => c.ProductBigTypeName).ToList();
方法語法這種長串的語法稱為鏈式語句,意思像一條長鏈一樣,這是因為所用的方法是擴展方法,都具有相同的類型(實現了IQueryable或IOrderable范型接口),所以可以連接起來;
3)聚合運算符:用於統計數據,與SQL語句中的相關聚合運算符類似,不同之處這些運算符都是擴展方法,首字母必須大寫,常用的聚合擴展方法如下:
Count(),表示結果的個數;
Min(),表示結果中的最小值
Max(),表示最大值;
Average(),表示數據結果的平均值;
Sum(),表示求和;
現在我們想要查詢出Product表中ProductBaseValue中最大的數據,就可以這樣寫:
public decimal MaxProductBasePrice() { using (var ctx = new ProductsEntities()) { var result= from c in ctx.Product select c.ProductBasePrice; return result.Max(); } }
或者采用方法語法:
return ctx.Product.Max(c => c.ProductBasePrice);
4)使用導航屬性:
昨天博客發表以后,有園友問為什么要用Entity Framework4.x POCO Generator,除了節省力氣以外,很重要的一個原因是自已手工寫的類經常會忘記導航屬性,導致在使用Linq無法導航到所需要的數據,不得不借助join來完成。實際上,Sql的內聯Join關鍵字完全可以少用或者不用(雖然Linq同樣提供了jion關鍵字和Join擴展方法),而采用一個新擴展方法Include來解決內聯的問題,可以順利地完成導航。在使用導航屬性進行內聯接之前,我們先來看看POCO生成的類:
public partial class Product { #region Primitive Properties public virtual System.Guid ProductId { get; set; } public virtual string ProductName { get; set; } public virtual System.Guid ProductSmallTypeId { get { return _productSmallTypeId; } set { if (_productSmallTypeId != value) { if (ProductSmallType != null && ProductSmallType.ProductSmallTypeId != value) { ProductSmallType = null; } _productSmallTypeId = value; } } } private System.Guid _productSmallTypeId; public virtual System.Guid ProductUnitId { get { return _productUnitId; } set { if (_productUnitId != value) { if (ProductUnit != null && ProductUnit.ProductUnitId != value) { ProductUnit = null; } _productUnitId = value; } } } private System.Guid _productUnitId; public virtual decimal ProductBasePrice { get; set; } public virtual decimal ProductLosePrice { get; set; } #endregion #region Navigation Properties public virtual ProductSmallType ProductSmallType { get { return _productSmallType; } set { if (!ReferenceEquals(_productSmallType, value)) { var previousValue = _productSmallType; _productSmallType = value; FixupProductSmallType(previousValue); } } } private ProductSmallType _productSmallType; public virtual ProductUnit ProductUnit { get { return _productUnit; } set { if (!ReferenceEquals(_productUnit, value)) { var previousValue = _productUnit; _productUnit = value; FixupProductUnit(previousValue); } } } private ProductUnit _productUnit; #endregion #region Association Fixup private void FixupProductSmallType(ProductSmallType previousValue) { if (previousValue != null && previousValue.Product.Contains(this)) { previousValue.Product.Remove(this); } if (ProductSmallType != null) { if (!ProductSmallType.Product.Contains(this)) { ProductSmallType.Product.Add(this); } if (ProductSmallTypeId != ProductSmallType.ProductSmallTypeId) { ProductSmallTypeId = ProductSmallType.ProductSmallTypeId; } } } private void FixupProductUnit(ProductUnit previousValue) { if (previousValue != null && previousValue.Product.Contains(this)) { previousValue.Product.Remove(this); } if (ProductUnit != null) { if (!ProductUnit.Product.Contains(this)) { ProductUnit.Product.Add(this); } if (ProductUnitId != ProductUnit.ProductUnitId) { ProductUnitId = ProductUnit.ProductUnitId; } } } #endregion }
其中ProductUnit和ProductSmallType兩個屬性就是導航屬性,這兩個屬性就可以在實際使用中進行內連接;
比如我們想在控制台中輸出若干條Product記錄(這里的搜索條件為ProductBasePrice介於49-50之間),要求顯示格式為:產品名稱:XXXX;產品大類:XXX;產品小類:XXX;單位:XXX;基本價格:XXX;
就需要通過內聯從其他表中獲取數據,就要用到前面所說的導航屬性。注意到Product表只導航到了ProductSmallType,還需要借助ProductSmallType類導航到ProductBigType。(如果您不明白,還請再回頭看有關數據庫表的說明)。
這樣代碼就可以這樣寫:
public static List<Product> GetProductByPrice() { using (var ctx = new ProductsEntities()) { return ctx.Product.Include("ProductUnit"). Include("ProductSmallType").Include("ProductSmallType.ProductBigType") .Where(c => (c.ProductBasePrice <= 50 && c.ProductBasePrice >= 49)).ToList(); } }
為了實現前面所說的輸出,需要在Presenter文件夾中新建一個類,類中有一個方法對相關數據進行處理:
public static class ProductDeatilView { public static void ShowProductDeatil() { foreach (var product in SearchData.GetProductByPrice()) { Console.WriteLine(String.Format("產品名稱:{0};產品大類:{1};產品小類:{2};單位:{3};基本價格:{4};", product.ProductName, product.ProductSmallType.ProductBigType.ProductBigTypeName, product.ProductSmallType.ProductSmallTypeName, product.ProductUnit.ProductUnitName, product.ProductBasePrice)); } } }
ShowProductDeatile方法只有一個語句,在調用時就使用了Product類的導航屬性,只要你想,可以導航到任何數據,但前提是已經將數據都包容進來了。
現在在Main方法中調用ShowProductDeatile方法,輸出結果如下:
這個例子使用查詢語法也簡單不了多少,因為並沒有include關鍵字可用,也必須要使用Include擴展方法。在此就不舉例了。需要注意的是關於Include擴展方法的參數問題,這個參數是導航的路徑,必須是一個字母不錯的導航屬性名,否則會因為查詢不到數據而引發異常。如果需要通過導航屬性再導航到其他相關的導航屬性上,則需要使用點式語法,如上例中對ProductBigType導航屬性的引用,就需要使用Include("ProductSmallType.ProductBigType")這樣的語法;你也可以試試去掉某一個Include語句,試試會發生什么問題。
這個例子還有一種相對麻煩的實現方法,就是新建一個ProductDepository類,將需要展示出的字段放進去,然后使用Linq的join關鍵字來進行查詢,新建的ProductDepository類:
public class ProductDepository { public string ProductName { get; set; } public string ProductBigTypeName { get; set; } public string ProductSmallTypeName { get; set; } public string ProductUntName { get; set; } public decimal ProductBasePrice { get; set; } }
而查詢語句如下:
public static List<ProductDepository> GetProductDepositoryByPrice() { using (var ctx = new ProductsEntities()) { var result = from c in ctx.Product join d in ctx.ProductSmallType on c.ProductSmallTypeId equals d.ProductSmallTypeId join e in ctx.ProductBigType on d.ProductBigTypeId equals e.ProductBigTypeId join f in ctx.ProductUnit on c.ProductUnitId equals f.ProductUnitId where c.ProductBasePrice<=50 && c.ProductBasePrice >=49 select new ProductDepository { ProductName = c.ProductName, ProductBigTypeName = e.ProductBigTypeName, ProductSmallTypeName = d.ProductSmallTypeName, ProductUntName = f.ProductUnitName }; return result.ToList(); } }
調用數據的方法則變為:
public static void ShowProductDeatil() { foreach (var productDepository in SearchData.GetProductDepositoryByPrice()) { Console.WriteLine(String.Format("產品名稱:{0};產品大類:{1};產品小類:{2};單位:{3};基本價格:{4};", productDepository.ProductName, productDepository.ProductBigTypeName, productDepository.ProductSmallTypeName, productDepository.ProductUntName, productDepository.ProductBasePrice)); } }
輸出的效果是一樣的。這種方法雖然比較麻煩,但是當類的關第比較復雜,需要重新梳理時,這不失為一種有效的解決方案,由於類可以無限制擴展,在進行類似WPF或SilverLIght的MVVM模式開發中,可以很方便地將模型與類本身進行分離。但是一般來說,使用導航屬性方便的時候,大可不必這么大動干戈,具體怎樣取舍,取決於程序的復雜程度。
另外,查詢語法的select字句不支持多個字段的語法,因此要想使用多個字段,就必須在查詢中創建新對象,可以顯示地創建類,也可以創建無名稱的對象,術語將這種情況稱為投影(projection);使用方法語法也可以進行實現投影,代碼如下:
var result=product.Select(c=>new{c.ProductName,c.ProductBasePrice});
聲明:本文系本人原創,版權歸屬作者和博客園共同所有,任何組織或個人不得隨意轉載,修改。需要轉載請與本人聯系:qouoww@163.com。