diff --git a/.editorconfig b/.editorconfig index 49e4d7f..d6f668b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,5 +1,8 @@ [*.cs] +# CA1510: Use 'ArgumentNullException.ThrowIfNull' instead of explicitly throwing a new exception instance +dotnet_diagnostic.CA1510.severity = none + # CA2007: Consider calling ConfigureAwait on the awaited task dotnet_diagnostic.CA2007.severity = none diff --git a/EFCore.GenericRepository.sln b/EFCore.GenericRepository.sln index 77a4919..5242437 100644 --- a/EFCore.GenericRepository.sln +++ b/EFCore.GenericRepository.sln @@ -20,6 +20,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TanvirArjel.EFCore.GenericR EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TanvirArjel.EFCore.QueryRepository", "src\TanvirArjel.EFCore.QueryRepository\TanvirArjel.EFCore.QueryRepository.csproj", "{B27024B0-3436-4E8B-9E6C-0D3C6850EE95}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{4704B96B-B9B8-4278-8581-15A16F254EA0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EFCore.QueryRepository.Tests", "tests\EFCore.QueryRepository.Tests\EFCore.QueryRepository.Tests.csproj", "{45A7BACF-88A0-43F6-A64D-FD7E16047080}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -42,6 +46,10 @@ Global {B27024B0-3436-4E8B-9E6C-0D3C6850EE95}.Debug|Any CPU.Build.0 = Debug|Any CPU {B27024B0-3436-4E8B-9E6C-0D3C6850EE95}.Release|Any CPU.ActiveCfg = Release|Any CPU {B27024B0-3436-4E8B-9E6C-0D3C6850EE95}.Release|Any CPU.Build.0 = Release|Any CPU + {45A7BACF-88A0-43F6-A64D-FD7E16047080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {45A7BACF-88A0-43F6-A64D-FD7E16047080}.Debug|Any CPU.Build.0 = Debug|Any CPU + {45A7BACF-88A0-43F6-A64D-FD7E16047080}.Release|Any CPU.ActiveCfg = Release|Any CPU + {45A7BACF-88A0-43F6-A64D-FD7E16047080}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -51,6 +59,7 @@ Global {375C3AF5-6957-46D1-92F4-869C7E26DE5F} = {A54E2127-8988-46A4-858A-39A4553504C6} {2D4EDBB7-8A7D-4348-881F-F670773401FA} = {98A19DB3-ED3F-4327-8F46-CE7C7D71EC65} {B27024B0-3436-4E8B-9E6C-0D3C6850EE95} = {98A19DB3-ED3F-4327-8F46-CE7C7D71EC65} + {45A7BACF-88A0-43F6-A64D-FD7E16047080} = {4704B96B-B9B8-4278-8581-15A16F254EA0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {B04FE49F-0045-4944-A589-88C8A33F7594} diff --git a/README.md b/README.md index a4391fb..53e8a1a 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ This library is a Generic Repository implementation for EF Core ORM which will r ## ⭐ Giving a star -**If you find this library useful, please don't forget to encouraging me to do such more stuffs by giving a star to this repository. Thank you.** +**If you find this library useful, please don't forget to encourage me to do such more stuffs by giving a star to this repository. Thank you.** ## 🔥 What's new @@ -74,7 +74,7 @@ Install-Package TanvirArjel.EFCore.GenericRepository dotnet add package TanvirArjel.EFCore.GenericRepository ``` -Then in the `ConfirugeServices` method of the `Startup` class: +Then in the `ConfigureServices` method of the `Startup` class: ```C# public void ConfigureServices(IServiceCollection services) @@ -104,7 +104,7 @@ Install-Package TanvirArjel.EFCore.QueryRepository dotnet add package TanvirArjel.EFCore.QueryRepository ``` -Then in the `ConfirugeServices` method of the `Startup` class: +Then in the `ConfigureServices` method of the `Startup` class: ```C# public void ConfigureServices(IServiceCollection services) @@ -125,17 +125,17 @@ public class EmployeeService { // For query version, please use `IQueryRepository` instead of `IRepository` private readonly IRepository _repository; // If single DbContext - private readonly IRepository _dbConext1Repository; // If multiple DbContext + private readonly IRepository _dbContext1Repository; // If multiple DbContext - public EmployeeService(IRepository repository, IRepository dbConext1Repository) + public EmployeeService(IRepository repository, IRepository dbContext1Repository) { _repository = repository; - _dbConext1Repository = dbConext1Repository; + _dbContext1Repository = dbContext1Repository; } public async Task GetEmployeeAsync(int employeeId) { - Employee employee = await _repository.GetByIdAsync(1); + Employee employee = await _repository.GetByIdAsync(employeeId); return employee; } } @@ -146,12 +146,12 @@ public class EmployeeService public class EmployeeService { private readonly IRepository _repository; // If single DbContext - private readonly IRepository _dbConext1Repository; // If multiple DbContext + private readonly IRepository _dbContext1Repository; // If multiple DbContext - public EmployeeService(IRepository repository, IRepository dbConext1Repository) + public EmployeeService(IRepository repository, IRepository dbContext1Repository) { _repository = repository; - _dbConext1Repository = dbConext1Repository; + _dbContext1Repository = dbContext1Repository; } public async Task CreateAsync(Employee employee) @@ -164,4 +164,4 @@ public class EmployeeService } ``` -### For more detail documentaion, please visit [Documentation Wiki](https://github.com/TanvirArjel/EFCore.GenericRepository/wiki) +### For more detail documentation, please visit [Documentation Wiki](https://github.com/TanvirArjel/EFCore.GenericRepository/wiki) diff --git a/demo/AspNetCoreApp/AspNetCoreApp.csproj b/demo/AspNetCoreApp/AspNetCoreApp.csproj index 4702f8f..3425d86 100644 --- a/demo/AspNetCoreApp/AspNetCoreApp.csproj +++ b/demo/AspNetCoreApp/AspNetCoreApp.csproj @@ -1,20 +1,21 @@ - net7.0 + net8.0 + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/demo/AspNetCoreApp/Properties/launchSettings.json b/demo/AspNetCoreApp/Properties/launchSettings.json index 41852ee..904e982 100644 --- a/demo/AspNetCoreApp/Properties/launchSettings.json +++ b/demo/AspNetCoreApp/Properties/launchSettings.json @@ -18,10 +18,10 @@ "AspNetCore5._0": { "commandName": "Project", "launchBrowser": true, + "applicationUrl": "https://localhost:5001;http://localhost:5000", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, - "applicationUrl": "https://localhost:5001;http://localhost:5000", "dotnetRunMessages": "true" } } diff --git a/demo/UnitTest/UnitTest.csproj b/demo/UnitTest/UnitTest.csproj index c8a4db2..7d8f65b 100644 --- a/demo/UnitTest/UnitTest.csproj +++ b/demo/UnitTest/UnitTest.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 false diff --git a/src/TanvirArjel.EFCore.GenericRepository/TanvirArjel.EFCore.GenericRepository.csproj b/src/TanvirArjel.EFCore.GenericRepository/TanvirArjel.EFCore.GenericRepository.csproj index f2b0fd1..0649973 100644 --- a/src/TanvirArjel.EFCore.GenericRepository/TanvirArjel.EFCore.GenericRepository.csproj +++ b/src/TanvirArjel.EFCore.GenericRepository/TanvirArjel.EFCore.GenericRepository.csproj @@ -2,7 +2,7 @@ netstandard2.1;net6.0;net7.0;net8.0 - 6.0.1 + 6.0.2 8.0 true @@ -75,7 +75,7 @@ - + diff --git a/src/TanvirArjel.EFCore.QueryRepository/QueryRepository.cs b/src/TanvirArjel.EFCore.QueryRepository/QueryRepository.cs index d74a2e4..b86ce62 100644 --- a/src/TanvirArjel.EFCore.QueryRepository/QueryRepository.cs +++ b/src/TanvirArjel.EFCore.QueryRepository/QueryRepository.cs @@ -18,10 +18,11 @@ using Microsoft.EntityFrameworkCore.Query; [assembly: InternalsVisibleTo("TanvirArjel.EFCore.GenericRepository")] +[assembly: InternalsVisibleTo("EFCore.QueryRepository.Tests")] namespace TanvirArjel.EFCore.GenericRepository { - [DebuggerStepThrough] + // [DebuggerStepThrough] internal class QueryRepository : IQueryRepository, IQueryRepository where TDbContext : DbContext { @@ -389,8 +390,10 @@ public async Task GetByIdAsync( IQueryable query = _dbContext.Set(); - return await query.Where(expressionTree).Select(selectExpression) - .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + return await query.Where(expressionTree) + .Select(selectExpression) + .FirstOrDefaultAsync(cancellationToken) + .ConfigureAwait(false); } public Task GetAsync( @@ -550,11 +553,11 @@ public async Task ExistsByIdAsync(object id, CancellationToken cancella throw new ArgumentException("Entity does not have any primary key defined", nameof(id)); } - object primayKeyValue = null; + object primaryKeyValue = null; try { - primayKeyValue = Convert.ChangeType(id, primaryKeyType, CultureInfo.InvariantCulture); + primaryKeyValue = Convert.ChangeType(id, primaryKeyType, CultureInfo.InvariantCulture); } catch (Exception) { @@ -563,7 +566,7 @@ public async Task ExistsByIdAsync(object id, CancellationToken cancella ParameterExpression pe = Expression.Parameter(typeof(T), "entity"); MemberExpression me = Expression.Property(pe, primaryKeyName); - ConstantExpression constant = Expression.Constant(primayKeyValue, primaryKeyType); + ConstantExpression constant = Expression.Constant(primaryKeyValue, primaryKeyType); BinaryExpression body = Expression.Equal(me, constant); Expression> expressionTree = Expression.Lambda>(body, new[] { pe }); diff --git a/src/TanvirArjel.EFCore.QueryRepository/QueryableExtensions.cs b/src/TanvirArjel.EFCore.QueryRepository/QueryableExtensions.cs index c758eb0..2d482da 100644 --- a/src/TanvirArjel.EFCore.QueryRepository/QueryableExtensions.cs +++ b/src/TanvirArjel.EFCore.QueryRepository/QueryableExtensions.cs @@ -89,18 +89,18 @@ public static async Task> ToPaginatedListAsync( if (specification.PageIndex < 1) { - throw new ArgumentOutOfRangeException(nameof(specification.PageIndex), $"The value of {nameof(specification.PageIndex)} must be greater than 0."); + throw new ArgumentOutOfRangeException(nameof(specification), $"The value of {nameof(specification.PageIndex)} must be greater than 0."); } if (specification.PageSize < 1) { - throw new ArgumentOutOfRangeException(nameof(specification.PageSize), $"The value of {nameof(specification.PageSize)} must be greater than 0."); + throw new ArgumentOutOfRangeException(nameof(specification), $"The value of {nameof(specification.PageSize)} must be greater than 0."); } IQueryable countSource = source; // modify the IQueryable using the specification's expression criteria - if (specification.Conditions != null && specification.Conditions.Any()) + if (specification.Conditions != null && specification.Conditions.Count != 0) { foreach (Expression> condition in specification.Conditions) { diff --git a/src/TanvirArjel.EFCore.QueryRepository/SpecificationEvaluator.cs b/src/TanvirArjel.EFCore.QueryRepository/SpecificationEvaluator.cs index 781b07d..bef7c8c 100644 --- a/src/TanvirArjel.EFCore.QueryRepository/SpecificationEvaluator.cs +++ b/src/TanvirArjel.EFCore.QueryRepository/SpecificationEvaluator.cs @@ -21,7 +21,7 @@ public static IQueryable GetSpecifiedQuery(this IQueryable inputQuery, { if (specification.Skip < 0) { - throw new ArgumentOutOfRangeException(nameof(specification.Skip), $"The value of {nameof(specification.Skip)} in {nameof(specification)} can not be negative."); + throw new ArgumentOutOfRangeException(nameof(specification), $"The value of {nameof(specification.Skip)} in {nameof(specification)} can not be negative."); } query = query.Skip((int)specification.Skip); @@ -31,7 +31,7 @@ public static IQueryable GetSpecifiedQuery(this IQueryable inputQuery, { if (specification.Take < 0) { - throw new ArgumentOutOfRangeException(nameof(specification.Take), $"The value of {nameof(specification.Take)} in {nameof(specification)} can not be negative."); + throw new ArgumentOutOfRangeException(nameof(specification), $"The value of {nameof(specification.Take)} in {nameof(specification)} can not be negative."); } query = query.Take((int)specification.Take); @@ -55,12 +55,12 @@ public static IQueryable GetSpecifiedQuery(this IQueryable inputQuery, if (specification.PageIndex < 1) { - throw new ArgumentOutOfRangeException(nameof(specification.PageIndex), "The value of pageIndex must be greater than 0."); + throw new ArgumentOutOfRangeException(nameof(specification), "The value of specification.PageIndex must be greater than 0."); } if (specification.PageSize < 1) { - throw new ArgumentOutOfRangeException(nameof(specification.PageSize), "The value of pageSize must be greater than 0."); + throw new ArgumentOutOfRangeException(nameof(specification), "The value of specification.PageSize must be greater than 0."); } IQueryable query = GetSpecifiedQuery(inputQuery, (SpecificationBase)specification); @@ -89,7 +89,7 @@ public static IQueryable GetSpecifiedQuery(this IQueryable inputQuery, IQueryable query = inputQuery; // modify the IQueryable using the specification's criteria expression - if (specification.Conditions != null && specification.Conditions.Any()) + if (specification.Conditions != null && specification.Conditions.Count != 0) { foreach (Expression> specificationCondition in specification.Conditions) { diff --git a/src/TanvirArjel.EFCore.QueryRepository/TanvirArjel.EFCore.QueryRepository.csproj b/src/TanvirArjel.EFCore.QueryRepository/TanvirArjel.EFCore.QueryRepository.csproj index fdbc11f..40f182b 100644 --- a/src/TanvirArjel.EFCore.QueryRepository/TanvirArjel.EFCore.QueryRepository.csproj +++ b/src/TanvirArjel.EFCore.QueryRepository/TanvirArjel.EFCore.QueryRepository.csproj @@ -2,7 +2,7 @@ netstandard2.1;net6.0;net7.0;net8.0 - 1.5.1 + 1.5.2 8.0 true @@ -70,7 +70,7 @@ - + diff --git a/tests/EFCore.QueryRepository.Tests/DemoDbContext.cs b/tests/EFCore.QueryRepository.Tests/DemoDbContext.cs new file mode 100644 index 0000000..6cb329f --- /dev/null +++ b/tests/EFCore.QueryRepository.Tests/DemoDbContext.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore; + +namespace EFCore.QueryRepository.Tests; + +public class DemoDbContext : DbContext +{ + public DemoDbContext() + { + } + + public DemoDbContext(DbContextOptions options) : base(options) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + if (modelBuilder == null) + { + throw new ArgumentNullException(nameof(modelBuilder)); + } + + base.OnModelCreating(modelBuilder); + modelBuilder.Entity().HasKey(e => e.Id); + } + + public virtual DbSet Employees { get; set; } +} + +public class Employee +{ + public int Id { get; set; } + + public int DepartmentId { get; set; } + + public string Name { get; set; } + + public Department Department { get; set; } +} + +public class Department +{ + public int Id { get; set; } + + public string Name { get; set; } +} diff --git a/tests/EFCore.QueryRepository.Tests/EFCore.QueryRepository.Tests.csproj b/tests/EFCore.QueryRepository.Tests/EFCore.QueryRepository.Tests.csproj new file mode 100644 index 0000000..3067435 --- /dev/null +++ b/tests/EFCore.QueryRepository.Tests/EFCore.QueryRepository.Tests.csproj @@ -0,0 +1,28 @@ + + + + net8.0 + enable + false + true + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/tests/EFCore.QueryRepository.Tests/GlobalUsings.cs b/tests/EFCore.QueryRepository.Tests/GlobalUsings.cs new file mode 100644 index 0000000..8c927eb --- /dev/null +++ b/tests/EFCore.QueryRepository.Tests/GlobalUsings.cs @@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/tests/EFCore.QueryRepository.Tests/QueryRepositoryTests.cs b/tests/EFCore.QueryRepository.Tests/QueryRepositoryTests.cs new file mode 100644 index 0000000..72fd5ce --- /dev/null +++ b/tests/EFCore.QueryRepository.Tests/QueryRepositoryTests.cs @@ -0,0 +1,90 @@ +using Microsoft.EntityFrameworkCore; +using Moq; +using Moq.EntityFrameworkCore; +using TanvirArjel.EFCore.GenericRepository; + +namespace EFCore.QueryRepository.Tests; + +public class QueryRepositoryTests +{ + private readonly List fakeEmployees = new() + { + new() + { + Id = 1, + DepartmentId = 1, + Name = "Mark", + Department = new Department + { + Id = 1, + Name = "IT" + } + }, + new() + { + Id = 2, + DepartmentId = 1, + Name = "Merry", + Department = new Department + { + Id = 2, + Name = "HR" + } + } + }; + + [Fact] + public async Task GetListAsyncTest() + { + // Arrange + Mock mockDemoContext = new(); + mockDemoContext.Setup(context => context.Set()).ReturnsDbSet(fakeEmployees); + + // Act + QueryRepository queryRepository = new(mockDemoContext.Object); + List employees = await queryRepository.GetListAsync(); + + // Assert + Assert.NotNull(employees); + Assert.NotEmpty(employees); + Assert.True(employees.Count == fakeEmployees.Count); + } + + [Fact] + public async Task GetListAsync_WithCondition_Test() + { + // Arrange + int expectedCount = fakeEmployees.Where(e => e.Id == 1).Count(); + string expectedName = "Mark"; + + Mock mockDemoContext = new(); + mockDemoContext.Setup(context => context.Set()).ReturnsDbSet(fakeEmployees); + + // Act + QueryRepository queryRepository = new(mockDemoContext.Object); + List employees = await queryRepository.GetListAsync(e => e.Id == 1); + + // Assert + Assert.NotNull(employees); + Assert.NotEmpty(employees); + Assert.True(employees.Count == expectedCount); + Assert.Equal(expectedName, employees.First().Name); + } + + [Fact] + public async Task GetListAsync_WithInclude_Test() + { + // Arrange + Mock mockDemoContext = new(); + mockDemoContext.Setup(context => context.Set()).ReturnsDbSet(fakeEmployees); + + // Act + QueryRepository queryRepository = new(mockDemoContext.Object); + List employees = await queryRepository.GetListAsync(q => q.Include(e => e.Department)); + + // Assert + Assert.NotNull(employees); + Assert.NotEmpty(employees); + Assert.True(employees.Count == fakeEmployees.Count); + } +} \ No newline at end of file