說明:
本文將幫助你理解SQL Server 2008表值參數,這裏已經用了ASP.NET MVC Framework 3.0,但你可以用ASP.NET MVC的其它框架。
背景:
在面向對象程序設計的開發環境中,很多情況下,我們需要存儲一整列對象到數據庫中,在這些情況中,程序員採用以下兩個選項之一:
用自定義分隔符來序列化對象陣列及其參數並在數據庫端將它們並行化,執行所有數據庫操作。但這使得計算變得相當複雜且費時,除了整列對象被當做varchar參數傳遞。
使整列對象成環並通過單獨創建連接在每個陣列元素執行數據庫操作。它在編程上不那麼複雜但就我們需要在每個迭代上創建連接而言,依然不是一個可行的選項。
Microsoft SQL Server 2008能通過引入表值參數使得用戶可以創建能輕易在存儲程序中使用的自定義類型table的參數,來解決上述兩個問題。
使用代碼:
表值參數可以用如下語法來創建:
create type TVPType astable(Name nvarchar(500),Salary decimal(18,2),Age int,EndHeader bit);
在上述代碼片段中,我們要注意創建自定義參數的類型和名稱。在本例中,是表參數結構跟隨表。一旦你執行代碼,就可以在其中找到你的自定義參數。
現在我們需要創建程序來插入從應用中接收到的數據。我們將使用如下存儲程序:
createproc AddDetails@tvp TVPType readonlyasbegininsertinto TVPTable(Name, Age, Salary) select t.Name,t.Age,t.Salary from@tvp t;end
由此現在我們已經完成了數據庫方面,將轉向應用方面。打開一個新的MVC項目或一個你想要使用的項目。我已經使用了類型化數據集替代實體框架,因爲實體框架不支持能夠用於表值參數的結構化類型參數。
如下即爲應用的截圖(在用表值參數插入記錄到數據庫的前後)。
插入記錄前的頁面顯示:
現在當我們插入記錄時,我已允許用戶通過使用如下JavaScript代碼插入任意量的記錄:
function addRow() {var table = document.getElementById('recordTable').lastChild;if (table) {var id = Number(table.children[table.children.length-1].id);if (!isNaN(id)) { id+=1;var child = document.createElement('tr'); child.id = id;var html = '<input type="text" id="name_' + id + '" maxlength="50"/>'; html += '<input type="text" id="age_' + id + '"/>'; html += '<input type="text" id="salary_' + id + '"/>'; child.innerHTML = html; table.appendChild(child); } }}
用戶可以用“Add more”按鈕添加新行,最後用“Submit”按鈕提交。
我們可以通過生成一個請求,用JSON對象傳送所有數據到控制器。如下即是完成上述任務的JavaScript代碼:
function submitRecords() {var table = document.getElementById('recordTable').lastChild;if (table) { var childArr = table.children;if (childArr.length > 0) {var jsonArr={tvp:[]};var nameObj, ageObj, salaryObj, id;var counter = 0;for (var i = 1; i < childArr.length; i++) { id = childArr[i].id; nameObj = document.getElementById('name_' + id); ageObj = document.getElementById('age_' + id); salaryObj = document.getElementById('salary_' + id);if (nameObj != null && nameObj.value.trim() != '' && ageObj != null && ageObj.value.trim() != '' && salaryObj != null && salaryObj.value.trim() != '') { jsonArr.tvp.push({"name":nameObj.value.trim(),"age":ageObj.value.trim(),"salary":salaryObj.value.trim() }); } }if (jsonArr.tvp.length > 0) { $.ajax({ url: "../TVP/SubmitRecord", data:{"data":JSON.stringify(jsonArr)}, type: "POST"}); } elsealert("Add data please"); } }}
在控制器一方,我們已經創建了一個函數SubmitRecord來接收我們的AJAX請求並將數據從AJAX請求中傳遞到相應的模型中。
[HttpPost]public ActionResult SubmitRecord(string data){ TVPModel.StoreValues(data);return View();}
在上述代碼中,HttpPost屬性指定了調用函數(接收當PSOT請求時)。我們已經用NewtonSoftJson庫來在模型類別上(解析JSON數據並將其存儲到DataTable)解析JSON對象。
publicstaticvoid StoreValues(string data){ JToken token = JObject.Parse(data);var tvp=token.SelectToken("tvp"); DataTable recordTable = Params.GetDetailsTable();foreach (var value in tvp) { DataRow row = recordTable.NewRow(); row["Name"] = value.SelectToken("name"); row["Age"] = value.SelectToken("age"); row["Salary"] = value.SelectToken("salary"); ; row["EndHeader"] = true; recordTable.Rows.Add(row); } TVPBL bl = new TVPBL(); bl.InsertTVP(recordTable);}
創建的DataTable被作爲參數傳送到數據庫程序。該DataTable應該和數據庫端的所述類型有着同樣的結構。我們已經創建了一個BL類,負責爲我們的類型化數據集創建一個表適配器並調用指定的程序。
publicvoid InsertTVP(DataTable tvpTable){ TVPTableTableAdapter adapter = GetTvpTableAdapter(); adapter.AddDetails(tvpTable);}
在完成上述程序後,我們的數據被存儲在數據庫中。現在當瀏覽主頁顯示“No details to show”消息時,它將不再可見。而主頁將顯示如下:
需要注意的點:
實體框架目前不支持表值參數。
表值參數不能被編輯但可以被刪除和重建,僅當你的數據庫中任意存儲程序沒有引用該表值參數時。
表值參數在只讀模式中使用。
表值參數不能被用於數據庫中用戶定義函數。
DataTable的表值參數和結構應該匹配。比如,列應該被定義在同一個位置。
點擊下載源代碼