From b136f483fc49910b7a806a61418bef579d9152c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 28 Mar 2018 10:24:55 +0300 Subject: [PATCH] Implemented data filters for mongodb. Revised IGeneratesDomainEvents --- .../Volo/Abp/Domain/Entities/AggregateRoot.cs | 39 +++++++++++---- .../Volo/Abp/Domain/Entities/EntityHelper.cs | 2 +- .../Domain/Entities/IGeneratesDomainEvents.cs | 6 ++- .../Abp/Domain/Repositories/RepositoryBase.cs | 7 +-- .../Abp/EntityFrameworkCore/AbpDbContext.cs | 7 +-- .../Repositories/MongoDB/MongoDbRepository.cs | 48 ++++++++++++++----- .../Abp/MongoDB/MultiTenant_Filter_Tests.cs | 9 ++++ ...sic_Tests.cs => Repository_Basic_Tests.cs} | 2 +- .../Abp/MongoDB/Repository_Queryable_Tests.cs | 9 ++++ .../Abp/MongoDB/SoftDelete_Filter_Tests.cs | 9 ++++ .../Volo/Abp/TestApp/Domain/Person.cs | 3 +- .../Testing/SoftDelete_Filter_Tests.cs | 23 ++++++++- 12 files changed, 134 insertions(+), 30 deletions(-) create mode 100644 test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MultiTenant_Filter_Tests.cs rename test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/{MongoDb_Repository_Basic_Tests.cs => Repository_Basic_Tests.cs} (95%) create mode 100644 test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repository_Queryable_Tests.cs create mode 100644 test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/SoftDelete_Filter_Tests.cs diff --git a/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/AggregateRoot.cs b/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/AggregateRoot.cs index ce7a31ec61..3a58098a2a 100644 --- a/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/AggregateRoot.cs +++ b/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/AggregateRoot.cs @@ -1,26 +1,34 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel.DataAnnotations.Schema; namespace Volo.Abp.Domain.Entities { [Serializable] public abstract class AggregateRoot : Entity, IAggregateRoot { - [NotMapped] //TODO: Better to handle in EF Core layer, or just use get/set methods instead of a property? - public virtual ICollection DomainEvents => _domainEvents ?? (_domainEvents = new Collection()); + private readonly ICollection _domainEvents = new Collection(); - private ICollection _domainEvents; + protected virtual void AddDomainEvent(object eventData) + { + _domainEvents.Add(eventData); + } + + public virtual IEnumerable GetDomainEvents() + { + return _domainEvents; + } + + public virtual void ClearDomainEvents() + { + _domainEvents.Clear(); + } } [Serializable] public abstract class AggregateRoot : Entity, IAggregateRoot { - [NotMapped] //TODO: Better to handle in EF Core layer, or just use get/set methods instead of a property?? - public virtual ICollection DomainEvents => _domainEvents ?? (_domainEvents = new Collection()); - - private ICollection _domainEvents; + private readonly ICollection _domainEvents = new Collection(); protected AggregateRoot() { @@ -32,5 +40,20 @@ namespace Volo.Abp.Domain.Entities { } + + protected virtual void AddDomainEvent(object eventData) + { + _domainEvents.Add(eventData); + } + + public virtual IEnumerable GetDomainEvents() + { + return _domainEvents; + } + + public virtual void ClearDomainEvents() + { + _domainEvents.Clear(); + } } } \ No newline at end of file diff --git a/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs b/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs index 7f6febe354..69a4d9ddaa 100644 --- a/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs +++ b/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/EntityHelper.cs @@ -76,7 +76,7 @@ namespace Volo.Abp.Domain.Entities { var lambdaParam = Expression.Parameter(typeof(TEntity)); var lambdaBody = Expression.Equal( - Expression.PropertyOrField(lambdaParam, "Id"), + Expression.PropertyOrField(lambdaParam, nameof(Entity.Id)), Expression.Constant(id, typeof(TKey)) ); diff --git a/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/IGeneratesDomainEvents.cs b/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/IGeneratesDomainEvents.cs index 4fdfe54ee7..d266206b90 100644 --- a/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/IGeneratesDomainEvents.cs +++ b/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Entities/IGeneratesDomainEvents.cs @@ -2,8 +2,12 @@ namespace Volo.Abp.Domain.Entities { + //TODO: Re-consider this interface + public interface IGeneratesDomainEvents { - ICollection DomainEvents { get; } + IEnumerable GetDomainEvents(); + + void ClearDomainEvents(); } } diff --git a/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs b/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs index 7495f2df88..8c8bad3219 100644 --- a/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs +++ b/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryBase.cs @@ -50,17 +50,18 @@ namespace Volo.Abp.Domain.Repositories return Task.CompletedTask; } - protected virtual IQueryable ApplyDataFilters(IQueryable query) + protected virtual TQueryable ApplyDataFilters(TQueryable query) + where TQueryable : IQueryable { if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity))) { - query = query.WhereIf(DataFilter.IsEnabled(), e => ((ISoftDelete)e).IsDeleted == false); + query = (TQueryable)query.WhereIf(DataFilter.IsEnabled(), e => ((ISoftDelete)e).IsDeleted == false); } if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity))) { var tenantId = CurrentTenant.Id; - query = query.WhereIf(DataFilter.IsEnabled(), e => ((IMultiTenant)e).TenantId == tenantId); + query = (TQueryable)query.WhereIf(DataFilter.IsEnabled(), e => ((IMultiTenant)e).TenantId == tenantId); } return query; diff --git a/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs b/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs index 920258f8c8..aca9fb8fd7 100644 --- a/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs +++ b/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs @@ -189,13 +189,14 @@ namespace Volo.Abp.EntityFrameworkCore return; } - if (generatesDomainEventsEntity.DomainEvents.IsNullOrEmpty()) + var entityEvents = generatesDomainEventsEntity.GetDomainEvents().ToArray(); + if (entityEvents.IsNullOrEmpty()) { return; } - domainEvents.AddRange(generatesDomainEventsEntity.DomainEvents.Select(eventData => new DomainEventEntry(entityAsObj, eventData))); - generatesDomainEventsEntity.DomainEvents.Clear(); + domainEvents.AddRange(entityEvents.Select(eventData => new DomainEventEntry(entityAsObj, eventData))); + generatesDomainEventsEntity.ClearDomainEvents(); } protected virtual void HandleConcurrencyStamp(EntityEntry entry) diff --git a/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs b/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs index bc05f1791a..db4ae76df7 100644 --- a/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs +++ b/src/Volo.Abp.MongoDB/Volo/Abp/Domain/Repositories/MongoDB/MongoDbRepository.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Threading; @@ -6,6 +7,7 @@ using System.Threading.Tasks; using MongoDB.Driver; using Volo.Abp.Domain.Entities; using Volo.Abp.MongoDB; +using Volo.Abp.MultiTenancy; namespace Volo.Abp.Domain.Repositories.MongoDB { @@ -74,7 +76,7 @@ namespace Volo.Abp.Domain.Repositories.MongoDB protected override IQueryable GetQueryable() { - return Collection.AsQueryable(); + return ApplyDataFilters(Collection.AsQueryable()); } protected virtual FilterDefinition CreateEntityFilter(TEntity entity) @@ -117,24 +119,24 @@ namespace Volo.Abp.Domain.Repositories.MongoDB return entity; } - public virtual void Delete(TKey id, bool autoSave = false) + public virtual async Task FindAsync(TKey id, CancellationToken cancellationToken = default) { - Collection.DeleteOne(CreateEntityFilter(id)); + return await Collection.Find(CreateEntityFilter(id, true)).FirstOrDefaultAsync(cancellationToken); } - public virtual Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default) + public virtual TEntity Find(TKey id) { - return Collection.DeleteOneAsync(CreateEntityFilter(id), cancellationToken); + return Collection.Find(CreateEntityFilter(id, true)).FirstOrDefault(); } - public virtual async Task FindAsync(TKey id, CancellationToken cancellationToken = default) + public virtual void Delete(TKey id, bool autoSave = false) { - return await Collection.Find(CreateEntityFilter(id)).FirstOrDefaultAsync(cancellationToken); + Collection.DeleteOne(CreateEntityFilter(id)); } - public virtual TEntity Find(TKey id) + public virtual Task DeleteAsync(TKey id, bool autoSave = false, CancellationToken cancellationToken = default) { - return Collection.Find(CreateEntityFilter(id)).FirstOrDefault(); + return Collection.DeleteOneAsync(CreateEntityFilter(id), cancellationToken); } protected override FilterDefinition CreateEntityFilter(TEntity entity) @@ -142,9 +144,33 @@ namespace Volo.Abp.Domain.Repositories.MongoDB return Builders.Filter.Eq(e => e.Id, entity.Id); } - protected virtual FilterDefinition CreateEntityFilter(TKey id) + protected virtual FilterDefinition CreateEntityFilter(TKey id, bool applyFilters = false) { - return Builders.Filter.Eq(e => e.Id, id); + var filters = new List> + { + Builders.Filter.Eq(e => e.Id, id) + }; + + if (applyFilters) + { + AddGlobalFilters(filters); + } + + return Builders.Filter.And(filters); + } + + protected virtual void AddGlobalFilters(List> filters) + { + if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)) && DataFilter.IsEnabled()) + { + filters.Add(Builders.Filter.Eq(e => ((ISoftDelete) e).IsDeleted, false)); + } + + if (typeof(IMultiTenant).IsAssignableFrom(typeof(TEntity))) + { + var tenantId = CurrentTenant.Id; + filters.Add(Builders.Filter.Eq(e => ((IMultiTenant) e).TenantId, tenantId)); + } } } } \ No newline at end of file diff --git a/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MultiTenant_Filter_Tests.cs b/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MultiTenant_Filter_Tests.cs new file mode 100644 index 0000000000..d4cee46471 --- /dev/null +++ b/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MultiTenant_Filter_Tests.cs @@ -0,0 +1,9 @@ +using Volo.Abp.TestApp.Testing; + +namespace Volo.Abp.MongoDB +{ + public class MultiTenant_Filter_Tests : MultiTenant_Filter_Tests + { + + } +} \ No newline at end of file diff --git a/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDb_Repository_Basic_Tests.cs b/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repository_Basic_Tests.cs similarity index 95% rename from test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDb_Repository_Basic_Tests.cs rename to test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repository_Basic_Tests.cs index 0823a61632..335916e308 100644 --- a/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/MongoDb_Repository_Basic_Tests.cs +++ b/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repository_Basic_Tests.cs @@ -9,7 +9,7 @@ using Xunit; namespace Volo.Abp.MongoDB { - public class MongoDb_Repository_Basic_Tests : Repository_Basic_Tests + public class Repository_Basic_Tests : Repository_Basic_Tests { [Fact] public async Task GetAsync() diff --git a/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repository_Queryable_Tests.cs b/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repository_Queryable_Tests.cs new file mode 100644 index 0000000000..ee49c19526 --- /dev/null +++ b/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repository_Queryable_Tests.cs @@ -0,0 +1,9 @@ +using Volo.Abp.TestApp.Testing; + +namespace Volo.Abp.MongoDB +{ + public class Repository_Queryable_Tests : Repository_Queryable_Tests + { + + } +} \ No newline at end of file diff --git a/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/SoftDelete_Filter_Tests.cs b/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/SoftDelete_Filter_Tests.cs new file mode 100644 index 0000000000..3bc3dfe979 --- /dev/null +++ b/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/SoftDelete_Filter_Tests.cs @@ -0,0 +1,9 @@ +using Volo.Abp.TestApp.Testing; + +namespace Volo.Abp.MongoDB +{ + public class SoftDelete_Filter_Tests : SoftDelete_Filter_Tests + { + + } +} \ No newline at end of file diff --git a/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs b/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs index d9c28f766a..88f3088572 100644 --- a/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs +++ b/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Domain/Person.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using Volo.Abp.Domain.Entities; using Volo.Abp.MultiTenancy; @@ -41,7 +42,7 @@ namespace Volo.Abp.TestApp.Domain var oldName = Name; Name = name; - DomainEvents.Add(new PersonNameChangedEvent{Person = this, OldName = oldName}); + AddDomainEvent(new PersonNameChangedEvent{Person = this, OldName = oldName}); } } } \ No newline at end of file diff --git a/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/SoftDelete_Filter_Tests.cs b/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/SoftDelete_Filter_Tests.cs index 45ebaa0ac5..891691114e 100644 --- a/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/SoftDelete_Filter_Tests.cs +++ b/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/SoftDelete_Filter_Tests.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Threading.Tasks; using Shouldly; using Volo.Abp.Data; using Volo.Abp.Domain.Repositories; @@ -22,7 +23,27 @@ namespace Volo.Abp.TestApp.Testing } [Fact] - public void Should_Not_Get_Deleted_Entities_By_Default() + public void Should_Not_Get_Deleted_Entities_Linq() + { + WithUnitOfWork(() => + { + var person = PersonRepository.FirstOrDefault(p => p.Name == "John-Deleted"); + person.ShouldBeNull(); + }); + } + + [Fact] + public async Task Should_Not_Get_Deleted_Entities_By_Id() + { + await WithUnitOfWorkAsync(async () => + { + var person = await PersonRepository.FindAsync(TestDataBuilder.UserJohnDeletedId); + person.ShouldBeNull(); + }); + } + + [Fact] + public void Should_Not_Get_Deleted_Entities_By_Default_ToList() { WithUnitOfWork(() => {