首頁 > 軟體

Entity Framework Core實現軟刪除與查詢過濾器

2022-02-21 16:00:47

注意:我使用的是 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程式碼的情況下,將軟刪除新增到現有模型中。

到此這篇關於Entity Framework Core實現軟刪除與查詢過濾器的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。


IT145.com E-mail:sddin#qq.com