.NET Core && EF Core更新部分字段

一、利用TryUpdateModelAsync

public ActionResult Edit(int id)
        {
            var q = dbContext.LeaveRecord.Find(id);
            if (q == null)
            {
                return NotFound();
            }
            return View(q);
        }
 
        // POST: LeaveController/Edit/5, 
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> EditAsync(int? id)
        {
 
            if (id ==null)
            {
                return NotFound();
            }
            if (ModelState.IsValid)
            {
                try
                {
                    var q = dbContext.LeaveRecord.Find(id);
                    if (await TryUpdateModelAsync(q, "", o => o.PersonName, o => o.LeaveType,  o => o.Sex))
                    {
                        await dbContext.SaveChangesAsync();
                    }
                }
                catch
                {
                    return View();
                }
                return RedirectToAction(nameof(Index));
            }
            return View();
        }

此方法首先查询出实体数据,然后根据lambda表达式中的字段进行更新对应的实体字段,注意lambda表达式,都为这样的参数格式o=>o.x1,o=>o.x2而不是o=>o.x1,o.x2

二、查询出实体,然后将表单form中的实体与查询出来的实体对应修改

// GET: LeaveController/Edit/5
        public ActionResult Edit(int id)
        {
            var q = dbContext.LeaveRecord.Find(id);
            if (q == null)
            {
                return NotFound();
            }
            return View(q);
        }
 
        // POST: LeaveController/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult EditAsync(int? id, LeaveRecord leaveRecord)
        {
 
            if (id ==null)
            {
                return NotFound();
            }
            if (ModelState.IsValid)
            {
                try
                {
                    var q = dbContext.LeaveRecord.Find(id);
                    q.PersonName = leaveRecord.PersonName;
                    q.Sex = leaveRecord.Sex;
                    q.LeaveType = leaveRecord.LeaveType;
                    q.LeaveReason = leaveRecord.LeaveReason;
 
                    dbContext.Update(q);
                    dbContext.SaveChanges();
                }
                catch
                {
                    return View();
                }
                return RedirectToAction(nameof(Index));
            }
            return View();
        }

三、字段绑定更新部分,将所有字段值读出来,然后在html中将不更新的字段设置为readonly,这样用户只能操作需要更新的字段值,但这也有一个问题,如果用户懂计算机,将实体在html中的readonly去掉,会导致后台更新数据 的同时会把值更新

view视图文件

<input type="text" readonly asp-for="Title" />
<input type="text" asp-for="Content" />

假设只更新content字段数据

四、利用属性设置更新部分字段,适用于更新字段少的情况,比如实体总共有10个字段,仅更新两个或一个字段的情况下,可以设置如下 :比如只更新了username字段

// GET: UserController/Edit/5
        public ActionResult Edit(int id)
        {
            
            var q = dbContext.User.Find(id);
            return View(q);
        }
 
        // POST: UserController/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(int id, User user)
        {
            try
            {
                dbContext.Attach(user);
                //将需要更新的字段设置为true,没有设置的字段不更新,这种方法不用查询数据库找到实体
                dbContext.Entry(user).Property(o => o.UserName).IsModified = true;
 
                dbContext.SaveChanges();
 
                return RedirectToAction(nameof(Index));
            }
            catch(Exception ex)
            {
                return View();
            }
        }

但如果需要更新的列多的话,会照样写成很多类似语句

dbContext.Entry(user).Property(o => o.UserName).IsModified = true;
dbContext.Entry(user).Property(o => o.UserPassword).IsModified = true;

当然这样的语句可以用循环的方式实现,将需要更新的字段封装起来,然后通过循环遍历设置为true,以下代码可以参考

public void UpdateEntityFields(T entity, List<string> fileds)
        {
            if (entity != null&&fileds!=null)
            {
                dbContext.CreateObjectSet<T>().Attach(entity);
                var SetEntry = ((IObjectContextAdapter)dbContext).ObjectContext.
                    ObjectStateManager.GetObjectStateEntry(entity);
                foreach (var t in fileds)
                {
                    SetEntry.SetModifiedProperty(t);
                }
            }
        }

五、如果更新字段较多,上面的方法都要写一堆需要更新字段的代码,假如有10个字段,更新9个,仅有一个字段不更新,那么,可以用以下方法,此方法适用总字段较多,但更新的字段较少的情况,和上面的正好相反。具体如下:

  // GET: UserController/Edit/5
        public ActionResult Edit(int id)
        {
 
            var q = dbContext.User.Find(id);
            return View(q);
        }
 
        // POST: UserController/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(int id, User user)
        {
            try
            {
                var d = dbContext.Attach(user);
 
                dbContext.Entry(user).State = EntityState.Modified;//将所有字段设置为要更新的状态
                dbContext.Entry(user).Property(o=>o.UserName).IsModified = false;//再将不需要的字段设置为不更新,那么这样就达到了更新多个字段,除此字段外
 
                dbContext.SaveChanges();
 
                return RedirectToAction(nameof(Index));
            }
            catch (Exception ex)
            {
                return View();
            }
        }

