MVC5+EF6 入门完整教程4 :EF基本的CRUD
概述 & 要点
下面是本文要点,正文部分会有详细介绍。
-
EF数据模型的CRUD
-
常用的HtmlHelper
-
Repository Pattern
理论基础 -- EF 三种编程方式 (略)
总共有三种方式:
Database First,Model First和Code First,我们采用的是code first.
理论基础 -- EF CRUD
针对之前创建的SysUser, SysRole, SysUserRole举一些典型例子,基本的CRUD大家在使用时模仿这些例子就可以了。
我们要用的数据库示例数据分别如下:
SysUser
SysRole
SysUserRole
EF数据查询
参见:https://www.cnblogs.com/wfy680/p/15114079.html
EF数据查询用LINQ实现(LINQ to Entities),通常有表达式和函数式两种方式。建议用函数式方式,比较简单。
假设我们已经定义好了context
private AccountContext db = new AccountContext();
[基本查询] 查询所有的SysUser
//表达式方式
var users = from u in db.SysUsers
select u;
//函数式方式
users = db.SysUsers;
[条件查询] 加入查询条件
//表达式方式
users = from u in db.SysUsers where u.UserName == "Tom"
select u;
//函数式方式
users = db.SysUsers.Where(u => u.UserName == "Tom");
[排序和分页查询]
users = (from u in db.SysUsers orderby u.UserName
select u).Skip(0).Take(5); //表达式方式
users = db.SysUsers.OrderBy(u => u.UserName).Skip(0).Take(5); //函数式方式
NOTE 只有排序了才能分页
[聚合查询]
//查user总数
var num = db.SysUsers.Count();
//查最小ID
minId = db.SysUsers.Min(u => u.ID);
NOTE 聚合查询只能通过函数式查询
[连接查询]
var users = from ur in db. SysUserRoles
join u in db. SysUsers
on ur.SysUserID equals u.ID
select ur;
NOTE
连接查询返回的结果还是一个类型为SysUserRoles的集合,只是用了内连接进行了的筛选。
那么问题来了,如果我需要选择一个集合,里面包括多张表,如SysUser里面的UserName和SysRole里面的RoleName怎么办?
这个是通过navigation property来实现的, 前面新建model的时候提到过,例如SysUser里面的
public virtual ICollection<SysUserRole> SysUserRoles { get; set; }
但这种做法还是不是太灵活,具体做法我们在下面的详细步骤里面讲。
补充:
内连接inner join:只显示两表id匹配的
左外连接left join:显示join左边的表的所有数据(不管两表是否匹配),对于不匹配的部分都用NULL显示
右外连接right join:与左外连接相反,显示join右边的表的所有数据
EF数据更新
UPDATE步骤比较清晰,直接看下面代码。
//数据更新,分三步:找到对象--> 更新对象数据--> 保存更改
public ActionResult EFUpdateDemo()
{
//1.找到对象
var sysUser = db.SysUsers.FirstOrDefault(u => u.UserName == "Tom");
//2.更新对象数据
if (sysUser != null)
{
sysUser.UserName = "Tom2";
}
//3.保存修改
db.SaveChanges();
return View();
}
EF数据添加/删除
与UPDATE类似。
//数据添加和删除
public ActionResult EFAddOrDeleteDemo()
{
//添加
//1.创建新的实体
var newSysUser = new SysUser()
{
UserName = "Scott",
Password = "tiger",
Email = "Scott@sohu.com"
};
//2.增加
db.SysUsers.Add(newSysUser);
//3.保存修改
db.SaveChanges();
//删除
//1.找到需要删除的对象
var delSysUser = db.SysUsers.FirstOrDefault(u => u.UserName == "Scott");
//2.删除
if (delSysUser!=null)
{
db.SysUsers.Remove(delSysUser);
}
//3.保存修改
db.SaveChanges();
return View("EFQueryDemo");
}
详细步骤
-
查询用户及相应角色的功能
-
修改用户
-
增加用户和删除用户
一、查询用户及相应的角色
修改Account控制器的 index方法,添加相关View, 显示所有用户
-
将Users 这个model作为参数传递给视图【控制器给页面传递参数】
public ActionResult Index() { return View(db.Users); 返回视图,给视图传递参数
}
-
Views ---> Account ---> Index.cshtml 顶部添加强类型声明【页面接收参数】
@model IEnumerable<MVCDemo.Models.SysUser>
body中添加个table用来显示数据
-
@model IEnumerable<MVCDemo.Models.User> @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> </head> <body> <table> <tr> <th>@Html.DisplayNameFor(u=>u.UserName)</th> <th>@Html.DisplayNameFor(u=>u.Email)</th> <th></th> </tr> @foreach(var item in Model) { <tr> <td>@Html.DisplayFor(m => item.UserName)</td> <td>@Html.DisplayFor(m => item.Email)</td> <td>@Html.ActionLink("详细信息", "Details", new { id = item.ID }) </tr> } </table> </body> </html>
@Html.ActionLink("Details", "Details", new { id = item.ID })生成一个相同controller下的路由地址。
显示结果:
二、增加一个Details方法,添加相关View, 显示相应用户及对应的角色
-
将特定的model传过去
public ActionResult Details(int id) { return View(db.Users.Find(id)); 传递1个特定对象 }
-
Views ---> Account ---> Details.cshtml 顶部添加强类型声明
@model MVCDemo.Models.SysUser 1个特定对象,不需要IEnumerable
显示数据,注意方框部分如何导航到另外一张表的信息中。
显示结果
-
更新用户,增加用户,删除用户
这三个操作都类似,属于更新的范畴,我们放在一起来讲。
-
修改Views---> Account --->Index.cshtml
开头增加Create链接。
table每条记录后面增加Edit,Delete链接。
@model IEnumerable<MVCDemo.Models.User> @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> </head> <body> <p>@Html.ActionLink("添加用户", "Create")</p> <table> <tr> <th>@Html.DisplayNameFor(u=>u.UserName)</th> <th>@Html.DisplayNameFor(u=>u.Email)</th> <th></th> </tr> @foreach(var item in Model) { <tr> <td>@Html.DisplayFor(m => item.UserName)</td> <td>@Html.DisplayFor(m => item.Email)</td> <td> @Html.ActionLink("详细信息", "Details", new { id = item.ID }) @Html.ActionLink("编辑", "Edit", new { id = item.ID }) @Html.ActionLink("删除", "Delete", new { id = item.ID }) </td> </tr> } </table> </body> </html>
-
在Controller中增加相应的方法。
//删除用户 public ActionResult Delete(int id) { User mUser = db.Users.Find(id); return View(mUser); } [HttpPost, ActionName("Delete")] public ActionResult DeleteConfirmed(int id) { User mUser = db.Users.Find(id); db.Users.Remove(mUser); db.SaveChanges(); return RedirectToAction("Index"); } //修改用户 public ActionResult Edit(int id) { User user = db.Users.Find(id); return View(user); } [HttpPost] public ActionResult Edit(User user) { db.Entry(user).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } public ActionResult Create() //无model传递给视图 { return View(); } [HttpPost] public ActionResult Create(User user) { db.Users.Add(user); db.SaveChanges(); return RedirectToAction("Index"); }
NOTE
涉及到数据更新的地方都有两个同名的方法重载,一个用来显示[HttpGet],一个用来数据更新[HttpPost]
-
在右键方法名,生成相应的View
每个View的顶部需要添加一个声明
@model MVCDemo.Models.SysUser
各个view的body中具体代码:
Create.cshtml
@model MVCDemo.Models.User @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Create</title> </head> <body> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>创建用户</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) <div class="form-group"> @Html.LabelFor(model => model.UserName, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.UserName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.UserName, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Email, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Email, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Email, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Password, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Password, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Password, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Create" class="btn btn-default" /> </div> </div> </div> } <div> @Html.ActionLink("Back to List", "Index") </div> </body> </html>
Edit.cshtml
@model MVCDemo.Models.User @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Edit</title> </head> <body> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-horizontal"> <h4>User</h4> <hr /> @Html.ValidationSummary(true, "", new { @class = "text-danger" }) @Html.HiddenFor(model => model.ID) <div class="form-group"> @Html.LabelFor(model => model.UserName, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.UserName, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.UserName, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Email, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Email, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Email, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> @Html.LabelFor(model => model.Password, htmlAttributes: new { @class = "control-label col-md-2" }) <div class="col-md-10"> @Html.EditorFor(model => model.Password, new { htmlAttributes = new { @class = "form-control" } }) @Html.ValidationMessageFor(model => model.Password, "", new { @class = "text-danger" }) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="Save" class="btn btn-default" /> </div> </div> </div> } <div> @Html.ActionLink("Back to List", "Index") </div> </body> </html>
Delete.cshtml
@model MVCDemo.Models.User @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Delete</title> </head> <body> <h3>Are you sure you want to delete this?</h3> <div> <h4>User</h4> <hr /> <dl class="dl-horizontal"> <dt> @Html.DisplayNameFor(model => model.UserName) </dt> <dd> @Html.DisplayFor(model => model.UserName) </dd> <dt> @Html.DisplayNameFor(model => model.Email) </dt> <dd> @Html.DisplayFor(model => model.Email) </dd> <dt> @Html.DisplayNameFor(model => model.Password) </dt> <dd> @Html.DisplayFor(model => model.Password) </dd> </dl> @using (Html.BeginForm()) { @Html.AntiForgeryToken() <div class="form-actions no-color"> <input type="submit" value="Delete" class="btn btn-default" /> | @Html.ActionLink("Back to List", "Index") </div> } </div> </body> </html>
NOTE
针对上面这些代码,我们提一下其中用到的HtmlHelper, 主要有这么几个:
DisplayNameFor (model=>model.xxx) 生成纯文本,显示xxx列名
DisplayFor (model=>model.xxx) 生成纯文本,显示xxx列的值
LableFor --->生成一个Lable标签
EditorFor --->生成一个text类型的input
PasswordFor ---> 类似于EditorFor, 隐藏文本内容
ActionLink --->生成一个<a>标签
BeginForm---> 生成一个表单
NOTE
HtmlHelper是可以通过View的Html属性调用的方法(@Html.xxx), 可以类比成原来WebForm的服务器端控件, 后续文章会将分成几类, 归类进行介绍,这里先简单提一下做个铺垫。这块最好的学习方法是用浏览器打开相应的页面,View page source,查看生成的相应HTML代码。
Repository Pattern
Repository Pattern是一种设计模式。
"企业架构模式" 上的定义:
Mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects.
具体的做法:
先定义Interface, 通过定义接口确定数据访问类的功能需求, 接着实现该接口。
以对SysUser这张表的操作为例。
先建文件夹 Repositories
建接口IUserRepository
namespace MVCDemo.Repositories { public interface IUserRepository { //查询所有用户 IQueryable<User> SelectAll(); //通过用户名查询用户 User SelectByName(string userName); //添加用户 void Add(User user); //删除用户 bool Delete(int id); } }
建类UserRepository ,继承接口,实现功能。
namespace MVCDemo.Repositories { public class UserRepository : IUserRepository { protected EntityContext db = new EntityContext(); //查询所有用户 public IQueryable<User> SelectAll() { return db.Users; } //通过用户名查询用户 public User SelectByName(string userName) { return db.Users.FirstOrDefault(u => u.UserName == userName); } //添加用户 public void Add(User sysUser) { db.Users.Add(sysUser); db.SaveChanges(); } //删除用户 public bool Delete(int id) { var delUser = db.Users.FirstOrDefault(u => u.ID == id); if (delUser != null) { db.Users.Remove(delUser); db.SaveChanges(); return true; } else { return false; } } } }
通过IUserRepository接口对象引用UserRepository类的实例来调用:
IUserRepository ur=new UserRepository();
var user=ur.xxx;
怎么样,平时听到的Repository Pattern实现起来就这么简单。
总结
OK,到此为止,我们对常用的CRUD(增删改查)做了介绍。View, Controller之间都是通过传递Model来交互的。特别要提下下面这张图,通过navigation property实现User ---> UserRole ---> Role 多表间查询。
当然,这种做法还是有局限性的,后续文章中我们会介绍如何实现类似于之前SQL查询多个表,将多个表的查询结果,例如datatable直接传到view中来显示数据。
<dl class="dl-horizontal"> <dt>@Html.DisplayNameFor(model => model.UserName)</dt> <dd>@Html.DisplayFor(model => model.UserName)</dd> <dt>@Html.DisplayNameFor(model => model.Email) </dt> <dd>@Html.DisplayFor(model => model.Email)</dd> <dt>@Html.DisplayNameFor(model => model.Password) </dt> <dd>@Html.DisplayFor(model => model.Password)</dd><dt>@Html.DisplayNameFor(model=>model.UserRoles)</dt> <dd> <table> <tr> <th>规则名称</th> <th>规则描述</th> </tr> @foreach(var item in Model.UserRoles) { <tr> <td>@Html.DisplayFor(modelItem=> item.Role.RoleName)</td> <td>@Html.DisplayFor(modelItem => item.Role.RoleDesc)</td> </tr> } </table> </dd> </dl>