千家信息网

EntityFramework.Extended如何用EF实现指定字段的更新

发表于:2024-10-02 作者:千家信息网编辑
千家信息网最后更新 2024年10月02日,EntityFramework.Extended如何用EF实现指定字段的更新,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。今天在将一个项
千家信息网最后更新 2024年10月02日EntityFramework.Extended如何用EF实现指定字段的更新

EntityFramework.Extended如何用EF实现指定字段的更新,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

今天在将一个项目中使用存储过程的遗留代码迁移至新的架构时,遇到了一个问题--如何用EF实现数据库中指定字段的更新(根据UserId更新Users表中的FaceUrl与AvatarUrl字段)?

原先调用存储过程的代码:

public bool UpdateAvatar(Guid userId, string faceUrl, string avatarUrl)
{
DbCommand command = _db.GetStoredProcCommand("User_UpdateFaceAvatar");
_db.AddInParameter(command, "@FaceUrl", DbType.String, faceUrl);
_db.AddInParameter(command, "@AvatarUrl", DbType.String, avatarUrl);
_db.AddInParameter(command, "@UserId", userId);
return _db.ExecuteNonQuery(command) > 0;
}

存储过程中所使用的SQL语句:

UPDATE Users SET FaceUrl=@FaceUrl,AvatarUrl=@AvatarUrlWHERE [UserId]=@UserId

在新的架构中,数据库访问用的是Entity Framework,并且用IUnitOfWork接口进行了封装,Application层对数据库的操作是通过IUnitOfWork接口完成的。

IUnitOfWork接口是这么定义的:

public interface IUnitOfWork : IDisposable
{
IQueryable Set() where T : class;

T Add(T entity) where T : class;

IEnumerable AddRange(IEnumerable entities) where T : class;

T Attach(T entity) where T : class;

T Remove(T entity) where T : class;

IEnumerable RemoveRange(IEnumerable entities) where T : class;

bool Commit();

Task CommitAsync();
}

如果不给IUnitOfWork添加新的接口方法,可以使用EF的change tracking完成指定字段的更新操作。

Application.Services中的实现代码:

public async Task UpdateFaceAvatar(Guid userId, string faceUrl, string avatarUrl)
{
var user = await _userRepository.GetByUserId(userId);
user.FaceUrl = faceUrl;
user.AvatarUrl = avatarUrl;
return await _unitOfWork.CommitAsync();
}

使用ef change tracking的优点是如果属性的值没有被改变,就不会触发数据库更新操作,缺点是每次更新前都要进行1次查询操作。

而对于这里的更新FaceUrl与AvatarUrl的应用场景,更新前就明确知道数据已经改变,直接更新数据库即可。ef change tracking的更新前查询不仅没有必要,而且增加了额外的开销。

于是,尝试寻找新的解决方法。

开始尝试的是EF的DbEntityEntry,未抽象的实现代码:

public class EfUnitOfWork : DbContext, IUnitOfWork
{
public async Task UpdateAsync(User user)
{
base.Set().Attach(user);
base.Entry(user).Property(x => x.FaceUrl).IsModified = true;
base.Entry(user).Property(x => x.AvatarUrl).IsModified = true;
return (await base.SaveChangesAsync()) > 0;
}
}

用代码:

await UpdateAsync(new User { UserId = userId, FaceUrl = faceUrl, AvatarUrl = avatarUrl });

但是基于这个实现,很难抽象出一个好用的接口方法。

后来突然想到前一段时间在一个项目中用到的EntityFramework.Extended。针对Update操作,它实现了一个优雅的EF扩展,为何不直接用它呢?

//example of using an IQueryable as the filter for the updatevar users = context.Users.Where(u => u.FirstName == "firstname");context.Users.Update(users, u => new User {FirstName = "newfirstname"});

于是,如获珍宝地基于EntityFramework.Extended进行实现。

首先在IUnitOfWork中添加一个UpdateAsync()的接口方法:

public interface IUnitOfWork : IDisposable{    Task UpdateAsync(IQueryable source, Expression> updateExpression) where T : class;}

然后在IUnitOfWork.UpdateAsync()的实现中,调用EntityFramework.Extended的UpdateAsync扩展方法,完成Update操作:

using EntityFramework.Extensions;
public class EfUnitOfWork : DbContext, IUnitOfWork
{
async Task IUnitOfWork.UpdateAsync(IQueryable source,
Expression> updateExpression)
{
return (await source.UpdateAsync(updateExpression)) > 0;
}
}

随之,Application.Services中的实现代码改为这样:

public async Task UpdateFaceAvatar(Guid userId, string faceUrl, string avatarUrl)
{
IQueryable userQuery = _userRepository.GetByUserId(userId);
return await _unitOfWork.UpdateAsync(
userQuery,
x => new User { FaceUrl = faceUrl, AvatarUrl = avatarUrl }
);
}

(注:这里的_userRepository.GetByUserId的返回类型需要改为IQueryable,再一次验证了Repository返回IQueryable的必要性,相关博文:开发笔记:用不用UnitOfWork以及Repository返回什么集合类型)

跑单元测试验证一下。

单元测试代码:

[Fact]
public async Task UpdateFaceAvatar()
{
var result = await _userService.UpdateFaceAvatar(userId, faceUrl, avatarUrl);
Assert.True(result);
}

试结果:

1 passed, 0 failed, 0 skipped, took 11.38 seconds (xUnit.net 1.9.1 build 1600).

测试通过!

用SQL Profiler查看一下数据库中实际执行的SQL语句:

exec sp_executesql N'UPDATE [dbo].[Users] SET
[FaceUrl] = @p__update__0,
[AvatarUrl] = @p__update__1
FROM [dbo].[Users] AS j0 INNER JOIN (
SELECT
AS [C1],
[Extent1].[UserId] AS [UserId]
FROM [dbo].[Users] AS [Extent1]
WHERE [Extent1].[UserId] = @p__linq__0
) AS j1 ON (j0.[UserId] = j1.[UserId])',N'@p__linq__0 uniqueidentifier,@p__update__0 nvarchar(24),@p__update__1 nvarchar(24)',
@p__linq__0='BD42420B-63CF-DD11-9E4D-001CF0CD104B',@p__update__0=N'20150810180742.png',@p__update__1=N'20150810180743.png'

就是我们期望的SQL语句。

最终验证了,添加IUnitOfWork.UpadteAsync()接口,基于EntityFramework.Extended,用EF实现数据库中指定字段的更新,这种方法在实际开发中使用完全可行。

于是,又少了一个使用存储过程的理由。

看完上述内容,你们掌握EntityFramework.Extended如何用EF实现指定字段的更新的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注行业资讯频道,感谢各位的阅读!

更新 数据 方法 代码 接口 数据库 字段 过程 存储 语句 问题 测试 验证 必要 中指 内容 单元 实际 更多 架构 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 网络安全普及教育的报告 银河麒麟在什么服务器硬件上 阿里云服务器0元 外卖系统数据库 数据库的启动方法一共几种 正定开展网络安全宣传 数据库权限有哪三种 全省网络安全与信息产业发展 辽源安全的计算机网络技术 我的世界服务器所需要的指令 单位网络安全检查信息 怎么通过代理服务器访问国外网址 宇泰串口服务器 路由器未找到pppoe服务器 连云港有哪些科学期刊数据库 网络安全法是哪一年发布 公安部网络安全保卫局办公室 软件开发入账科目 王者服务器爆满可以还区吗 软件开发架构师用什么语言 服务器搭建聊天软件 邮件服务器设备多少钱 网络安全运维检查模板 福州氢氧互联网科技有限公司 mysql登录远程数据库 青浦区提供软件开发服务报价表 电力设备数据库构建 生物医学数据库建立 北京网络技术咨询是什么 服务器管理系统的软件
0