注意:加了Attach在SaveChanges()前面一定不要再加dbContext.Update(user);加上此句会出错,导致上面的设置状态无效,会全部更新,所以一定不要加。

六、使用原生SQL执行更新操作

dbContext.Database.ExecuteSqlRaw(“sql语句”,参数1值, 参数2值,…);

 public ActionResult Edit(int id, User user)
        {
            try
            {
                dbContext.Database.ExecuteSqlRaw($"update User set userName='{user.UserName}',userpassword='{user.UserPassword}' where id={user.Id}");
                return RedirectToAction(nameof(Index));
            }
            catch (Exception ex)
            {
                return View();
            }
        }

警告

始终对原始 SQL 查询使用参数化

向原始 SQL 查询引入任何用户提供的值时,必须注意防范 SQL 注入攻击。 除了验证确保此类值不包含无效字符,请始终使用会将值与 SQL 文本分开发送的参数化处理。

具体而言,如果连接和内插的字符串 ($“”) 带有用户提供的未经验证的值,则切勿将其传递到 FromSqlRaw 或 ExecuteSqlRaw。 通过 FromSqlInterpolated 和 ExecuteSqlInterpolated 方法,可采用一种能抵御 SQL 注入攻击的方式使用字符串内插语法。一般这种方法用于内部确定的更新操作。比如将某个字段更新为某个值等。

以上方法必须保证参数值是经过处理的,否则不建议用$""这样的形式。下面的两个方法进行参数化防止sql注入攻击:

第一种简写:

dbContext.Database.ExecuteSqlRaw("update User set userName={0},userpassword={1} where id={2}","user01","123123",3);//将id=3的user用户名和密码进行更新

第二种参数写法:

这里是mysql用 MySqlParameter,sql用SqlParameter,注意对应关系否则出错,代码如下:

int id=3;
dbContext.Database.ExecuteSqlRaw("update User set userName=@userName,userpassword=@password where id=@id",
                new MySqlParameter("@userName", "jone"),
                new MySqlParameter("@password", "12345"),
                new MySqlParameter("@id",id));

上面使用的参数化赋值,参数可与字段名不同,可以参考对比。

FromSqlRaw只用作查询。

params object[] parameters或params object[] args作为方法参数时,指明该参数为可变长度和类型参数,比如“abc",1,true可传入。

前面几种方法根据具体情况使用即可。

关于过度发布:

经常见类似这样的[bind(“”)] User user,为什么要绑定对应字段呢,主要是为了防止过度发布,比如User实体中有3个字段,分别 为id,username,userpassword,在进行更新Edit操作时,虽然表单里对应的只id,name两个input,但下面的代码user对象里是3个字段,黑客会利用工具将userpassword字段什传入了,导致更新时把密码也修改了!

// POST: UserController/Edit/5
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(int id,[Bind("id,username")] User user)

微软官方实例如下图
在这里插入图片描述
防止以上过度发布可以使用Bind属性来绑定需要更新的字段,其它字段即可传送过来也会清空,但这样在更新数据时不能直接dbcontext.update(user)然后进行savechanges()提交。只能查询到的user实体,再将参数中user赋值到需要更新的字段,当然还有更好的办法是使用上面提到的,

dbContext.Entry(user).Property(o => o.UserName).IsModified = true;

将需要更新的字段更新,这种方法不需要进行查询,直接使用的user实体。

参考资料:

EntityState的5个状态解释:

成员名称说明
Detached对象存在,但没有被跟踪。 在创建实体之后、但将其添加到对象上下文之前,该实体处于此状态。 An entity is also in this state after it has been removed from the context by calling the Detach method or if it is loaded by using a NoTrackingMergeOption. 没有 ObjectStateEntry 实例与状态为 Detached 的对象关联。
Unchanged自对象附加到上下文中后,或自上次调用 SaveChanges 方法后,此对象尚未经过修改。
Added对象为新对象,并且已添加到对象上下文,但尚未调用 SaveChanges 方法。 在保存更改后,对象状态将更改为 Unchanged。 状态为 Added 的对象在 ObjectStateEntry 中没有原始值。
Modified对象上的一个标量属性已更改,但尚未调用 SaveChanges 方法。 在不带更改跟踪代理的 POCO 实体中,调用 DetectChanges 方法时,已修改属性的状态将更改为 Modified。 在保存更改后,对象状态将更改为 Unchanged。

莫点劳资

上下文正在跟踪实体的状态。

public enum EntityState

继承

Enum
EntityState

字段
Added4实体正在由上下文跟踪,但数据库中尚不存在。
Deleted2实体正在由上下文跟踪,并存在于数据库中。 已将其标记为从数据库中删除。
Modified3实体正在由上下文跟踪,并存在于数据库中。 已经修改了其部分或全部属性值。
Unchanged1实体正在由上下文跟踪,并存在于数据库中。 不会更改数据库中的值的属性值。
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值