MVC5+EF6 入门完整教程10 数据查询更新

 

关联数据更新有两种情况:

1.一对多

2.多对多

第一种情况关联表有主外键关联,只要简单的更新外键值就可以了(相当于更新单表),我们主要讲解第二种多对多的情况。

使用之前很熟悉的模型:

我们定义一个场景:    

一个用户可以有任意多个角色,一个角色可以有任意多个用户。

我们接下来完成下面操作:

编辑某个用户时,显示该用户的角色进行编辑。

即 更新某个用户(SysUser表)及其相关的角色(SysUserRole表)。

 

详细步骤:

1.先添加一个ViewModel, 用来表示角色是否分配给某个用户。

2.打开UserRoleController,添加一个Edit的Action用来显示编辑页面。

PopulationRoles:将该用户具有的角色存在ViewBag.assignedRole

        private void PopulationRoles(User user)
        {
            var allRoles = db.Roles.ToList();
            var userRoleIDs = new HashSet<int>(user.UserRoles.Select(m => m.RoleID));
            var assignedRole = new List<AssignedRoleData>();
            foreach (var role in allRoles)
            {
                assignedRole.Add(
                    new ViewModels.AssignedRoleData
                    {
                        RoldId = role.ID,
                        RoldName = role.RoleName,
                        Assigned = userRoleIDs.Contains(role.ID)
                    });
            }
            ViewBag.assignedRole = assignedRole;
        }

  

PopulationDepartmentDropDownList:将该用户部门信息存在ViewBag.DepartmentDropDownList

        private void PopulationDepartmentDropDownList(User user)
        {
            // 部门信息
            var allDepartments = db.Departments.ToList();
            ViewBag.DepartmentDropDownList = new SelectList(allDepartments, "id", "name", user.DepartmentID);
            return;
        }

  

        public ActionResult Edit(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            IQueryable<User> users = db.Users
                .Include("Department")
                .Include("UserRoles")
                .Where(u => u.ID == id);

            if (users.Count() < 1) return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            User user = users.Single();
            PopulationRoles(user);
            PopulationDepartmentDropDownList(user.DepartmentID);
            if (user == null)
            {
                return HttpNotFound();
            }
            return View(user);
        }

 

     public ActionResult Index(int? id)
        {
            var viewModel = new UserAndUserRole();
            viewModel.Users = db.Users
                .Include("Department")
                .Include("UserRoles")
            .OrderBy(u => u.UserName).ToList();

            if (id != null)
            {
                ViewBag.UserID = id.Value;
                // 按照用户ID选择1个用户,在选择该用户的UserRoles
                IEnumerable< User> users = viewModel.Users.Where(u => u.ID == id.Value);
                if (users.Count<User>()>0) //用户存在
                {
                    viewModel.UserRoles = users.Single().UserRoles;//该用户的角色 IEnumerable
                    viewModel.Roles = (viewModel.UserRoles.Where(ur => ur.UserID == id.Value)).Select(ur => ur.Role);
                }
            }
            return View(viewModel);
        }

 

 

 3.打开Views\UserRole\Index.cshtml, 增加一个编辑按钮

            <td>
                @{
                    foreach (var userRole in item.UserRoles)
                    {
                        @userRole.Role.RoleName <br />
                    }
                }
            </td>
            <td>
                @Html.ActionLink("Select", "Index", new { id = item.ID }) |
                @Html.ActionLink("Edit", "Edit", new { id = item.ID })
            </td>

  

4.再根据Edit Action自动生成Edit View

修改相关内容,主要是两点:

