Browse Source

Implemented Soft Delete filter.

pull/179/head
Halil İbrahim Kalkan 9 years ago
parent
commit
e2cfcacca6
  1. 28
      src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  2. 16
      src/Volo.Abp/Volo/Abp/ISoftDelete.cs
  3. 70
      test/Volo.Abp.EntityFrameworkCore.Tests/Migrations/20171026101049_Added_IsDeleted_To_Person.Designer.cs
  4. 26
      test/Volo.Abp.EntityFrameworkCore.Tests/Migrations/20171026101049_Added_IsDeleted_To_Person.cs
  5. 2
      test/Volo.Abp.EntityFrameworkCore.Tests/Migrations/TestAppDbContextModelSnapshot.cs
  6. 26
      test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/SoftDelete_Filter_Tests.cs
  7. 4
      test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs
  8. 2
      test/Volo.Abp.TestApp/Volo/Abp/TestApp/TestDataBuilder.cs

28
src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

@ -22,6 +22,7 @@ namespace Volo.Abp.EntityFrameworkCore
public Guid? CurrentTenantId => CurrentTenant?.Id;
protected virtual bool IsMayHaveTenantFilterEnabled => true; //TODO: Change this when data filtering system is full implemented
protected virtual bool IsSoftDeleteFilterEnabled => true; //TODO: Change this when data filtering system is full implemented
public ICurrentTenant CurrentTenant { get; set; }
@ -115,7 +116,10 @@ namespace Volo.Abp.EntityFrameworkCore
CheckAndSetId(entry);
break;
case EntityState.Modified:
HandleConcurrencyStamp(entry);
break;
case EntityState.Deleted:
CancelDeletionForSoftDelete(entry);
HandleConcurrencyStamp(entry);
break;
}
@ -133,6 +137,18 @@ namespace Volo.Abp.EntityFrameworkCore
entity.ConcurrencyStamp = Guid.NewGuid().ToString();
}
protected virtual void CancelDeletionForSoftDelete(EntityEntry entry)
{
if (!(entry.Entity is ISoftDelete))
{
return;
}
entry.Reload();
entry.State = EntityState.Modified;
entry.Entity.As<ISoftDelete>().IsDeleted = true;
}
protected virtual void CheckAndSetId(EntityEntry entry)
{
//Set GUID Ids
@ -179,6 +195,18 @@ namespace Volo.Abp.EntityFrameworkCore
{
Expression<Func<TEntity, bool>> expression = null;
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
/* This condition should normally be defined as below:
* !IsSoftDeleteFilterEnabled || !((ISoftDelete) e).IsDeleted
* But this causes a problem with EF Core (see https://github.com/aspnet/EntityFrameworkCore/issues/9502)
* So, we made a workaround to make it working. It works same as above.
*/
Expression<Func<TEntity, bool>> softDeleteFilter = e => !((ISoftDelete)e).IsDeleted || ((ISoftDelete)e).IsDeleted != IsSoftDeleteFilterEnabled;
expression = expression == null ? softDeleteFilter : CombineExpressions(expression, softDeleteFilter);
}
if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity)))
{
/* This condition should normally be defined as below:

16
src/Volo.Abp/Volo/Abp/ISoftDelete.cs

@ -0,0 +1,16 @@
namespace Volo.Abp
{
/// <summary>
/// Used to standardize soft deleting entities.
/// Soft-delete entities are not actually deleted,
/// marked as IsDeleted = true in the database,
/// but can not be retrieved to the application normally.
/// </summary>
public interface ISoftDelete
{
/// <summary>
/// Used to mark an Entity as 'Deleted'.
/// </summary>
bool IsDeleted { get; set; }
}
}

70
test/Volo.Abp.EntityFrameworkCore.Tests/Migrations/20171026101049_Added_IsDeleted_To_Person.Designer.cs

@ -0,0 +1,70 @@
// <auto-generated />
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.Internal;
using System;
using Volo.Abp.TestApp.Domain;
using Volo.Abp.TestApp.EntityFrameworkCore;
namespace Volo.Abp.EntityFrameworkCore.Tests.Migrations
{
[DbContext(typeof(TestAppDbContext))]
[Migration("20171026101049_Added_IsDeleted_To_Person")]
partial class Added_IsDeleted_To_Person
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
modelBuilder.Entity("Volo.Abp.TestApp.Domain.Person", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("Age");
b.Property<bool>("IsDeleted");
b.Property<string>("Name");
b.Property<Guid?>("TenantId");
b.HasKey("Id");
b.ToTable("People");
});
modelBuilder.Entity("Volo.Abp.TestApp.Domain.Phone", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Number");
b.Property<Guid>("PersonId");
b.Property<int>("Type");
b.HasKey("Id");
b.HasIndex("PersonId");
b.ToTable("AppPhones");
});
modelBuilder.Entity("Volo.Abp.TestApp.Domain.Phone", b =>
{
b.HasOne("Volo.Abp.TestApp.Domain.Person")
.WithMany("Phones")
.HasForeignKey("PersonId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

26
test/Volo.Abp.EntityFrameworkCore.Tests/Migrations/20171026101049_Added_IsDeleted_To_Person.cs

@ -0,0 +1,26 @@
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace Volo.Abp.EntityFrameworkCore.Tests.Migrations
{
public partial class Added_IsDeleted_To_Person : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<bool>(
name: "IsDeleted",
table: "People",
type: "INTEGER",
nullable: false,
defaultValue: false);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "IsDeleted",
table: "People");
}
}
}

2
test/Volo.Abp.EntityFrameworkCore.Tests/Migrations/TestAppDbContextModelSnapshot.cs

@ -27,6 +27,8 @@ namespace Volo.Abp.EntityFrameworkCore.Tests.Migrations
b.Property<int>("Age");
b.Property<bool>("IsDeleted");
b.Property<string>("Name");
b.Property<Guid?>("TenantId");

26
test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/SoftDelete_Filter_Tests.cs

@ -0,0 +1,26 @@
using System.Linq;
using Shouldly;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.TestApp.Domain;
using Xunit;
namespace Volo.Abp.EntityFrameworkCore.Repositories
{
public class SoftDelete_Filter_Tests : EntityFrameworkCoreTestBase
{
private readonly IRepository<Person> _personRepository;
public SoftDelete_Filter_Tests()
{
_personRepository = GetRequiredService<IRepository<Person>>();
}
[Fact]
public void Should_Not_Get_Deleted_Entities_By_Default()
{
var people = _personRepository.GetList();
people.Count.ShouldBe(1);
people.Any(p => p.Name == "Douglas").ShouldBeTrue();
}
}
}

4
test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs

@ -5,7 +5,7 @@ using Volo.Abp.MultiTenancy;
namespace Volo.Abp.TestApp.Domain
{
public class Person : AggregateRoot, IMultiTenant
public class Person : AggregateRoot, IMultiTenant, ISoftDelete
{
public virtual Guid? TenantId { get; set; }
@ -15,6 +15,8 @@ namespace Volo.Abp.TestApp.Domain
public virtual Collection<Phone> Phones { get; set; }
public bool IsDeleted { get; set; }
private Person()
{

2
test/Volo.Abp.TestApp/Volo/Abp/TestApp/TestDataBuilder.cs

@ -30,6 +30,8 @@ namespace Volo.Abp.TestApp
_personRepository.Insert(douglas);
_personRepository.Insert(new Person(Guid.NewGuid(), "John-Deleted", 33) { IsDeleted = true });
var tenant1Person1 = new Person(Guid.NewGuid(), TenantId1 + "-Person1", 42, TenantId1);
var tenant1Person2 = new Person(Guid.NewGuid(), TenantId1 + "-Person2", 43, TenantId1);

Loading…
Cancel
Save