`
sangei
  • 浏览: 336570 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

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实现数据库中指定字段的更新,这种方法在实际开发中使用完全可行。

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

分享到:
评论

相关推荐

    MVC+EF与数据库实现简单的删除添加修改显示

    - **Update**:更新记录,用户编辑后,Controller接收更新后的数据,通过EF更新数据库。 - **Delete**:删除记录,用户选择要删除的项,Controller处理删除请求,并利用EF执行删除操作。 4. **Solution1.ssmssln*...

    EF批量更新、批量插入、 批量删除使用的是EFUtilities,免费的操作简单,速度超级快

    在.NET开发中,Entity Framework(简称EF)是一个强大的对象关系映射(ORM)框架,它允许开发者使用...通过其简单的API,开发者可以轻松地集成到现有的EF项目中,实现批量插入、更新和删除,从而优化应用程序的性能。

    ASP .net MVC5(C#)新手学习CURD

    控制器的PUT或PATCH方法接收更新的数据,使用EF更新数据库中的相应记录。 **删除(Delete)**: 删除操作一般分为两个步骤:确认和执行。确认阶段,用户看到待删除的记录并有机会取消。执行阶段,如果用户确认,控制...

    EF批量修改删除数据

    4. **批量修改操作**:使用EF-Plus的`DbContext.BulkUpdate()`方法,可以一次性更新数据库中多条记录,只需提供更新条件和更新后的值。 5. **批量删除操作**:同样,`DbContext.BulkDelete()`方法用于批量删除满足...

    EF Core 入门教程

    迁移还包括创建和删除API,以支持数据库的迭代更新。 开发者将学习查询数据库的基本方法,包括基本查询、加载相关数据、客户端与服务器端评估、异步查询以及使用原始SQL查询。此外,还可以了解查询的工作原理,包括...

    EFcore Repository 依赖注入方式实现数据库基本操作

    在.NET Core应用中,Entity Framework Core (EF Core) 是一个轻量级、高性能的对象关系映射(ORM)框架,它允许开发者用C#等语言直接操作数据库。依赖注入(Dependency Injection, DI)是一种设计模式,它有助于降低...

    ef core 与 dapper 组合使用demo

    Code First允许开发者通过定义C#类来创建数据库模型,然后使用EF Core的迁移功能自动创建或更新数据库结构。在实际操作中,这通常涉及到以下步骤: 1. 创建实体类:定义C#类来代表数据库中的表。 2. 配置上下文:...

    学习EFCore.pdf

    ### EF Core 简介 Entity Framework Core(简称EF Core)是一个轻量级、可扩展、跨平台的.NET对象关系映射器(ORM)。EF Core 支持多种数据库提供者,并且可以用于.NET Core以及.NET Framework项目。它提供了数据访问...

    ASP.NET Core 和EF Core官方中文文档

    4. **迁移**:EF Core的迁移功能允许开发者在项目开发过程中跟踪数据库结构的变化,方便地更新数据库架构。 5. **数据库上下文**:DbContext是EF Core的主要入口点,用于管理实体和数据库连接,以及执行查询和保存...

    VS 2019 EF Core Power Tools v2.5.790.vsix

    《深入理解VS 2019与EF Core Power Tools v2.5.790.vsix》 在软件开发的世界中,Visual Studio (VS) 2019 是一款广泛使用的集成开发环境(IDE),它提供了丰富的功能和工具,帮助开发者高效地构建各种应用程序。而 ...

    监控EF执行的SQL

    1. **查看SQL日志**:每个数据库操作都会被记录下来,包括查询、更新、删除和插入操作。 2. **性能分析**:通过查看SQL执行时间,可以找到慢速查询并进行优化。 3. **比较不同实现**:如果你尝试了多种查询方式,...

    C# EF框架 sqlserver

    - **数据库迁移(Database Migrations)**: 允许开发者在模型发生变化时,自动更新数据库结构。 **2. 使用EF6与SQL Server** - **配置连接字符串**: 在Web.config或app.config文件中,需要配置SQL Server的连接字符...

    完整EF实例demo

    **描述:**这个“完整EF实例demo”是一个全面的实体框架(Entity Framework,简称EF)示例,它涵盖了在实际的企业级项目中经常遇到的各种操作,包括添加、删除、查询和更新数据。这个实例是即用型的,意味着下载并...

    EF DataFirst优先

    6. **Migrations**:在EF DataFirst中,当数据库结构发生变化时,可以使用Migrations功能来自动更新数据库结构,确保代码与数据库的一致性。 7. **Entity Data Model (EDM)**:这是EF中的一个概念模型,用于描述...

    dotnet core3.1 EF连接达梦数据库demo

    在本示例中,我们将深入探讨如何在 .NET Core 3.1 框架下使用 Entity Framework Core (EF Core) 连接国产达梦数据库。由于达梦数据库是国产数据库系统,它可能不支持像 SQL Server 或 MySQL 那样的通用驱动,因此...

    EF动态表名 c# 读取不固定表

    // 其他查询、更新或删除操作... } ``` 5. **考虑多表查询** 如果需要同时处理多个动态表,你可能需要在`ModelBuilder`中添加多个`ToTable`调用,或者使用`IIncludableQueryable`来处理多表联接。注意,这可能...

    EF+Mapper结合使用实现Dto到实体类再到数据的(框架模型基础实现)

    在.NET开发领域,Entity Framework(简称EF)是微软提供的一款强大的对象关系映射(ORM)框架,它允许开发者使用面向对象的方式来操作数据库。而Mapper工具则用于对象之间的映射,如AutoMapper,它可以方便地将数据...

    eform自定义表单

    开源社区的存在也意味着eform会持续得到更新和优化,用户可以享受到最新的功能和技术支持。 **数据库备份与恢复**:在提供的压缩包文件中,有一个名为`databasebak.rar`的文件,这通常包含数据库的备份。在开发或...

    EF架构

    **EF架构** Entity Framework(简称EF)是微软提供的一款开源对象关系映射(ORM)框架,用于.NET应用程序。它允许开发人员使用面向对象的编程语言(如C#或VB.NET)来操作数据库,而无需编写大量的SQL代码。通过EF,...

    EF完整实例代码

    这个"EF完整实例代码"应该包含了一系列使用EF进行数据库操作的示例,包括创建数据模型、上下文、查询、插入、更新和删除数据等。 在EF中,数据模型通常通过实体类定义,这些类代表数据库中的表。每个类的属性对应于...

Global site tag (gtag.js) - Google Analytics