作為一個專業抱大腿的開發者,我一直以來都認為DataTable是.Net世界里無敵的存在,它的自由、灑脫、包容。。。這些優秀的品質一直深深的吸引着我。。。
沒錯,對於傳統的.Net應用(silverlight當然除外),使用DataTable來承載數據源,對GridView進行數據綁定,批量增刪改是一種萬金油的戰術,隨時隨地的添加行、列,要是再配合上Aspose.Cell的模板列綁定報表,可謂神器也!(我自己也是這么做的)。
但是在各種SOA中間件潮水一樣涌入后,我們在借助開發平台的幫助下,為了縮短數據庫交互業務開發在整個開發過程中所占的時間,往往會使用開發工具提供的技術支持,這里面實體類所起到的作用是相當大的。
試想一下,在建立數據庫的同時,開發工具會比照數據庫結構給你一個和數據庫一模一樣的實體類庫,這個實體類庫包含了拓展函數諸如:Insert();UpDate();Delete()。。。這些原本需要使用Sql語句或存儲過程實現的數據庫業務,任何時候你實例化一個 var xxx = new DataEntity(); 之后,你都不需要在仔細考慮這條數據到底有多少個字段,每個字段是干嘛用的(建立數據庫時寫個中文備注),而且需要增刪改的時候直接調用拓展函數即可,這確實大大的減少了我們在開發中浪費在數據庫迷宮中的時間。
我曾經就要求我的同事們在對數據庫查詢的時候禁止使用 select * from xxx 這樣的方法讀數據庫,這會給后來的維護者帶來極大麻煩和困擾。。。尤其對於喜歡寫insert語句的同學啊。。。數據庫結構變了之后你不着急么。。。。
貌似有點跑偏了,題目中寫的用linq+orm,linq是啥不用多說,orm這里面是EAS.NET開發平台帶的一個實體類生成工具,通過連接數據庫,自動生成該數據庫對應的實體類,以及進一步自動生成基礎crud操作界面,詳細方法可以看他的官網或找它的開發團隊。。。我們看下面這個頁面。。。
這是一個很典型的匯總查詢應用界面,通過匯總查詢,可以將結果添加到明細表,明細表的數據批量導入后,可以在右側查詢當前的實時庫存。這樣一個界面在傳統的開發過程中,你可能需要寫一個查詢函數,定義個公用的DT,寫一個匯總函數,寫一個更新函數blblblblblblbl
如果使用ORM設計器又如何呢,
step1、用orm設計器生成實體類
step2、用formdesigner做一個基礎的界面,這里面包含了基礎查詢方法和綁定方法,dataGridView也是現成的了
step3、使用linq進一步細化業務。
代碼如下:
using System; using System.Collections; using System.Collections.Generic; using System.Drawing; using System.ComponentModel; using System.Data; using System.Windows.Forms; using System.IO; using System.Linq; using System.Globalization; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using EAS.Data; using EAS.Data.ORM; using EAS.Data.Access; using EAS.Modularization; using EAS.Services; using EAS.Data.Linq; using IronMES.Entities.HBWL; using IronMES.BLL; using IronMES.Entities; using IronMES.WinUI.StockManager.HBWL; using IronMES.WinUI.Helper; namespace IronMES.WinUI.HBWL { [Module("84972d17-2274-4484-bde3-3106a1760481", "汽運檢斤接口", "將汽車運輸的檢斤量批量導入庫存管理系統")] public partial class JL_CARWEIGHTHISTORYList : UserControl { IList<JL_CARWEIGHTHISTORY> vList = null; IList<JL_CARWEIGHTHISTORY> vListGather = null; IList<JL_CARWEIGHTHISTORY> pList = null; IList<vSMInventory> iList = null; public JL_CARWEIGHTHISTORYList() { InitializeComponent(); this.dataGridView1.AutoGenerateColumns = false; this.dataGridView1.DataSource = this.datasourcedataGridView1; this.dataGridView2.AutoGenerateColumns = false; this.dataGridView2.DataSource = this.bindingSource2; this.dataGridView3.AutoGenerateColumns = false; } [ModuleStart] public void StartEx() { } /// <summary> /// 顯示記錄。 /// </summary> IList<JL_CARWEIGHTHISTORY> DisplayList { get { return this.datasourcedataGridView1.DataSource as IList<JL_CARWEIGHTHISTORY>; } } public void GetDataList(DateTime dateStart, DateTime dateEnd,string fs_material, string fs_carno, string fs_downloadaddress, string fs_sendername, string fs_receivername, string fs_materialname, string fs_cg_minenum, string cg_order_num, string send_place, string cyunit) { this.vList = ServiceContainer.GetService<IHBWLService>().GetDataList(dateStart, dateEnd,fs_material, fs_carno, fs_downloadaddress, fs_sendername, fs_receivername, fs_materialname, fs_cg_minenum, cg_order_num, send_place, cyunit).ToList(); } public void GetInventoryList() { DataEntityQuery<vSMInventory> query = DataEntityQuery<vSMInventory>.Create(); var p = from item in query orderby item.FID descending select item; this.iList = p.ToList(); } private void dataPager_PageChanged(object sender, EventArgs e) { //this.datasourcedataGridView1.DataSource = this.vList.Skip(this.dataPager.Skip).Take(this.dataPager.Take).ToList(); } //添加本條記錄到批量錄入的准備集中 private void btnAddOne_Click(object sender, EventArgs e) { if (this.dataGridView1.RowCount > 0 && this.dataGridView1.CurrentRow != null) { var dataEntity = this.dataGridView1.CurrentRow.DataBoundItem as JL_CARWEIGHTHISTORY; if (dataEntity.FS_WEIGHTBILLNO != null) { pList.Add(dataEntity); } else { var p = from item in vList where item.FS_MATERIALNAME == dataEntity.FS_MATERIALNAME && item.FS_SENDERNAME == dataEntity.FS_SENDERNAME && item.FS_RECEIVERNAME == dataEntity.FS_RECEIVERNAME select item; foreach (var item in p.ToList()) { pList.Add(item); } } } this.bindingSource2.DataSource = pList.ToList(); this.dataGridView2.DataSource = this.bindingSource2; } //添加所有GridView1中的記錄到批量錄入的准備集中 private void btnAddAll_Click(object sender, EventArgs e) { if (this.dataGridView1.RowCount > 0) { for (int i = 0; i < dataGridView1.Rows.Count; i++) { var dataEntity = dataGridView1.Rows[i].DataBoundItem as JL_CARWEIGHTHISTORY; if (dataEntity.FS_WEIGHTBILLNO != null) { pList.Add(dataEntity); } else { var p = from item in vList where item.FS_MATERIALNAME == dataEntity.FS_MATERIALNAME && item.FS_SENDERNAME == dataEntity.FS_SENDERNAME && item.FS_RECEIVERNAME == dataEntity.FS_RECEIVERNAME select item; foreach (var item in p.ToList()) { pList.Add(item); } } } } this.bindingSource2.DataSource = pList.ToList(); this.dataGridView2.DataSource = this.bindingSource2; } private void btnRemoveOne_Click(object sender, EventArgs e) { if (this.dataGridView2.RowCount > 0) { pList.Remove(dataGridView2.CurrentRow.DataBoundItem as JL_CARWEIGHTHISTORY); dataGridView2.AllowUserToDeleteRows = true; dataGridView2.Rows.Remove(dataGridView2.CurrentRow); //dataGridView2.DataSource = pList.ToList(); } } private void btnRemoveAll_Click(object sender, EventArgs e) { if (this.dataGridView2.RowCount > 0) { pList.Clear(); this.dataGridView2.DataSource = pList.ToList(); } } private void btnQuery_Click(object sender, EventArgs e) { this.GetDataList(this.dateTimePickerStart.Value, this.dateTimePickerEnd.Value, this.tbFS_MATERIAL.Text.Trim(),this.tbFS_CARNO.Text.Trim(), this.tbFS_DOWNLOADADDRESS.Text.Trim(), this.tbFS_SENDERNAME.Text.Trim(), this.tbFS_RECEIVERNAME.Text.Trim(), this.tbFS_MATERIALNAME.Text.Trim(), this.tbFS_CG_MINENUM.Text.Trim(), this.tbCG_ORDER_NUM.Text.Trim(), this.tbSEND_PLACE.Text.Trim(), this.tbCYUNIT.Text.Trim()); pList = new List<JL_CARWEIGHTHISTORY>(); this.dataGridView2.DataSource = pList.ToList(); if (this.cbGather.Checked == true) { var v = from item in vList group item by item.FS_MATERIALNAME into materialGroup select new { materialGroup.Key, senderGroup = from item2 in materialGroup group item2 by item2.FS_SENDERNAME into senderGroup select new { senderGroup.Key, receiverGroup = from item3 in senderGroup group item3 by item3.FS_RECEIVERNAME into receiverGroup select new { receiverGroup.Key, FS_NETWEIGHT = receiverGroup.Sum(x => x.FS_NETWEIGHT), FS_GROSSWEIGHT = receiverGroup.Sum(x => x.FS_GROSSWEIGHT), FS_TAREWEIGHT = receiverGroup.Sum(x => x.FS_TAREWEIGHT), FCount = receiverGroup.Count() } } }; var _vList = v.ToList(); if (_vList.Count > 0) { this.vListGather = new List<JL_CARWEIGHTHISTORY>(); foreach (var item in _vList) { foreach (var item2 in item.senderGroup) { foreach (var item3 in item2.receiverGroup) { var dataEntity = new JL_CARWEIGHTHISTORY(); dataEntity.FS_MATERIALNAME = item.Key; dataEntity.FS_SENDERNAME = item2.Key; dataEntity.FS_RECEIVERNAME = item3.Key; dataEntity.FS_NETWEIGHT = item3.FS_NETWEIGHT; dataEntity.CG_SHIPPED_COUNT = item3.FCount; dataEntity.FS_TAREWEIGHT = item3.FS_TAREWEIGHT; dataEntity.FS_GROSSWEIGHT = item3.FS_GROSSWEIGHT; vListGather.Add(dataEntity); } } } } this.datasourcedataGridView1.DataSource = this.vListGather.ToList(); } else this.datasourcedataGridView1.DataSource = this.vList.ToList(); DataGridViewHelper.DisplayNullColumn(dataGridView1); this.GetInventoryList(); this.dataGridView3.DataSource = iList.ToList(); } private void btnPrint_Click(object sender, EventArgs e) { string[] titles = new string[] { "汽車計量查詢結果", "批量導入明細", "實時庫存(導入前)" }; DataGridView[] dataGridViews = new DataGridView[] { dataGridView1, dataGridView2, dataGridView3 }; ExcelMaker.OutToExcelFromDataGridView("汽運檢斤接口導出表", titles, dataGridViews, true); } private void btnClose_Click(object sender, EventArgs e) { EAS.Application.Instance.CloseModule(); } #region 報表相關 /// <summary> /// 報表。 /// </summary> protected EAS.Explorer.Entities.Report Report { get { if (this.m_Report == null) { this.m_Report = new EAS.Explorer.Entities.Report(); this.m_Report.Name = string.Empty; } return this.m_Report; } } EAS.Explorer.Entities.Report m_Report; /// <summary> /// 打印窗體。 /// </summary> protected EAS.Report.Controls.PrintViewDialog PrintForm { get { if (this.m_PrintForm == null) { this.m_PrintForm = new EAS.Report.Controls.PrintViewDialog(); } return this.m_PrintForm; } } EAS.Report.Controls.PrintViewDialog m_PrintForm; #endregion /// <summary> /// 點擊后批量導入庫存信息 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { HBWLInputSet inputEditor = new HBWLInputSet(); var inputs = new HBWLInputs(); inputs.inputDatas = new List<InputData>(); inputs.FAmount = 0; inputs.FComMaterial = ""; inputs.FComMaterialCode = ""; inputs.FCount = 0; foreach (var item in pList) { inputs.FAmount += item.FS_NETWEIGHT; inputs.FCount++; inputs.FComMaterial = item.FS_MATERIALNAME; inputs.FComMaterialCode = item.FS_MATERIAL; var inputData = new InputData(); inputData.FBillNo = item.FS_WEIGHTBILLNO; inputData.FWeight = item.FS_NETWEIGHT; inputs.inputDatas.Add(inputData); } inputEditor.DataEntity = inputs; if (inputEditor.ShowDialog() == DialogResult.OK) { this.btnQuery_Click(sender, e); } } } }
里面其實只是用了一個簡單的linq匯總,其他的大部分代碼都是設計器自動生成的,在進行批量數據導入的時候,使用了實體類的拓展Insert方法,代碼如下。
private bool SaveWeightBill(InputData inputData) { string guid = Guid.NewGuid().ToString(); var query = DataEntityQuery<SMStockBill>.Create(); var p = from item in query where item.FHBWLBillNo == inputData.FBillNo select item; var list = p.ToList(); if (list.Count >0) { return false; } var dataEntity = new SMStockBill(); dataEntity.FBillerID = (EAS.Application.Instance.Session.Client as EAS.Explorer.IAccount).LoginID; dataEntity.FDate = this.dateTimePickerFDate.Value; dataEntity.FDateTime = DateTime.Now; dataEntity.FSCStockID = Convert.ToInt32(this.cbxFBillType.SelectedValue) == 1 ? 0 : Convert.ToInt32(this.cbxFSCStockID.SelectedValue); dataEntity.FGuid = guid; dataEntity.FHBWLBillNo = inputData.FBillNo; dataEntity.FItemID = (int)this.cbxFName.SelectedValue; dataEntity.FNote = this.tbFNote.Text; dataEntity.FQty = inputData.FWeight; dataEntity.FDCStockID = Convert.ToInt32(this.cbxFBillType.SelectedValue) == 2 ? 0 : Convert.ToInt32(this.cbxFDCStockID.SelectedValue); dataEntity.FStockBillTypeID = (int)this.cbxFBillType.SelectedValue; dataEntity.FWet = Convert.ToDecimal(this.tbFWet.Text); dataEntity.Insert(); InventoryMaker.SaveInventory(dataEntity); return true; }
左邊2個dgv其實是一樣的,使用了一個隱藏列的公共方法,我都是這么干的,代碼如下。
/// <summary> /// 將dataGirdView里面全是空的列隱藏 /// </summary> /// <param name="dataGirdView"></param> public static void DisplayNullColumn(DataGridView dataGirdView) { for (int i = 0; i < dataGirdView.Columns.Count; i++) { dataGirdView.Columns[i].Visible = true; bool isNull = true; for (int j = 0; j < dataGirdView.Rows.Count; j++) { //如果不是日期格式,不為空即可 if (dataGirdView.Rows[j].Cells[i].ValueType != typeof(DateTime) && dataGirdView.Rows[j].Cells[i].Value != null && dataGirdView.Rows[j].Cells[i].Value.ToString() != string.Empty) { isNull = false; continue; } //如果是日期格式需要大於最小時間 else if (dataGirdView.Rows[j].Cells[i].ValueType == typeof(DateTime) && (DateTime)dataGirdView.Rows[j].Cells[i].Value >= new DateTime(1973, 1, 1)) { isNull = false; continue; } } if (isNull) dataGirdView.Columns[i].Visible = false; } }
也可能有人會遇到必須使用sql語句查詢的尷尬情況(比如我需要訪問一個某單位買來的數據庫。。。就是這樣)
為了方便繼續用formdesigner設計出的便宜代碼~~~在讀取數據的服務中加下面這一段即可完美轉換DataTable為IList
public IList<IronMES.Entities.Runtime.History> HistoryNonCyclicList(DateTime dateStart, DateTime dateEnd, string tagname) { string sqlStr = "SELECT * FROM History WHERE" + " (TagName = '" + tagname + "') AND (DateTime >= '" + dateStart + "') AND (DateTime < '" + dateEnd + "') " + "AND (value <> 0) AND (wwQualityRule = 'Good')"; var dt = da.QueryDataTable(sqlStr); var list = new List<History>(); if (dt.Rows.Count > 0) { foreach (DataRow item in dt.Rows) { var entity = new History(); foreach (Column prop in entity.GetColumns()) { entity.SetValue(prop, Convert.ChangeType(item[prop.Name],prop.Property.PropertyType)); } list.Add(entity); } } return list; }
通過使用orm實體類,form設計器,再配合上linq,我個人感覺在類似上面顯示的界面業務時極大的縮短了每一個模塊的開發周期,讓開發者可以節約更多的時間到數據庫設計、ui交互設計等更重要的工作中。
由於自己水平很有限,所以沒辦法從根本上解釋一下使用DataTable和使用實體類進行查詢業務時的區別,比如效率啊、資源占用啊啥的,我覺得這些和平時的開發沒啥大影響。。。(高端請無視我。。。)