a.部门  后台向前台传DropDownList信息

        <div class="form-group">
            @Html.LabelFor(model => model.DepartmentID, null, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                @Html.DropDownList("DepartmentID", ViewBag.DepartmentDropDownList as SelectList, htmlAttributes: new { @class = "form -control" })
                @Html.ValidationMessageFor(model => model.DepartmentID, "", new { @class = "text-danger" })
            </div>
        </div>

  

 b.角色  checkbox显示

        <div class="form-group">
            <div class="col-md-offset-2  col-md-10">
                <table>
                    <tr>
                        @{ 
                            List<AssignedRoleData> roles = ViewBag.assignedRole;
                            foreach (var role in roles)
                            {
                                <td>
                                    <input type="checkbox" name="selectedRoles" value="@role.RoldId" 
                                           @(Html.Raw(role.Assigned ? "checked=\"checked\"" : ""))/> 
                                   @role.RoldName
                                </td>
                            }

                        }
                    </tr>
                </table>
            </div>
        </div>

  

 运行下Index页面。

 

 

进入编辑页面。

 

5.最后再完成HttpPost的Edit功能。

首先更新User表:

用model binder中的值更新entity: userToUpdate.

可以看到,我们使用了白名单指定数据库中需要更新的字段。

TryUpdateModel(userToUpdate,"",

new string[] {"LoginName","Email","Password","CreateDate","SysDepartmentID"})

 

再更新SysUserRole表:

将数据库中值和编辑后的值进行比对,基本逻辑是:

如果被选中了,原来没有的要添加;

如果没被选中,原来有的要删除。

UpdateUserRoles(selectedRoles, userToUpdate);

 

        [HttpPost]
        public ActionResult Edit(int? id, string[] selectedRoles) //selectedRoles  view中的名称
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }

            var  userToUpdate = db.Users
                .Include("UserRoles")
                .Where(u => u.ID == id)
                .Single();
            // 显式模型绑定 :将form返回的UserName,Email,Password,CreateDate,DepartmentID值显示绑定到模型实体userToUpdate
            if (TryUpdateModel(userToUpdate,"",new string[] {"UserName","Email","Password","CreateDate","DepartmentID"}))
            {
                try
                {
                    UpdateUserRoles(selectedRoles, userToUpdate);
                    db.Entry(userToUpdate).State = System.Data.Entity.EntityState.Modified;
                    db.SaveChanges();
                    return RedirectToAction("Index");
                }
                catch (Exception e)
                {
                    throw;
                }
            }
            PopulationDepartmentDropDownList(userToUpdate);
            PopulationRoles(userToUpdate);
            return View(userToUpdate);
        }

  

        private void UpdateUserRoles(string[] selectedRoles, User userToUpdate)
        {
            // 没有选择角色,全部清空
            if (selectedRoles == null)
            {
                //用户原有角色
                foreach (var item in userToUpdate.UserRoles.ToList())
                {
                    db.UserRoles.Remove(item);
                }
                db.SaveChanges();
            }
            else
            {
                //选中的角色
                var selectedRolesHS_String = new HashSet<string>(selectedRoles);
                //原来的角色
                var oldRolesHS_Int = new HashSet<int>(userToUpdate.UserRoles.Select(r => r.RoleID));

                foreach (var role in this.db.Roles.ToList()) //比较所有的角色
                {
                    if (selectedRolesHS_String.Contains(role.ID.ToString()))//该角色选中
                    {
                        if (!oldRolesHS_Int.Contains(role.ID))//原来没有选中
                        {
                            UserRole userRole = new UserRole { UserID = userToUpdate.ID, RoleID = role.ID };
                            db.UserRoles.Add(userRole);
                            db.SaveChanges();
                        }
                    }
                    else  //该角色没有选中
                    {
                        if (oldRolesHS_Int.Contains(role.ID))//原来选中
                        {
                            UserRole userRole = db.UserRoles.FirstOrDefault(u => u.RoleID == role.ID && u.UserID == userToUpdate.ID);
                            db.UserRoles.Remove(userRole);
                            db.SaveChanges();
                        }
                    }

                }
            }
        }

  

 

重新运行下Index, 如下一组图,这时我们看到角色编辑已经起作用了。

至此,多表更新的示例就介绍到这,其他情况相信你可以举一反三自己推导出来做法。

 

 

使用原生SQL

使用EF的一个优点就是自动帮我们生成SQL,这在常规情况下很方便,但有些情况下用EF却不适合。

例如我们上面更新SysUserRole这张表时,每次增减一条数据,要循环很多次。

另外还有些特别复杂的语句,利用EF很难生成。

EF提供一组方法用来执行原生的SQL.

有以下三种:

 

例子1:DbSet.SqlQuery查询并返回Entities

我们打开Controllers\AccountController.cs做实验

找到Details方法

将注释的部分改成方框部分即可。

 

 

方框中的和注释掉的内容User user=db.Users.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 User";

var names=db.Database.SqlQuery<string>( query).ToList();

以上会返回一个System.Collections.Generic.List<string>类型。

这种方式和第一种情况最大的区别就是返回non-entity 类型。

我们可以根据需要,自己构建需要的类型。

我们也可以自定义一个entity type让它返回,例如类似我们上一个例子:

User user = db. Database.SqlQuery(query, paras).SingleOrDefault();

这样也可以返回entity, 但要注意,这种方式将不会被context track, 返回后就没关系了,如果我们在View中用类似于Model.XXX导航属性获取其他关联数据就会报错。例如@foreach (var item in Model.UserRoles),这种情况下会报Model为null的错误。

 

例子3:Database.ExecuteSqlCommand执行更新语句

最后一个是更新的,直接看示例就明白了:

context.Database.ExecuteSqlCommand("UPDATE dbo.Posts SET Rating = 5 WHERE Author = @author", new SqlParameter("@author", userSuppliedAuthor));

 

EF6 :  https://docs.microsoft.com/zh-cn/ef/ef6/

最后提下执行存储过程,也类似,我就不多说了,如下MSDN(https://msdn.microsoft.com/en-us/data/jj592907)截图。

 

 

原生SQL使用总结     https://docs.microsoft.com/zh-cn/ef/ef6/querying/raw-sql

原生SQL执行查询:

需要返回实体模型,使用DbSet.SqlQuery (context会跟踪,等效于LINQ方式)

需要返回其他类型,使用Database.SqlQuery

原生SQL执行更新:

使用Database.ExecuteSqlCommand

 

 

 

 

posted @ 2021-01-14 15:35  清语堂  阅读(289)  评论(0编辑  收藏  举报