【EF】Entity Framework Core 软删除与查询过滤器
本文翻译自《Entity Framework Core: Soft Delete using Query Filters》,由于水平有限,故无法保证翻译完全正确,欢迎指出错误。谢谢!
注意:我使用的是 Entity Framework Core 2.0 (2.0.0-preview2-final)。正式版发布后,功能可能存在变动。
继续探索Entity Framework Core 2.0,今天我将探讨如何轻松使用软删除(或逻辑删除)。我的意思是以透明的方式实现软删除,例如,您是物理上的删除行。
要实现软删除,您需要添加一列以指示该行数据是否被逻辑删除。如果您想知道该行被删除,可以使用布尔列,如果您想知道删除的时间,可以使用日期列。其次是更改所有查询,使用此列过滤结果集;您还需要将删除语句替换成为更新语句。
现在我们来看看如何用 Entity Framework Core 来实现这两件事!
添加IsDeleted列
实体框架核心提供了非常灵活的映射。在上一篇关于跟踪列(英文原文)的博客中,您将找到映射列的3种方法。在介绍中,我已经说过软删除应该是透明的,所以我决定在类型中不暴露IsDeleted属性。类型定义如下:
public class Blog
{
    public int BlogId { get; set; }
    public string Url { get; set; }
    public List<Post> Posts { get; set; }
}
public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public int BlogId { get; set; }
    public Blog Blog { get; set; }
}
现在,我们需要向 Entity Framework Core 指明类型有一个附加列:
public class BloggingContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }
    public DbSet<Post> Posts { get; set; }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<Post>()
            .Property<bool>("IsDeleted");
    }
}
更改插入、删除查询
Entity Framework Core 使用ChangeTracker存储所有的更改。您可以在EF生成SQL语句和执行这些语句之前修改ChangeTracker。
public class BloggingContext : DbContext
{
    public override int SaveChanges(bool acceptAllChangesOnSuccess)
    {
        OnBeforeSaving();
        return base.SaveChanges(acceptAllChangesOnSuccess);
    }
    public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default(CancellationToken))
    {
        OnBeforeSaving();
        return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
    }
    private void OnBeforeSaving()
    {
        foreach (var entry in ChangeTracker.Entries<Post>())
        {
            switch (entry.State)
            {
                case EntityState.Added:
                    entry.CurrentValues["IsDeleted"] = false;
                    break;
                case EntityState.Deleted:
                    entry.State = EntityState.Modified;
                    entry.CurrentValues["IsDeleted"] = true;
                    break;
            }
        }
    }
}
现在生成以下代码执行的SQL语句:
using (var context = new BloggingContext())
{
    var post = new Post { Blog = blog };
    context.Posts.Add(post);
    context.SaveChanges();
}
exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [Posts] ([BlogId], [Content], [IsDeleted], [Title])
VALUES (@p1, @p2, @p3, @p4);
SELECT [PostId]
FROM [Posts]
WHERE @@ROWCOUNT = 1 AND [PostId] = scope_identity();
-- @p3 is 0 (false)
',N'@p1 int,@p2 nvarchar(4000),@p3 bit,@p4 nvarchar(4000)',@p1=1,@p2=NULL,@p3=0,@p4=NULL
    context.Posts.Remove(post);
    context.SaveChanges();
exec sp_executesql N'SET NOCOUNT ON;
UPDATE [Posts] SET [BlogId] = @p0, [Content] = @p1, [IsDeleted] = @p2, [Title] = @p3
WHERE [PostId] = @p4;
SELECT @@ROWCOUNT;
',N'@p4 int,@p0 int,@p1 nvarchar(4000),@p2 bit,@p3 nvarchar(4000)',@p4=1,@p0=1,@p1=NULL,@p2=1,@p3=NULL
插入和删除请求已经被处理,您现在还必须更改所有查询语句。
更改查询语句
Entity Framework Core 2.0 引入了一个新的概念:查询过滤器。查询过滤器总是在生成的查询语句后面追加一个的where子句。这意味着,您可以在模型创建时声明一个实体的过滤器,然后将此过滤器隐式添加到使用该表的生成的每个查询语句中。
public class BloggingContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<Post>()
            .Property<bool>("IsDeleted");
        modelBuilder.Entity<Post>()
            .HasQueryFilter(post => EF.Property<bool>(post, "IsDeleted") == false);
    }
}
让我们看看查询过滤器的作用:
 var posts = context.Posts.ToList();
SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[IsDeleted], [p].[Title]
FROM [Posts] AS [p]
WHERE [p].[IsDeleted] = 0 -- Query filter
查询过滤器也可以用于关联查询:
 var blogs = context.Blogs.Include(_ => _.Posts);
SELECT [_].[BlogId], [_].[Url]
FROM [Blogs] AS [_]
ORDER BY [_].[BlogId]
SELECT [p].[PostId], [p].[BlogId], [p].[Content], [p].[IsDeleted], [p].[Title]
FROM [Posts] AS [p]
INNER JOIN (
    SELECT [_0].[BlogId]
    FROM [Blogs] AS [_0]
) AS [t] ON [p].[BlogId] = [t].[BlogId]
WHERE [p].[IsDeleted] = 0 -- Query filter
ORDER BY [t].[BlogId]
通过查询过滤器实现软删除非常容易 :)
查询软删除的行
如果您要还原已删除的行,您必须能够查询到这些数据。这意味着您需要临时删除查询过滤器。EF已经添加了一种新方法IgnoreQueryFilters来表明您不希望将查询过滤器用于当前查询。
var deletedPosts = context.Posts.IgnoreQueryFilters()
                    .Where(post => EF.Property<bool>(post, "IsDeleted") == true);
恢复已删除的帖子有点啰嗦,您需要更改跟踪器中查询并更新IsDeleted属性。
var deletedPosts = context.Posts.IgnoreQueryFilters().Where(post => EF.Property<bool>(post, "IsDeleted") == true);
foreach (var deletedPost in deletedPosts)
{
    var postEntry = context.ChangeTracker.Entries<Post>().First(entry => entry.Entity == deletedPost);
    postEntry.Property("IsDeleted").CurrentValue = false;
}
context.SaveChanges();
总结
使用Entity Framework Core 2.0实现软删除模式非常简单,并且可以是透明的。实际上,您可以无需更改LINQ代码的情况下,将软删除添加到现有模型中。
转载请注明出处,原文链接:http://www.cnblogs.com/tdfblog/p/entity-framework-core-soft-delete-using-query-filters.html。
【EF】Entity Framework Core 软删除与查询过滤器的更多相关文章
- Entity Framework Core 软删除与查询过滤器
		
本文翻译自<Entity Framework Core: Soft Delete using Query Filters>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 注意 ...
 - Entity Framework Core  关联删除
		
关联删除通常是一个数据库术语,用于描述在删除行时允许自动触发删除关联行的特征:即当主表的数据行被删除时,自动将关联表中依赖的数据行进行删除,或者将外键更新为NULL或默认值. 数据库关联删除行为 我们 ...
 - EF Entity Framework Core DBContext中文文档
		
Add(Object) 以添加状态开始跟踪给定的实体和任何其他尚未被跟踪的可访问实体,以便在调用SaveChanges()时将它们插入数据库.使用State设置单个实体的状态. Add<TEnt ...
 - 张高兴的 Entity Framework Core 即学即用:(一)创建第一个 EF Core 应用
		
写在前面 Entity Framework Core (EF Core) 是 .NET 平台流行的对象关系映射(ORM)框架.虽然 .NET 平台中 ORM 框架有很多,比如 Dapper.NHibe ...
 - Entity Framework Core系列之DbContext(删除)
		
上一篇我们介绍了Entity Framework Core系列之DbContext(修改),这一篇我们介绍下删除数据 修改实体的方法取决于context是否正在跟踪需要删除的实体. 下面的示例中con ...
 - 【EF Core】Entity Framework Core 批处理语句
		
在Entity Framework Core (EF Core)有许多新的功能,最令人期待的功能之一就是批处理语句.那么批处理语句是什么呢?批处理语句意味着它不会为每个插入/更新/删除语句发送单独的请 ...
 - 【EF】Entity Framework Core 2.0 特性介绍和使用指南
		
阅读目录 前言 获取和使用 新特性 项目升级和核心API变化 下一步计划 遗憾的地方 回到目录 前言 这是.Net Core 2.0生态生态介绍的最后一篇,EF一直是我喜欢的一个ORM框架,随着版本升 ...
 - Entity Framework Core Like 查询揭秘
		
在Entity Framework Core 2.0中增加一个很酷的功能:EF.Functions.Like(),最终解析为SQL中的Like语句,以便于在 LINQ 查询中直接调用. 不过Entit ...
 - 使用Entity Framework Core需要注意的一个全表查询问题
		
.NET Core 迁移工作如火如荼,今天在使用 Entity Frameowork Core(又名EF Core)时写了下面这样的 LINQ 查询表达式: .Where(u => u.Id = ...
 
随机推荐
- 优步UBER司机全国各地奖励政策汇总:北京、上海、广州、深圳、佛山、天津、南京、武汉、成都、重庆、济南、西安、宁波、青岛、长沙、苏州
			
Uber当周奖励政策 当前奖励包括:高峰翻倍奖励.行程奖励.金牌司机奖励 获得任何奖励的前提条件: 当周评分高于4.7分,当周成单率高于45%,且当周完成至少5单(含5单) 滴滴快车单单2.5倍,注册 ...
 - 创龙OMAPL138的SPI FLASH读写
			
1. 目前最大的疑问是OMAPL138和DSP6748的DSP部分是完全一样的吗(虽然知道芯片完全是引脚兼容的)?因此现在使用OMAPL138的DSP内核去读写一下外部的SPI FLASH芯片,先看下 ...
 - RDS for MySQL有哪些限制
			
原文来自:https://help.aliyun.com/knowledge_detail/41834.html 1.不支持在命令行创建数据库和数据库账号.只支持在RDS管理控制台操作. 2.不支持M ...
 - WPF DataGridTable
			
由于项目要显示表头合并,而数据源列随时变更,又不想重复的画表格,就实现动态数据(dynamic)绑定和配置数据列模板的方式 编辑DataGridColumnHeader样式实现表头合并:效果如下 实现 ...
 - Lambada表达式的作用
			
Lambda函数的用处 假设你设计了一个地址簿的类.现在你要提供函数查询这个地址簿,可能根据姓名查询,可能根据地址查询,还有可能两者结合.要是你为这些情况都写个函数,那么你一定就跪了.所以你应该提 ...
 - 开源工具 | 手游自动化框架GAutomator,新增iOS系统和UE4引擎支
			
WeTest 导读 GAutomator是腾讯WeTest推出的手游自动化测试框架,已用于腾讯多个手游项目组的自动化测试. 1.GAutomator诞生背后 研究过手游自动化测试的同学都知道,虽然市场 ...
 - cocos2dx2.0 帧动画的创建和播放过程 深入分析
			
一.帧动画的创建过程帧动画的实现有四个不可或缺的类,如下:1.CCSpriteFrame:精灵帧信息.存储帧动画的每一帧的纹理基本信息. class CC_DLL CCSpriteFrame : pu ...
 - python-模块详解
			
模块: 模块的分类: 第三方模块/扩展模块:没在安装python解释器的时候安装的那些功能 自定义模块:你写的功能如果是一个通用的功能,那你就把它当做一个模块 内置模块:安装python解释器的时候跟 ...
 - Qt 将字符串转成16进制显示
			
最近项目用到了需要将字符串转换成16进制显示.这玩意折腾了一上午. 首先,数据块内容 struct UserData { char Head[3] = {'X','J','J'}; char Flag ...
 - 大O算法
			
大O计法:根据执行次数计算#sum = (1+n)*n/2://执行了一次,即为O(1)#for(i=0;i<n;i++);//执行了n次,即为O(n)#算法的时间复杂度:T(n) = O(f( ...