您好,登錄后才能下訂單哦!
EntityFramework.Extended如何用EF實現(xiàn)指定字段的更新,相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。
今天在將一個項目中使用存儲過程的遺留代碼遷移至新的架構(gòu)時,遇到了一個問題——如何用EF實現(xiàn)數(shù)據(jù)庫中指定字段的更新(根據(jù)UserId更新Users表中的FaceUrl與AvatarUrl字段)?
原先調(diào)用存儲過程的代碼:
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
在新的架構(gòu)中,數(shù)據(jù)庫訪問用的是Entity Framework,并且用IUnitOfWork接口進行了封裝,Application層對數(shù)據(jù)庫的操作是通過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中的實現(xiàn)代碼:
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的優(yōu)點是如果屬性的值沒有被改變,就不會觸發(fā)數(shù)據(jù)庫更新操作,缺點是每次更新前都要進行1次查詢操作。
而對于這里的更新FaceUrl與AvatarUrl的應(yīng)用場景,更新前就明確知道數(shù)據(jù)已經(jīng)改變,直接更新數(shù)據(jù)庫即可。ef change tracking的更新前查詢不僅沒有必要,而且增加了額外的開銷。
于是,嘗試尋找新的解決方法。
開始嘗試的是EF的DbEntityEntry,未抽象的實現(xiàn)代碼:
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()) > 0;
}
}
用代碼:
await UpdateAsync(new User { UserId = userId, FaceUrl = faceUrl, AvatarUrl = avatarUrl });
但是基于這個實現(xiàn),很難抽象出一個好用的接口方法。
后來突然想到前一段時間在一個項目中用到的EntityFramework.Extended。針對Update操作,它實現(xiàn)了一個優(yōu)雅的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進行實現(xiàn)。
首先在IUnitOfWork中添加一個UpdateAsync()的接口方法:
public interface IUnitOfWork : IDisposable { Task<bool> UpdateAsync<T>(IQueryable<T> source, Expression<Func<T, T>> updateExpression) where T : class; }
然后在IUnitOfWork.UpdateAsync()的實現(xiàn)中,調(diào)用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)) > 0;
}
}
隨之,Application.Services中的實現(xiàn)代碼改為這樣:
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的必要性,相關(guān)博文:開發(fā)筆記:用不用UnitOfWork以及Repository返回什么集合類型)
跑單元測試驗證一下。
單元測試代碼:
[Fact]
public async Task UpdateFaceAvatar()
{
var result = await _userService.UpdateFaceAvatar(userId, faceUrl, avatarUrl);
Assert.True(result);
}
試結(jié)果:
1 passed, 0 failed, 0 skipped, took 11.38 seconds (xUnit.net 1.9.1 build 1600).
測試通過!
用SQL Profiler查看一下數(shù)據(jù)庫中實際執(zhí)行的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實現(xiàn)數(shù)據(jù)庫中指定字段的更新,這種方法在實際開發(fā)中使用完全可行。
于是,又少了一個使用存儲過程的理由。
看完上述內(nèi)容,你們掌握EntityFramework.Extended如何用EF實現(xiàn)指定字段的更新的方法了嗎?如果還想學到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。