开发笔记:基于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) > ;
}
存储过程中所使用的SQL语句:
UPDATE Users
SET FaceUrl=@FaceUrl,AvatarUrl=@AvatarUrl
WHERE [UserId]=@UserId
在新的架构中,数据库访问用的是Entity Framework,并且用IUnitOfWork接口进行了封装,Application层对数据库的操作是通过IUnitOfWork接口完成的。
IUnitOfWork接口是这么定义的:
public interface IUnitOfWork : IDisposable
{
IQueryable<T> Set<T>() where T : class; T Add<T>(T entity) where T : class; IEnumerable<T> AddRange<T>(IEnumerable<T> entities) where T : class; T Attach<T>(T entity) where T : class; T Remove<T>(T entity) where T : class; IEnumerable<T> RemoveRange<T>(IEnumerable<T> entities) where T : class; bool Commit(); Task<bool> CommitAsync();
}
如果不给IUnitOfWork添加新的接口方法,可以使用EF的change tracking完成指定字段的更新操作。
Application.Services中的实现代码:
public async Task<bool> 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<bool> UpdateAsync(User user)
{
base.Set<User>().Attach(user);
base.Entry<User>(user).Property(x => x.FaceUrl).IsModified = true;
base.Entry<User>(user).Property(x => x.AvatarUrl).IsModified = true;
return (await base.SaveChangesAsync()) > ;
}
}
调用代码:
await UpdateAsync(new User { UserId = userId, FaceUrl = faceUrl, AvatarUrl = avatarUrl });
但是基于这个实现,很难抽象出一个好用的接口方法。
后来突然想到前一段时间在一个项目中用到的EntityFramework.Extended。针对Update操作,它实现了一个优雅的EF扩展,为何不直接用它呢?
//example of using an IQueryable as the filter for the update
var 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<bool> UpdateAsync<T>(IQueryable<T> source, Expression<Func<T, T>> updateExpression) where T : class;
}
然后在IUnitOfWork.UpdateAsync()的实现中,调用EntityFramework.Extended的UpdateAsync扩展方法,完成Update操作:
using EntityFramework.Extensions;
public class EfUnitOfWork : DbContext, IUnitOfWork
{
async Task<bool> IUnitOfWork.UpdateAsync<T>(IQueryable<T> source,
Expression<Func<T, T>> updateExpression)
{
return (await source.UpdateAsync<T>(updateExpression)) > ;
}
}
随之,Application.Services中的实现代码改为这样:
public async Task<bool> UpdateFaceAvatar(Guid userId, string faceUrl, string avatarUrl)
{
IQueryable<User> userQuery = _userRepository.GetByUserId(userId);
return await _unitOfWork.UpdateAsync<User>(
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
1 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实现指定字段的更新的更多相关文章
- 采用EntityFramework.Extended 对EF进行扩展(Entity Framework 延伸系列2)
前言 Entity Framework 延伸系列目录 今天我们来讲讲EntityFramework.Extended 首先科普一下这个EntityFramework.Extended是什么,如下: 这 ...
- EntityFramework.Extended 对EF进行扩展
前言 Entity Framework 延伸系列目录 今天我们来讲讲EntityFramework.Extended 首先科普一下这个EntityFramework.Extended是什么,如下: 这 ...
- 采用EntityFramework.Extended 对EF进行扩展
今天我们来讲讲EntityFramework.Extended 首先科普一下这个EntityFramework.Extended是什么,如下: 这是一个对Entity Framework进行扩展的类库 ...
- iOS开发笔记 基于wsdl2objc调用asp.net WebService
1.准备 先下载待会要用到的工具 WSDL2ObjC-0.6.zip WSDL2ObjC-0.7-pre1.zip 我用的是WSDL2ObjC-0.6.zip 1.1搭建asp.net WebServ ...
- IOS开发笔记 - 基于SDWebImage的网络图片加载处理
前言: 在IOS下通过URL读一张网络图片并不像Asp.net那样可以直接把图片路径放到图片路径的位置就ok, 而是需要我们通过一段类似流的方式去加载网络图片,接着才能把图片放入图片路径显示. 这里找 ...
- IOS开发笔记 - 基于wsdl2objc调用webservice
为了方便在ios下调用webserivce,找来了wsdl2objc这样一个开源的框架来解析webservice方便在ios下引用. 下面做个小例子. 1.首先是用Asp.net搭建一个测试的webs ...
- 安卓开发笔记(十九):异步消息处理机制实现更新软件UI
主界面代码 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:andr ...
- odoo开发笔记 -- 传递上下文实现列表视图按照指定条件过滤显示
按钮传递上下文: <xpath expr="//div[@name='dec_work_sheet_id']" position="after"> ...
- entity framework 新手入门篇(4)-entity framework扩展之 entityframework.extended
对于EF的操作,我们已经有了大概的了解了,但对于实战来说,似乎还欠缺着一些常用的功能,那就是批量的删除,更新数据. 承接上面的部分,我们有一个叫做House的数据库,其中包含house表和seller ...
随机推荐
- MPI之求和
// MPI1.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include "mpi.h" #include &l ...
- 转 jdk1.5新特性 ConcurrentHashMap
ConcurrentHashMap特点:效率比Hashtable高,并发性比hashmap好.结合了两者的特点. 集合是编程中最常用的数据结构.而谈到并发,几乎总是离不开集合这类高级数据结构的支持 ...
- 在chrome下的文本框sendkeys,提示element can't focus--解决方法
在chrome下的文本框sendkeys,提示element can't focus--解决方法(成都-半步流雲,群友解决) 成都-半步流雲1.升级你的chromedriver,2.降chrome版本 ...
- 大气漂亮的jQuery水晶样式下拉菜单
预览地址:http://demo.zzblo.com/sc/2013/11/201311131041 下载地址:http://sc.zzblo.com/js/201311/79.html
- Android Meun 用法
Android Meun 用法 点击菜单实体键弹出菜单:如下图 main_activity.xml <?xml version="1.0" encoding="ut ...
- 使用ABP时报错“UPDATE 语句与 FOREIGN KEY SAME TABLE 约束"FK_dbo.AbpUsers_dbo.AbpUsers_LastModifierUserId"冲突”的解决办法
ABP理论学习总目录 一步一步使用ABP框架搭建正式项目系列教程 ABP之Module-Zero学习目录 本篇目录 问题 原因 解决办法 问题 问题的是在下面这种情况下出现的: 我在使用CodeFir ...
- 可在广域网部署运行的QQ高仿版 -- GG叽叽V2.4,增加远程协助、桌面共享功能(源码)
QQ的远程协助.或者说桌面共享是一个非常实用的功能,所以,2.4版本的GG复制了它,而且,GG增强了桌面共享的功能,它可以允许指定要共享桌面的区域,这样,对方就只能看到指定区域的桌面,这对节省流量会非 ...
- 【Bugly干货分享】一起用 HTML5 Canvas 做一个简单又骚气的粒子引擎
Bugly 技术干货系列内容主要涉及移动开发方向,是由Bugly邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创,转载请标明出处. 前言 好吧,说是“粒子引擎”还是大言不 ...
- js模版引擎handlebars.js实用教程——each-基本循环使用方法
返回目录 <!DOCTYPE html> <html> <head> <META http-equiv=Content-Type content=" ...
- 成都亚马逊AWSome Day回顾
6月25日我和公司同仁一起参加了亚马逊在成都的第一场AWSome Day活动.整个活动时间异常紧促,短短一天包含了7堂session,讲师的狂轰乱炸使得我们同学们普遍觉得比上班累多了.好了,废话不多说 ...