From 9e333ef94a96d48ce47708e4467022b53992ef79 Mon Sep 17 00:00:00 2001 From: Erfan Shokrollahi Date: Sat, 18 Dec 2021 02:06:54 +0330 Subject: [PATCH 1/6] Added EnsureExistsAsync for optimal checking --- .../Repositories/RepositoryExtensions.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs index 2b4dd70e62..83b61b43e3 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs @@ -47,6 +47,33 @@ public static class RepositoryExtensions } } + + public static async Task EnsureExistsAsync( + this IRepository repository, + TKey id, + CancellationToken cancellationToken = default + ) + where TEntity : class, IEntity + { + if (await repository.AnyAsync(x => x.Id.Equals(id), cancellationToken)) + { + throw new EntityNotFoundException(typeof(TEntity), id); + } + } + + public static async Task EnsureExistsAsync( + this IRepository repository, + Expression> expression, + CancellationToken cancellationToken = default + ) + where TEntity : class, IEntity + { + if (await repository.AnyAsync(expression, cancellationToken)) + { + throw new EntityNotFoundException(typeof(TEntity)); + } + } + public static async Task HardDeleteAsync( this IRepository repository, Expression> predicate, From 45c6a6ea189254c85e9c3c1f1a8020c22aea74f7 Mon Sep 17 00:00:00 2001 From: Erfan Shokrollahi Date: Mon, 20 Dec 2021 15:07:19 +0330 Subject: [PATCH 2/6] Update framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs Co-authored-by: Qingxiao Ren --- .../Volo/Abp/Domain/Repositories/RepositoryExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs index 83b61b43e3..a3ba6e3ac0 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs @@ -55,7 +55,7 @@ public static class RepositoryExtensions ) where TEntity : class, IEntity { - if (await repository.AnyAsync(x => x.Id.Equals(id), cancellationToken)) + if (!await repository.AnyAsync(x => x.Id.Equals(id), cancellationToken)) { throw new EntityNotFoundException(typeof(TEntity), id); } From 7d5016e84a257197f6073690e144c899dcb28408 Mon Sep 17 00:00:00 2001 From: Erfan Shokrollahi Date: Mon, 20 Dec 2021 15:12:47 +0330 Subject: [PATCH 3/6] Fixed missing ! in EnsureExistsAsync method --- .../Volo/Abp/Domain/Repositories/RepositoryExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs index a3ba6e3ac0..35c31af06e 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs @@ -68,7 +68,7 @@ public static class RepositoryExtensions ) where TEntity : class, IEntity { - if (await repository.AnyAsync(expression, cancellationToken)) + if (!await repository.AnyAsync(expression, cancellationToken)) { throw new EntityNotFoundException(typeof(TEntity)); } From 107b848e86672a1c03e802e4eb38faf9d9af5c9b Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 21 Dec 2021 10:30:17 +0800 Subject: [PATCH 4/6] Update RepositoryExtensions.cs --- .../Abp/Domain/Repositories/RepositoryExtensions.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs index 35c31af06e..21162ed9e7 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs @@ -47,13 +47,12 @@ public static class RepositoryExtensions } } - public static async Task EnsureExistsAsync( - this IRepository repository, - TKey id, - CancellationToken cancellationToken = default - ) - where TEntity : class, IEntity + this IRepository repository, + TKey id, + CancellationToken cancellationToken = default + ) + where TEntity : class, IEntity { if (!await repository.AnyAsync(x => x.Id.Equals(id), cancellationToken)) { From 9355e0673df9fc5f0e456d08435889d65d9891e9 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 10 May 2022 11:19:09 +0800 Subject: [PATCH 5/6] Add document and unit tests. --- docs/en/Entity-Framework-Core.md | 12 ++++++ .../Repositories/RepositoryExtensions.cs | 20 +++++----- .../RepositoryExtensions_Tests.cs | 8 ++++ .../RepositoryExtensions_Tests.cs | 8 ++++ .../RepositoryExtensions_Tests.cs | 10 +++++ .../Testing/RepositoryExtensions_Tests.cs | 38 +++++++++++++++++++ 6 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/RepositoryExtensions_Tests.cs create mode 100644 framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/Repositories/RepositoryExtensions_Tests.cs create mode 100644 framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/RepositoryExtensions_Tests.cs create mode 100644 framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/RepositoryExtensions_Tests.cs diff --git a/docs/en/Entity-Framework-Core.md b/docs/en/Entity-Framework-Core.md index 568623a15d..de26b7c3f0 100644 --- a/docs/en/Entity-Framework-Core.md +++ b/docs/en/Entity-Framework-Core.md @@ -607,6 +607,18 @@ Whenever you access to a property/collection, EF Core automatically performs an See also [lazy loading document](https://docs.microsoft.com/en-us/ef/core/querying/related-data/lazy) of the EF Core. +## Repository Extensions + +There are some useful repository extension methods. + +````csharp +EnsureCollectionLoadedAsync // **Explicitly load** a sub collection. +EnsurePropertyLoadedAsync // **Explicitly load** a navigation property. +EnsureExistsAsync // Make sure the entitys exists, It accepts the entity id or a query expression. +HardDeleteAsync // Hard delete the entitys, It accepts one or more entities or query expressions. +HardDeleteWithUnitOfWorkAsync // Hard delete the entitys in a uow(create a new uow if current is null), It accepts one or more entities or query expressions. +```` + ## Access to the EF Core API In most cases, you want to hide EF Core APIs behind a repository (this is the main purpose of the repository pattern). However, if you want to access the `DbContext` instance over the repository, you can use `GetDbContext()` or `GetDbSet()` extension methods. Example: diff --git a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs index 21162ed9e7..379203f46a 100644 --- a/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs +++ b/framework/src/Volo.Abp.Ddd.Domain/Volo/Abp/Domain/Repositories/RepositoryExtensions.cs @@ -15,7 +15,7 @@ namespace Volo.Abp.Domain.Repositories; public static class RepositoryExtensions { - public static async Task EnsureCollectionLoadedAsync( + public async static Task EnsureCollectionLoadedAsync( this IBasicRepository repository, TEntity entity, Expression>> propertyExpression, @@ -31,7 +31,7 @@ public static class RepositoryExtensions } } - public static async Task EnsurePropertyLoadedAsync( + public async static Task EnsurePropertyLoadedAsync( this IBasicRepository repository, TEntity entity, Expression> propertyExpression, @@ -47,7 +47,7 @@ public static class RepositoryExtensions } } - public static async Task EnsureExistsAsync( + public async static Task EnsureExistsAsync( this IRepository repository, TKey id, CancellationToken cancellationToken = default @@ -60,7 +60,7 @@ public static class RepositoryExtensions } } - public static async Task EnsureExistsAsync( + public async static Task EnsureExistsAsync( this IRepository repository, Expression> expression, CancellationToken cancellationToken = default @@ -73,7 +73,7 @@ public static class RepositoryExtensions } } - public static async Task HardDeleteAsync( + public async static Task HardDeleteAsync( this IRepository repository, Expression> predicate, bool autoSave = false, @@ -97,7 +97,7 @@ public static class RepositoryExtensions } } - public static async Task HardDeleteAsync( + public async static Task HardDeleteAsync( this IBasicRepository repository, IEnumerable entities, bool autoSave = false, @@ -121,7 +121,7 @@ public static class RepositoryExtensions } } - public static async Task HardDeleteAsync( + public async static Task HardDeleteAsync( this IBasicRepository repository, TEntity entity, bool autoSave = false, @@ -165,7 +165,7 @@ public static class RepositoryExtensions return unitOfWorkManagerAccessor.UnitOfWorkManager; } - private static async Task HardDeleteWithUnitOfWorkAsync( + private async static Task HardDeleteWithUnitOfWorkAsync( IRepository repository, Expression> predicate, bool autoSave, @@ -181,7 +181,7 @@ public static class RepositoryExtensions } } - private static async Task HardDeleteWithUnitOfWorkAsync( + private async static Task HardDeleteWithUnitOfWorkAsync( IBasicRepository repository, IEnumerable entities, bool autoSave, @@ -199,7 +199,7 @@ public static class RepositoryExtensions await repository.DeleteManyAsync(entities, autoSave, cancellationToken); } - private static async Task HardDeleteWithUnitOfWorkAsync( + private async static Task HardDeleteWithUnitOfWorkAsync( IBasicRepository repository, TEntity entity, bool autoSave, diff --git a/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/RepositoryExtensions_Tests.cs b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/RepositoryExtensions_Tests.cs new file mode 100644 index 0000000000..70f99ac26a --- /dev/null +++ b/framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/Repositories/RepositoryExtensions_Tests.cs @@ -0,0 +1,8 @@ +using Volo.Abp.TestApp.Testing; + +namespace Volo.Abp.EntityFrameworkCore.Repositories; + +public class RepositoryExtensions_Tests : RepositoryExtensions_Tests +{ + +} diff --git a/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/Repositories/RepositoryExtensions_Tests.cs b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/Repositories/RepositoryExtensions_Tests.cs new file mode 100644 index 0000000000..dbb37bb71e --- /dev/null +++ b/framework/test/Volo.Abp.MemoryDb.Tests/Volo/Abp/MemoryDb/Repositories/RepositoryExtensions_Tests.cs @@ -0,0 +1,8 @@ +using Volo.Abp.TestApp.Testing; + +namespace Volo.Abp.MemoryDb.Repositories; + +public class RepositoryExtensions_Tests : RepositoryExtensions_Tests +{ + +} diff --git a/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/RepositoryExtensions_Tests.cs b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/RepositoryExtensions_Tests.cs new file mode 100644 index 0000000000..dd653878b8 --- /dev/null +++ b/framework/test/Volo.Abp.MongoDB.Tests/Volo/Abp/MongoDB/Repositories/RepositoryExtensions_Tests.cs @@ -0,0 +1,10 @@ +using Volo.Abp.TestApp.Testing; +using Xunit; + +namespace Volo.Abp.MongoDB.Repositories; + +[Collection(MongoTestCollection.Name)] +public class RepositoryExtensions_Tests : RepositoryExtensions_Tests +{ + +} diff --git a/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/RepositoryExtensions_Tests.cs b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/RepositoryExtensions_Tests.cs new file mode 100644 index 0000000000..747c53e333 --- /dev/null +++ b/framework/test/Volo.Abp.TestApp/Volo/Abp/TestApp/Testing/RepositoryExtensions_Tests.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Volo.Abp.Domain.Entities; +using Volo.Abp.Domain.Repositories; +using Volo.Abp.Modularity; +using Volo.Abp.TestApp.Domain; +using Xunit; + +namespace Volo.Abp.TestApp.Testing; + +public abstract class RepositoryExtensions_Tests : TestAppTestBase + where TStartupModule : IAbpModule +{ + protected readonly IRepository PersonRepository; + + protected RepositoryExtensions_Tests() + { + PersonRepository = GetRequiredService>(); + } + + [Fact] + public async Task EnsureExistsAsync_Test() + { + await WithUnitOfWorkAsync(async () => + { + var id = Guid.NewGuid(); + await Assert.ThrowsAsync(async () => + await PersonRepository.EnsureExistsAsync(Guid.NewGuid()) + ); + await Assert.ThrowsAsync(async () => + await PersonRepository.EnsureExistsAsync(x => x.Id == id) + ); + + await PersonRepository.EnsureExistsAsync(TestDataBuilder.UserDouglasId); + await PersonRepository.EnsureExistsAsync(x => x.Id == TestDataBuilder.UserDouglasId); + }); + } +} From 9e48c27db671a15378db8d9fd469f76234046830 Mon Sep 17 00:00:00 2001 From: maliming Date: Tue, 10 May 2022 11:37:22 +0800 Subject: [PATCH 6/6] Update `EnsureExistsAsync` document. --- docs/en/Entity-Framework-Core.md | 12 ------------ docs/en/Repositories.md | 4 ++++ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/docs/en/Entity-Framework-Core.md b/docs/en/Entity-Framework-Core.md index de26b7c3f0..568623a15d 100644 --- a/docs/en/Entity-Framework-Core.md +++ b/docs/en/Entity-Framework-Core.md @@ -607,18 +607,6 @@ Whenever you access to a property/collection, EF Core automatically performs an See also [lazy loading document](https://docs.microsoft.com/en-us/ef/core/querying/related-data/lazy) of the EF Core. -## Repository Extensions - -There are some useful repository extension methods. - -````csharp -EnsureCollectionLoadedAsync // **Explicitly load** a sub collection. -EnsurePropertyLoadedAsync // **Explicitly load** a navigation property. -EnsureExistsAsync // Make sure the entitys exists, It accepts the entity id or a query expression. -HardDeleteAsync // Hard delete the entitys, It accepts one or more entities or query expressions. -HardDeleteWithUnitOfWorkAsync // Hard delete the entitys in a uow(create a new uow if current is null), It accepts one or more entities or query expressions. -```` - ## Access to the EF Core API In most cases, you want to hide EF Core APIs behind a repository (this is the main purpose of the repository pattern). However, if you want to access the `DbContext` instance over the repository, you can use `GetDbContext()` or `GetDbSet()` extension methods. Example: diff --git a/docs/en/Repositories.md b/docs/en/Repositories.md index dc82876efb..a1a5ac5cce 100644 --- a/docs/en/Repositories.md +++ b/docs/en/Repositories.md @@ -164,6 +164,10 @@ If your entity is a soft-delete entity, you can use the `HardDeleteAsync` method > See the [Data Filtering](Data-Filtering.md) documentation for more about soft-delete. +### Ensure Entities Exists + +The `EnsureExistsAsync` extension method accepts entity id or entities query expression to ensure entities exist, otherwise, it will throw `EntityNotFoundException`. + ## Other Generic Repository Types Standard `IRepository` interface exposes the standard `IQueryable` and you can freely query using the standard LINQ methods. This is fine for most of the applications. However, some ORM providers or database systems may not support standard `IQueryable` interface. If you want to use such providers, you can't rely on the `IQueryable`.