本文翻译自《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. python 多线程笔记(1)-- 概念

    本文对不使用线程和使用线程做了一个对比. 假设有两件事情:听歌.看电影 一.不用线程 import time songs = ['爱情买卖','朋友','回家过年','好日子'] movies = [ ...

  2. MySQLdb in Python: “Can't connect to MySQL server on 'localhost'”

    因为我使用的是win64,所以在此系统下,需要设置为 127.0.0.1 #coding=utf-8 import MySQLdb if __name__ == '__main__': # 打开数据库 ...

  3. Linux目录与文件操作

    文件命名规则: 1.严格区分大小写: 2.长度不能超过255个字符: 3.不能使用/当文件名 mkdir:创建空目录 -p:parent,父目录,逐级创建 -v:verbose,打印详细信息 命令行展 ...

  4. linux-ubuntu常用命令(深圳文鹏)

    系统信息 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件 - (SMBIOS ...

  5. python的30个编程技巧

     1.原地交换两个数字 x, y =10, 20 print(x, y) y, x = x, y print(x, y) 10 20 20 10 2.链状比较操作符 n = 10 print(1 &l ...

  6. 算法笔记(c++)--完全背包问题

    算法笔记(c++)--完全背包和多重背包问题 完全背包 完全背包不同于01背包-完全背包里面的东西数量无限 假设现在有5种物品重量为5,4,3,2,1  价值为1,2,3,4,5  背包容量为10 # ...

  7. [咸恩静][Coffee House]

    歌词来源:http://music.163.com/#/song?id=5400159 하루의 시작은 향긋한 커피 [ha-lu-e xi-ja-geun hyang-geu-Tan Keo-Pi] ...

  8. ES6的新特性(5)——数值的扩展

    数值的扩展 二进制和八进制表示法 ES6 提供了二进制和八进制数值的新的写法,分别用前缀0b(或0B)和0o(或0O)表示. 0b111110111 === 503 // true 0o767 === ...

  9. 原生js和jquey获取窗口宽高,滚动条,鼠标位置总结

    JQuery获取浏览器窗口的可视区域高度和宽度,滚动条高度   alert($(window).height()); //浏览器时下窗口可视区域高度 alert($(document).height( ...

  10. ssd a

    Alpha版本测试报告 (1)测试计划 测试人员 工作安排 瞿煌人 制定测试计划,撰写测试报告 周建峰 执行测试,撰写测试报告 注:测试结果Y表示通过测试,N表示未通过测试. 功能 描述 效果 测试结 ...