如何使用“Entity Framework Core”(又名EF7)實現“Soft Deletes”?

c# entity-framework-core

我正在嘗試使用EF7實現“軟刪除”。我的Item表有一個名為IsDeleted的字段,類型為bit 。我在SO和其他地方看到的所有例子都使用這樣的東西:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Item>().Map(m => m.Requires("IsDeleted").HasValue(false));
}

Map()不再是ModelBuilder的方法。

編輯:讓我澄清一下。我現在大多只對閱讀數據感興趣。我希望EF自動過濾掉我的Item表中IsDeleted == 1 (或true)的所有記錄。我不想在每個查詢結束時要求&& x.IsDeleted == false

專家解答

免責聲明 :我是項目Entity Framework Plus的所有者

正如您將在@Adem鏈接中看到的,我們的庫支持查詢過濾。

您可以輕鬆啟用/禁用全局/實例過濾器

QueryFilterManager.Filter<Item>(q => q.Where(x => !x.IsDeleted));

Wiki: EF查詢過濾器

編輯 :回答子問題

關心如何在幕後工作?

首先,您可以全局或按實例初始化過濾器

// Filter by global configuration
QueryFilterManager.Filter<Customer>(q => q.Where(x => x.IsActive));
var ctx = new EntitiesContext();
// TIP: You can also add this line in EntitiesContext constructor instead
QueryFilterManager.InitilizeGlobalFilter(ctx);

// Filter by instance configuration
var ctx = new EntitiesContext();
ctx.Filter<Post>(MyEnum.EnumValue, q => q.Where(x => !x.IsSoftDeleted)).Disable();

在引擎蓋下,庫將循環上下文的每個DbSet,並檢查是否可以將過濾器應用於泛型類型。

在這種情況下,庫將使用過濾器從DbSet過濾原始/過濾的查詢,然後修改新過濾查詢的當前內部查詢。

總之,我們更改了一些DbSet內部值以使用過濾後的查詢。

如果您想了解它是如何工作的,代碼是免費開源的

編輯 :回答子問題

@jonathan這個過濾器也會包含導航集合嗎?

對於EF Core,由於Interceptor尚未推出,因此尚未得到支持。但是從EF Core 2.x開始,EF團隊已經實現了全局查詢過濾器,應該允許這樣做。


熱門答案

如果您可以遷移到EF Core 2.0,則可以使用模型級查詢過濾器https://docs.microsoft.com/en-us/ef/core/what-is-new/index

如果您使用EF Core 1.0您可以使用可用的EF Core功能製作一些技巧:

繼承https://docs.microsoft.com/en-us/aspnet/core/data/ef-mvc/inheritance

影子屬性https://docs.microsoft.com/en-us/ef/core/modeling/shadow-properties

public class Attachment : AttachmentBase
{}

public abstract class AttachmentBase
{
    public const string StatePropertyName = "state";

    public Guid Id { get; set; }
}

public enum AttachmentState
{
    Available,
    Deleted
}

public class AttachmentsDbContext : DbContext
{
    public AttachmentsDbContext(DbContextOptions options)
        : base(options)
    {
    }

    public DbSet<Attachment> Attachments { get; set; }

    public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        IEnumerable<EntityEntry<Attachment>> softDeletedAttachments = ChangeTracker.Entries<Attachment>().Where(entry => entry.State == EntityState.Deleted);

        foreach (EntityEntry<Attachment> softDeletedAttachment in softDeletedAttachments)
        {
            softDeletedAttachment.State = EntityState.Modified;
            softDeletedAttachment.Property<int>(AttachmentBase.StatePropertyName).CurrentValue = (int)AttachmentState.Deleted;
        }
        return base.SaveChangesAsync(cancellationToken);
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<AttachmentBase>()
            .HasDiscriminator<int>(AttachmentBase.StatePropertyName)
            .HasValue<Attachment>((int)AttachmentState.Available);

        modelBuilder.Entity<AttachmentBase>().Property<int>(AttachmentBase.StatePropertyName).Metadata.IsReadOnlyAfterSave = false;

        modelBuilder.Entity<Attachment>()
            .ToTable("available_attachment");

        modelBuilder.Entity<AttachmentBase>()
            .ToTable("attachment");

        base.OnModelCreating(modelBuilder);
    }
}


Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow