EntityFramework.Extended如何用EF实现指定字段的更新,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
成都创新互联主要从事网站建设、成都网站制作、网页设计、企业做网站、公司建网站等业务。立足成都服务乌苏,十年网站建设经验,价格优惠、服务专业,欢迎来电咨询建站服务:028-86922220
今天在将一个项目中使用存储过程的遗留代码迁移至新的架构时,遇到了一个问题——如何用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
T Add
IEnumerable
T Attach
T Remove
IEnumerable
bool Commit();
Task
}
如果不给IUnitOfWork添加新的接口方法,可以使用EF的change tracking完成指定字段的更新操作。
Application.Services中的实现代码:
public async Task
{
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
{
base.Set
base.Entry
base.Entry
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 { TaskUpdateAsync (IQueryable source, Expression > updateExpression) where T : class; }
然后在IUnitOfWork.UpdateAsync()的实现中,调用EntityFramework.Extended的UpdateAsync扩展方法,完成Update操作:
using EntityFramework.Extensions;
public class EfUnitOfWork : DbContext, IUnitOfWork
{
async Task
Expression
{
return (await source.UpdateAsync
}
}
随之,Application.Services中的实现代码改为这样:
public async Task
{
IQueryable
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实现指定字段的更新的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注创新互联行业资讯频道,感谢各位的阅读!