1. 程式人生 > >開發筆記:基於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=@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()) > 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 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)) > 0;
    }     
}

隨之,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.ExtendedEF實現指定更新

今天在將一個專案中使用儲存過程的遺留程式碼遷移至新的架構時,遇到了一個問題——如何用EF實現資料庫中指定欄位的更新(根據UserId更新Users表中的FaceUrl與AvatarUrl欄位)? 原先呼叫儲存過程的程式碼: public bool UpdateAvatar(Guid userId, s

sql update 一個表的更新另一個表

UPDATE  a  SET a.areacode=b.areacode FROM [dbo].[T_Mid_AirPoll] a ,[dbo].[T_Bas_AirStation] b   WHER

資料脫敏——基於Java自定義註解實現日誌脫敏

  上文說了資料過敏主要有兩個思路:第一個就是在序列化實體之前先把需要脫敏的欄位進行處理,之後正常序列化;第二個就是在實體序列化的時候,對要脫敏的欄位進行處理。 脫敏實現思路   這裡探討第一種方法,用基於自定義註解的方式實現日誌脫敏。   要對

MongoTemplate查詢指定的正確奧義

這裡舉一個我個人犯的一個錯誤,希望看到此博文的人能避免犯同樣的錯誤: 在CompanyInfo這個類中,它其中包含一個List型別的AccountInfo屬性。然後好戲開始了,我希望通過BasicDB

SAP公開課筆記基於HANA的軟體開發

第一章 通過OData暴露和消費資料的基礎架構 這章的內容很簡單。在開始真正開始開發之前,回顧了一下第一章所提到過的傳統軟體開發架構和使用XS架構的區別。值得記下來的是ICM是整個架構的一部分。 SAP internet communication manager(ICM)

開發筆記不用UnitOfWork以及Repository返回什麼集合型別

這2天實際開發中明確的東西,在這篇博文中記錄一下。 之前對是否需要自己封裝UnitOfWork有些猶豫,因為Entity Framework就是一個UnitOfWork實現, 自己再封裝一下顯得有些多餘。 但是在這次開發中,把涉及資料庫操作的實現程式碼放在最後寫,先完成其他層的程式碼。這種情況下,根本用不

張高興的 Windows 10 IoT 開發筆記使用 ULN2003A 控制步進電機

uln2003 zhang windows iot ges 開發 ima dem win   GitHub:https://github.com/ZhangGaoxing/windows-iot-demo/tree/master/ULN2003A   張高興的 Wind

張高興的 Windows 10 IoT 開發筆記三軸數字羅盤 HMC5883L

cnblogs -i mas https http png 開發 target 分享   註意,數據不包含校驗,準確的來說我不知道怎麽校驗,但方向看起來差不多是對的。。。   GitHub:https://github.com/ZhangGaoxing/windows-io

張高興的 Xamarin.Forms 開發筆記為 Android 與 iOS 引入 UWP 風格的漢堡菜單 ( MasterDetailPage )

操作 using eat stp 取消 height 新建 屬性 turn   所謂 UWP 樣式的漢堡菜單,我曾在“張高興的 UWP 開發筆記:漢堡菜單進階”裏說過,也就是使用 Segoe MDL2 Assets 字體作為左側 Icon,並且左側使用填充顏色的矩形用來表示

張高興的 Windows 10 IoT 開發筆記BMP180 氣壓傳感器

bsp git left margin 氣壓 play 氣壓傳感器 分享 get 原文:張高興的 Windows 10 IoT 開發筆記:BMP180 氣壓傳感器  註意:海拔高度僅供參考   GitHub : https://github.com/ZhangGaoxin

張高興的 Windows 10 IoT 開發筆記使用 MAX7219 驅動 8×8 點陣

.com 使用 window win get mas image href git 原文:張高興的 Windows 10 IoT 開發筆記:使用 MAX7219 驅動 8×8 點陣  GitHub:https://github.com/ZhangGaoxing/w

張高興的 Windows 10 IoT 開發筆記DHT11 溫濕度傳感器

分享 lock spl .com play ima image log win 原文:張高興的 Windows 10 IoT 開發筆記:DHT11 溫濕度傳感器  GitHub : https://github.com/ZhangGaoxing/windows-iot-de

張高興的 Windows 10 IoT 開發筆記ToF Sensor VL53L0X

mar 分享 demo isp 技術 ref windows ges mas   GitHub : https://github.com/ZhangGaoxing/windows-iot-demo/tree/master/VL53L0X 張高興的 Windows 10 Io

張高興的 Windows 10 IoT 開發筆記紅外溫度傳感器 MLX90614

ima master lock right win hang tps iot pla   GitHub : https://github.com/ZhangGaoxing/windows-iot-demo/tree/master/MLX90614 張高興的 Windows

張高興的 Windows 10 IoT 開發筆記使用 Lightning 中的軟件 PWM 驅動 RGB LED

pic pwm 原生 感覺 發現 rgb rgb led ace light 感覺又幫 Windows 10 IoT 開荒了,所以呢,正兒八經的寫篇博客吧。其實大概半年前就想寫的,那時候想做個基於 Windows 10 IoT 的小車,但樹莓派原生不支持 PWM 啊。百度也

android開發筆記MainActivity.java與activity_main.xml

https://www.jianshu.com/p/f5e56fb2f215 剛開始開發android的時候,新建一個activity總是會新建兩個檔案,我們已預設命名MainActivity.java與activity_main.xml兩個檔案來給大家介紹。 activity

開發實戰基於深度學習+maven+SSM+EasyUI的高校共享汽車管理系統(二)

基於深度學習+maven+SSM+EasyUI的高校共享汽車管理系統   繼上一篇 [專案需求分析](https://blog.csdn.net/ITBigGod/article/details/82729233)之後,接下來就是資料庫設計了。      作為一個管理系統,各種資訊表是必

開發實戰基於深度學習+maven+SSM+EasyUI的高校共享汽車管理系統(一)

基於深度學習+maven+SSM+EasyUI的高校共享汽車管理系統 1.專案簡介   在現在,共享汽車在中國各地方開始熱起來,於是本人想做一個基於maven+SSM+EasyUI的高校共享汽車管理系統,當然該專案是博主本人2019年的畢業設計,除了javaweb部分,本專案還

Linux應用程式開發筆記測試程式碼執行時間

  #include <stdio.h> #include <sys/times.h> #include <unistd.h> void main(void) { double duration; clock_t start,

Linux應用程式開發筆記make menuconfig環境搭建

1、目的 Linux應用程式開發採用與Linux核心一致的menuconfig圖形配置,方便功能元件裁剪。   2、準備工作 下載:Kconfiglib原始碼(https://github.com/ulfalizer/Kconfiglib)   3、環境搭