本篇是第一階段的完結篇。 html
學完這篇後,你應該能夠利用MVC進行完整項目的開發了。 前端
本篇主要講述多表關聯數據的更新,以及如何使用原生SQL。 數據庫
咱們在第四篇文章已經講過數據的更新了,不過那個是針對單表結構的更新。 框架
此次咱們講下使用EF進行關聯數據的更新。 學習
關聯數據更新有兩種狀況: spa
1.一對多 3d
2.多對多 htm
第一種狀況關聯表有主外鍵關聯,只要簡單的更新外鍵值就能夠了(至關於更新單表),咱們主要講解第二種多對多的狀況。 blog
使用以前很熟悉的模型: 排序
咱們定義一個場景:
一個用戶能夠有任意多個角色,一個角色能夠有任意多個用戶。
咱們接下來完成下面操做:
編輯某個用戶時,顯示該用戶的角色進行編輯。
即 更新某個用戶(SysUser表)及其相關的角色(SysUserRole表)。
1.先添加一個ViewModel, 用來表示角色是否分配給某個用戶。
2.打開UserRoleController,添加一個Edit的Action用來顯示編輯頁面。
有兩點說明一下:
a.咱們沿用上一篇文章的模型,多了一個SysDepartment,實際模型以下:
b.PopulateAssigenedRoleData將特定用戶下選中的角色標記出來。
3.打開Views\UserRole\Index.cshtml, 增長一個編輯按鈕
4.再根據Edit Action自動生成Edit View
修改相關內容,主要是兩點:
a.部門
b.角色
角色是經過一組checkbox來顯示的。
Checkbox顯示數據庫中全部角色,已分配給用戶的會顯示選中狀態。
經過勾選checkbox的方式來實現用戶角色的更新。
說明
角色少這樣弄沒問題,若是多的話經典的作法,能夠用兩個listbox,中間用箭頭將左右兩邊的選項移動。本篇文章主要說明關聯表的更新,後續文章咱們會提供更好的作法的示例。
運行下Index頁面。
進入編輯頁面。
這樣編輯的顯示功能就已經完成了。
能夠看到,用一組checkbox表示roles是否選中。
5.最後再完成HttpPost的Edit功能。
首先更新SysUser表:
用model binder中的值更新entity: userToUpdate.
能夠看到,咱們使用了白名單指定數據庫中須要更新的字段。
TryUpdateModel(userToUpdate,"",
new string[] {"LoginName","Email","Password","CreateDate","SysDepartmentID"})
再更新SysUserRole表:
將數據庫中值和編輯後的值進行比對,基本邏輯是:
若是被選中了,原來沒有的要添加;
若是沒被選中,原來有的要刪除。
UpdateUserRoles(selectedRoles, userToUpdate);
注意在UpdateUserRoles裏,我新建了一個鏈接
using (AccountContext db2=new AccountContext())
若是用以前的db會報以下錯誤:
已有打開的與此 Command 相關聯的 DataReader,必須首先將它關閉。
從新運行下Index, 以下一組圖,這時咱們看到角色編輯已經起做用了。
至此,多表更新的示例就介紹到這,其餘狀況相信你能夠觸類旁通本身推導出來作法。
使用EF的一個優勢就是自動幫咱們生成SQL,這在常規狀況下很方便,但有些狀況下用EF卻不適合。
例如咱們上面更新SysUserRole這張表時,每次增減一條數據,要循環不少次。
另外還有些特別複雜的語句,利用EF很難生成。
EF提供一組方法用來執行原生的SQL.
有如下三種:
1.DbSet.SqlQuery
2.Database.SqlQuery
3.Database.ExecuteSqlCommand
這三種有啥區別呢?咱們來看例子。
對三種形式咱們各舉一例。
例子1:DbSet.SqlQuery查詢並返回Entities
咱們打開Controllers\AccountController.cs作實驗
找到Details方法
將註釋的部分改爲方框部分便可。
方框中的和註釋掉的內容SysUser sysUser=db.SysUsers.Find(id)徹底同樣。
前端顯示效果不變:
注意兩點:
1.構造帶參數的SQL語句(養成好習慣,防止SQL注入,老是用帶參數的SQL語句)
2.此處使用的是DbSet<TEntity>執行SQL方法,返回的直接是Entity, 和LINQ查詢同樣。若是暫時不熟悉LINQ,用這種方法替換(做爲一個過渡),可讓你快速的使用起新框架。
這種狀況有一些缺陷,例如
SELECT LoginName as UserName,* FROM [dbo].[SysUser] WHERE ID=@id
你們能夠看到我添加了LoginName as UserName,這是由於Model中用了Column Attribute,數據庫中存的字段是LoginName
這樣我若是不轉換,model就會找不到匹配的字段而出錯,而若是用db.SysUsers.Find(id) 就能夠智能轉換。
例子2 Database.SqlQuery 返回其餘類型
string query = "select loginName from SysUser";
var names=db.Database.SqlQuery<string>( query).ToList();
以上會返回一個System.Collections.Generic.List<string>類型。
這種方式和第一種狀況最大的區別就是返回non-entity 類型。
咱們能夠根據須要,本身構建須要的類型。
咱們也能夠自定義一個entity type讓它返回,例如相似咱們上一個例子:
SysUser sysUser = db. Database.SqlQuery(query, paras).SingleOrDefault();
這樣也能夠返回entity, 但要注意,這種方式將不會被context track, 返回後就不要緊了,若是咱們在View中用相似於Model.XXX導航屬性獲取其餘關聯數據就會報錯。例如@foreach (var item in Model.SysUserRoles),這種狀況下會報Model爲null的錯誤。
例子3:Database.ExecuteSqlCommand執行更新語句
最後一個是更新的,直接看示例就明白了:
context.Database.ExecuteSqlCommand("UPDATE dbo.Posts SET Rating = 5 WHERE Author = @author", new SqlParameter("@author", userSuppliedAuthor));
最後提下執行存儲過程,也相似,我就很少說了,以下MSDN(https://msdn.microsoft.com/en-us/data/jj592907)截圖。
原生SQL執行查詢:
須要返回實體模型,使用DbSet.SqlQuery (context會跟蹤,等效於LINQ方式)
須要返回其餘類型,使用Database.SqlQuery
原生SQL執行更新:
使用Database.ExecuteSqlCommand
至此,本系列文章的第一階段(1~10)就結束了,下一階段再見。
感謝支持,祝學習進步!
P.S. 方便你們觀看,列出系列文章地址: