本文翻译自《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 软删除与查询过滤器的更多相关文章

  1. Entity Framework Core 软删除与查询过滤器

    本文翻译自<Entity Framework Core: Soft Delete using Query Filters>,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 注意 ...

  2. Entity Framework Core 关联删除

    关联删除通常是一个数据库术语,用于描述在删除行时允许自动触发删除关联行的特征:即当主表的数据行被删除时,自动将关联表中依赖的数据行进行删除,或者将外键更新为NULL或默认值. 数据库关联删除行为 我们 ...

  3. EF Entity Framework Core DBContext中文文档

    Add(Object) 以添加状态开始跟踪给定的实体和任何其他尚未被跟踪的可访问实体,以便在调用SaveChanges()时将它们插入数据库.使用State设置单个实体的状态. Add<TEnt ...

  4. 张高兴的 Entity Framework Core 即学即用:(一)创建第一个 EF Core 应用

    写在前面 Entity Framework Core (EF Core) 是 .NET 平台流行的对象关系映射(ORM)框架.虽然 .NET 平台中 ORM 框架有很多,比如 Dapper.NHibe ...

  5. Entity Framework Core系列之DbContext(删除)

    上一篇我们介绍了Entity Framework Core系列之DbContext(修改),这一篇我们介绍下删除数据 修改实体的方法取决于context是否正在跟踪需要删除的实体. 下面的示例中con ...

  6. 【EF Core】Entity Framework Core 批处理语句

    在Entity Framework Core (EF Core)有许多新的功能,最令人期待的功能之一就是批处理语句.那么批处理语句是什么呢?批处理语句意味着它不会为每个插入/更新/删除语句发送单独的请 ...

  7. 【EF】Entity Framework Core 2.0 特性介绍和使用指南

    阅读目录 前言 获取和使用 新特性 项目升级和核心API变化 下一步计划 遗憾的地方 回到目录 前言 这是.Net Core 2.0生态生态介绍的最后一篇,EF一直是我喜欢的一个ORM框架,随着版本升 ...

  8. Entity Framework Core Like 查询揭秘

    在Entity Framework Core 2.0中增加一个很酷的功能:EF.Functions.Like(),最终解析为SQL中的Like语句,以便于在 LINQ 查询中直接调用. 不过Entit ...

  9. 使用Entity Framework Core需要注意的一个全表查询问题

    .NET Core 迁移工作如火如荼,今天在使用 Entity Frameowork Core(又名EF Core)时写了下面这样的 LINQ 查询表达式: .Where(u => u.Id = ...

随机推荐

  1. C语言第三周

    一. 字符串常量 只要有一对双引号括起来的字符序列就是字符串常量.列如"hello"接"123" 注意:"a"是字符串常量'a'是字符常量. ...

  2. WPF 带水印的密码输入框

    原文:WPF 带水印的密码输入框 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/BYH371256/article/details/83652540 ...

  3. tkinter菜单图标,工具栏

    所用的图片: import tkinter as tk from tkinter import messagebox, filedialog, simpledialog, colorchooser f ...

  4. svn 安装 配置及同步

    1.环境centos 2.安装svn #yum -y install subversion 3 查看版本 #svnserve --version 4 建立版本库目录 #mkdir /data/svnd ...

  5. shell 判断日期间隔及润年

    #!/bin/bash test.sh until echo "----------------------------------" echo "请输入您的选择:&qu ...

  6. centos 6.5 双网卡 上网 virtualbox nat hostonly

    虚拟机两张网卡:分别调成NAT(eth0)和host only(eht1)模式. nat的网卡不用设置,host only网卡调为(vi /etc/sysconfig/network-scripts/ ...

  7. JAVA学习笔记--组合与继承

    JAVA一个很重要的功能就是代码的可复用性,代码复用可以大大提升编程效率.这里主要介绍两种代码复用方式:组合和继承. 一.组合 组合比较直观,只需在新的类中产生现有类的对象,新的类由现有类的对象组成, ...

  8. 面向对象编程(OOP)思想小结

    Concepts 类(class):对我们要解决问题的抽象,比如建造房子的蓝图:但实现机制上来讲,类是根据蓝图构建而成的,存储在内存中的,用来表示对象的数据. 对象(object):根据类构建的实体, ...

  9. 针对某一网站的UI进行分析

    本周课上教学通过对PM(项目经理)的学习,我了解到PM 对项目所有功能的把握, 特别是有关的UI内容.最差的UI, 体现了团队的组织架构:其次, 体现了产品的内部结构:最好, 体现了用户的自然需求. ...

  10. CS小分队第一阶段冲刺站立会议(5月14日)

    昨日成果:为抽号计时器添加了第一类抽号,基本实现界面,功能出现了一些错误 遇到问题:我预想通过timer控件来实现随机抽号而拜托随机生成数,但是出现了只有个位随机滚动,其他位数不动的现象,我预计是数值 ...