How can I implement “Soft Deletes” with “Entity Framework Core” (aka EF7)?

情到浓时终转凉″ 提交于 2019-12-24 18:29:03

问题


I'm trying to implement a "Soft Delete" using EF7. My Item table has a field named IsDeleted of type bit. All of the examples that I see around SO and elsewhere are using something like this:

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

but Map() is no longer a method of ModelBuilder.

EDIT: Let me clarify. I'm mostly only interested in reading the data right now. I want EF to automatically filter out all records in my Item table where IsDeleted == 1 (or true). I do not want to require an && x.IsDeleted == false at the end of every query.


回答1:


Disclaimer: I'm the owner of the project Entity Framework Plus

As you will see in @Adem link, our library supports query filtering.

You can easily enable/disable a global/instance filter

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

Wiki: EF Query Filter

Edit: Answer sub question

Care to explain how this works behind the scene?

Firstly, you can either initialize filter globally or by instance

// 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();

Under the hood, the library will loop on every DbSet of the context and checks if a filter can be applied to the generic type.

In this case, the library will filter the original/filtered query from the DbSet using the filter then modify the current internal query for the new filtered query.

In summary, we changed some DbSet internal value to use the filtered query.

The code is FREE & Open Source if you want to learn about how it works.

Edit: Answer sub question

@jonathan will this filter included navigation collections too?

For EF Core, it's not supported yet since Interceptor is not available yet. But starting from EF Core 2.x, the EF Team has implemented Global query filters which should allow this.




回答2:


If you can migrate to EF Core 2.0 you can use Model-level query filters https://docs.microsoft.com/en-us/ef/core/what-is-new/index

If you use EF Core 1.0 You can make some trick with available EF Core features:

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

Shadow properties 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);
    }
}


来源:https://stackoverflow.com/questions/37932339/how-can-i-implement-soft-deletes-with-entity-framework-core-aka-ef7

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!