From 60d3239e2b6173d4fd501f73221c42ca7e46b1cc Mon Sep 17 00:00:00 2001 From: Fati Iseni Date: Sat, 22 Jul 2023 00:30:00 +0200 Subject: [PATCH] Added sample applications. (#347) --- Ardalis.Specification.sln | 42 ++--- .../Ardalis.Sample.App1.csproj | 22 +++ sample/Ardalis.Sample.App1/Program.cs | 145 ++++++++++++++++ .../appsettings.Development.json | 8 + sample/Ardalis.Sample.App1/appsettings.json | 12 ++ .../AppSpecificationEvaluator.cs | 13 ++ .../Ardalis.Sample.App2.csproj | 22 +++ sample/Ardalis.Sample.App2/IReadRepository.cs | 24 +++ sample/Ardalis.Sample.App2/IRepository.cs | 27 +++ sample/Ardalis.Sample.App2/PagedResponse.cs | 14 ++ sample/Ardalis.Sample.App2/Pagination.cs | 95 +++++++++++ .../Ardalis.Sample.App2/PaginationSettings.cs | 14 ++ sample/Ardalis.Sample.App2/Program.cs | 139 +++++++++++++++ .../Ardalis.Sample.App2/QueryTagEvaluator.cs | 22 +++ sample/Ardalis.Sample.App2/RepositoryBase.cs | 156 +++++++++++++++++ .../appsettings.Development.json | 8 + sample/Ardalis.Sample.App2/appsettings.json | 12 ++ sample/Ardalis.Sample.Domain/Address.cs | 9 + .../Ardalis.Sample.Domain.csproj} | 9 +- sample/Ardalis.Sample.Domain/Customer.cs | 10 ++ .../Filters/BaseFilter.cs | 9 + .../Filters/CustomerFilter.cs | 8 + .../Ardalis.Sample.Domain/IAggregateRoot.cs | 5 + .../Specs/CustomerByIdSpec.cs | 12 ++ .../Specs/CustomerSpec.cs | 24 +++ .../Specs/CustomerSpecExtensions.cs | 36 ++++ .../Entities/CustomerAggregate/Customer.cs | 54 ------ .../Entities/CustomerAggregate/Store.cs | 20 --- .../Entities/Seeds/CustomerSeed.cs | 23 --- .../Interfaces/IAggregateRoot.cs | 5 - .../Interfaces/ICustomerRepository.cs | 11 -- .../Interfaces/IRepository.cs | 13 -- .../Specifications/CustomerByNameSpec.cs | 17 -- .../CustomerByNameWithStoresSpec.cs | 17 -- .../Specifications/CustomerSpec.cs | 33 ---- .../Specifications/Filters/BaseFilter.cs | 10 -- .../Specifications/Filters/CustomerFilter.cs | 8 - .../Specifications/PaginationHelper.cs | 29 ---- .../Ardalis.SampleApp.Infrastructure.csproj | 22 --- .../Data/CachedCustomerRepository.cs | 158 ------------------ .../Data/CustomerRepository.cs | 28 ---- .../Data/MyRepository.cs | 18 -- .../Configurations/CustomerConfiguration.cs | 18 -- .../Configurations/StoreConfiguration.cs | 15 -- .../DataAccess/SampleDbContext.cs | 23 --- .../DataAccess/SampleDbContextSeed.cs | 42 ----- .../20201102224526_SampleDB-v1.Designer.cs | 78 --------- .../Migrations/20201102224526_SampleDB-v1.cs | 59 ------- .../SampleDbContextModelSnapshot.cs | 76 --------- .../Ardalis.SampleApp.Web.csproj | 29 ---- .../Ardalis.SampleApp.Web/AutomapperMaps.cs | 19 --- .../Controllers/CustomersController.cs | 44 ----- .../Interfaces/ICustomerUiService.cs | 14 -- .../Models/BaseFilterDto.cs | 18 -- .../Models/CustomerDto.cs | 13 -- .../Models/CustomerFilterDto.cs | 8 - .../Ardalis.SampleApp.Web/Models/StoreDto.cs | 8 - .../Ardalis.SampleApp.Web/Pages/Index.cshtml | 24 --- .../Pages/Index.cshtml.cs | 35 ---- sample/Ardalis.SampleApp.Web/Program.cs | 42 ----- .../Services/CustomerUiService.cs | 63 ------- sample/Ardalis.SampleApp.Web/Startup.cs | 60 ------- .../appsettings.Development.json | 12 -- sample/Ardalis.SampleApp.Web/appsettings.json | 13 -- 64 files changed, 870 insertions(+), 1206 deletions(-) create mode 100644 sample/Ardalis.Sample.App1/Ardalis.Sample.App1.csproj create mode 100644 sample/Ardalis.Sample.App1/Program.cs create mode 100644 sample/Ardalis.Sample.App1/appsettings.Development.json create mode 100644 sample/Ardalis.Sample.App1/appsettings.json create mode 100644 sample/Ardalis.Sample.App2/AppSpecificationEvaluator.cs create mode 100644 sample/Ardalis.Sample.App2/Ardalis.Sample.App2.csproj create mode 100644 sample/Ardalis.Sample.App2/IReadRepository.cs create mode 100644 sample/Ardalis.Sample.App2/IRepository.cs create mode 100644 sample/Ardalis.Sample.App2/PagedResponse.cs create mode 100644 sample/Ardalis.Sample.App2/Pagination.cs create mode 100644 sample/Ardalis.Sample.App2/PaginationSettings.cs create mode 100644 sample/Ardalis.Sample.App2/Program.cs create mode 100644 sample/Ardalis.Sample.App2/QueryTagEvaluator.cs create mode 100644 sample/Ardalis.Sample.App2/RepositoryBase.cs create mode 100644 sample/Ardalis.Sample.App2/appsettings.Development.json create mode 100644 sample/Ardalis.Sample.App2/appsettings.json create mode 100644 sample/Ardalis.Sample.Domain/Address.cs rename sample/{Ardalis.SampleApp.Core/Ardalis.SampleApp.Core.csproj => Ardalis.Sample.Domain/Ardalis.Sample.Domain.csproj} (54%) create mode 100644 sample/Ardalis.Sample.Domain/Customer.cs create mode 100644 sample/Ardalis.Sample.Domain/Filters/BaseFilter.cs create mode 100644 sample/Ardalis.Sample.Domain/Filters/CustomerFilter.cs create mode 100644 sample/Ardalis.Sample.Domain/IAggregateRoot.cs create mode 100644 sample/Ardalis.Sample.Domain/Specs/CustomerByIdSpec.cs create mode 100644 sample/Ardalis.Sample.Domain/Specs/CustomerSpec.cs create mode 100644 sample/Ardalis.Sample.Domain/Specs/CustomerSpecExtensions.cs delete mode 100644 sample/Ardalis.SampleApp.Core/Entities/CustomerAggregate/Customer.cs delete mode 100644 sample/Ardalis.SampleApp.Core/Entities/CustomerAggregate/Store.cs delete mode 100644 sample/Ardalis.SampleApp.Core/Entities/Seeds/CustomerSeed.cs delete mode 100644 sample/Ardalis.SampleApp.Core/Interfaces/IAggregateRoot.cs delete mode 100644 sample/Ardalis.SampleApp.Core/Interfaces/ICustomerRepository.cs delete mode 100644 sample/Ardalis.SampleApp.Core/Interfaces/IRepository.cs delete mode 100644 sample/Ardalis.SampleApp.Core/Specifications/CustomerByNameSpec.cs delete mode 100644 sample/Ardalis.SampleApp.Core/Specifications/CustomerByNameWithStoresSpec.cs delete mode 100644 sample/Ardalis.SampleApp.Core/Specifications/CustomerSpec.cs delete mode 100644 sample/Ardalis.SampleApp.Core/Specifications/Filters/BaseFilter.cs delete mode 100644 sample/Ardalis.SampleApp.Core/Specifications/Filters/CustomerFilter.cs delete mode 100644 sample/Ardalis.SampleApp.Core/Specifications/PaginationHelper.cs delete mode 100644 sample/Ardalis.SampleApp.Infrastructure/Ardalis.SampleApp.Infrastructure.csproj delete mode 100644 sample/Ardalis.SampleApp.Infrastructure/Data/CachedCustomerRepository.cs delete mode 100644 sample/Ardalis.SampleApp.Infrastructure/Data/CustomerRepository.cs delete mode 100644 sample/Ardalis.SampleApp.Infrastructure/Data/MyRepository.cs delete mode 100644 sample/Ardalis.SampleApp.Infrastructure/DataAccess/Configurations/CustomerConfiguration.cs delete mode 100644 sample/Ardalis.SampleApp.Infrastructure/DataAccess/Configurations/StoreConfiguration.cs delete mode 100644 sample/Ardalis.SampleApp.Infrastructure/DataAccess/SampleDbContext.cs delete mode 100644 sample/Ardalis.SampleApp.Infrastructure/DataAccess/SampleDbContextSeed.cs delete mode 100644 sample/Ardalis.SampleApp.Infrastructure/Migrations/20201102224526_SampleDB-v1.Designer.cs delete mode 100644 sample/Ardalis.SampleApp.Infrastructure/Migrations/20201102224526_SampleDB-v1.cs delete mode 100644 sample/Ardalis.SampleApp.Infrastructure/Migrations/SampleDbContextModelSnapshot.cs delete mode 100644 sample/Ardalis.SampleApp.Web/Ardalis.SampleApp.Web.csproj delete mode 100644 sample/Ardalis.SampleApp.Web/AutomapperMaps.cs delete mode 100644 sample/Ardalis.SampleApp.Web/Controllers/CustomersController.cs delete mode 100644 sample/Ardalis.SampleApp.Web/Interfaces/ICustomerUiService.cs delete mode 100644 sample/Ardalis.SampleApp.Web/Models/BaseFilterDto.cs delete mode 100644 sample/Ardalis.SampleApp.Web/Models/CustomerDto.cs delete mode 100644 sample/Ardalis.SampleApp.Web/Models/CustomerFilterDto.cs delete mode 100644 sample/Ardalis.SampleApp.Web/Models/StoreDto.cs delete mode 100644 sample/Ardalis.SampleApp.Web/Pages/Index.cshtml delete mode 100644 sample/Ardalis.SampleApp.Web/Pages/Index.cshtml.cs delete mode 100644 sample/Ardalis.SampleApp.Web/Program.cs delete mode 100644 sample/Ardalis.SampleApp.Web/Services/CustomerUiService.cs delete mode 100644 sample/Ardalis.SampleApp.Web/Startup.cs delete mode 100644 sample/Ardalis.SampleApp.Web/appsettings.Development.json delete mode 100644 sample/Ardalis.SampleApp.Web/appsettings.json diff --git a/Ardalis.Specification.sln b/Ardalis.Specification.sln index c70637d7..366ecdf5 100644 --- a/Ardalis.Specification.sln +++ b/Ardalis.Specification.sln @@ -3,12 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.31612.314 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ardalis.SampleApp.Core", "sample\Ardalis.SampleApp.Core\Ardalis.SampleApp.Core.csproj", "{CAAAD1EF-5375-4B71-92CB-237692D60262}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ardalis.SampleApp.Web", "sample\Ardalis.SampleApp.Web\Ardalis.SampleApp.Web.csproj", "{EB3F4056-691C-4D22-903B-A2467D93D552}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ardalis.SampleApp.Infrastructure", "sample\Ardalis.SampleApp.Infrastructure\Ardalis.SampleApp.Infrastructure.csproj", "{C2EC4331-4C8A-4121-A68C-4C96A7D40A98}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ardalis.Specification", "Specification\src\Ardalis.Specification\Ardalis.Specification.csproj", "{5FF21FE4-19B9-4BD2-958D-EAFEDE72D7F8}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ardalis.Specification.UnitTests", "Specification\tests\Ardalis.Specification.UnitTests\Ardalis.Specification.UnitTests.csproj", "{85D42214-D223-4F13-9A19-9CFA0D3C1981}" @@ -45,24 +39,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specification.EntityFramewo EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Specification.EntityFramework6", "Specification.EntityFramework6", "{327AEBD6-C8A6-4851-BA42-632F8014CFC5}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ardalis.Sample.Domain", "sample\Ardalis.Sample.Domain\Ardalis.Sample.Domain.csproj", "{4386E123-F4CA-4607-BD8F-8EB11D92458C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ardalis.Sample.App1", "sample\Ardalis.Sample.App1\Ardalis.Sample.App1.csproj", "{EBABFB82-50D0-41A2-AEAC-F0E2C103ED08}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ardalis.Sample.App2", "sample\Ardalis.Sample.App2\Ardalis.Sample.App2.csproj", "{ECBFDD7F-E60F-48C4-8279-DD6B3B03D27A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {CAAAD1EF-5375-4B71-92CB-237692D60262}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {CAAAD1EF-5375-4B71-92CB-237692D60262}.Debug|Any CPU.Build.0 = Debug|Any CPU - {CAAAD1EF-5375-4B71-92CB-237692D60262}.Release|Any CPU.ActiveCfg = Release|Any CPU - {CAAAD1EF-5375-4B71-92CB-237692D60262}.Release|Any CPU.Build.0 = Release|Any CPU - {EB3F4056-691C-4D22-903B-A2467D93D552}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EB3F4056-691C-4D22-903B-A2467D93D552}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EB3F4056-691C-4D22-903B-A2467D93D552}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EB3F4056-691C-4D22-903B-A2467D93D552}.Release|Any CPU.Build.0 = Release|Any CPU - {C2EC4331-4C8A-4121-A68C-4C96A7D40A98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C2EC4331-4C8A-4121-A68C-4C96A7D40A98}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C2EC4331-4C8A-4121-A68C-4C96A7D40A98}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C2EC4331-4C8A-4121-A68C-4C96A7D40A98}.Release|Any CPU.Build.0 = Release|Any CPU {5FF21FE4-19B9-4BD2-958D-EAFEDE72D7F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5FF21FE4-19B9-4BD2-958D-EAFEDE72D7F8}.Debug|Any CPU.Build.0 = Debug|Any CPU {5FF21FE4-19B9-4BD2-958D-EAFEDE72D7F8}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -87,20 +75,32 @@ Global {4BEB4DC4-DE33-4DF1-8A2F-CE76C1D72A4A}.Debug|Any CPU.Build.0 = Debug|Any CPU {4BEB4DC4-DE33-4DF1-8A2F-CE76C1D72A4A}.Release|Any CPU.ActiveCfg = Release|Any CPU {4BEB4DC4-DE33-4DF1-8A2F-CE76C1D72A4A}.Release|Any CPU.Build.0 = Release|Any CPU + {4386E123-F4CA-4607-BD8F-8EB11D92458C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4386E123-F4CA-4607-BD8F-8EB11D92458C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4386E123-F4CA-4607-BD8F-8EB11D92458C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4386E123-F4CA-4607-BD8F-8EB11D92458C}.Release|Any CPU.Build.0 = Release|Any CPU + {EBABFB82-50D0-41A2-AEAC-F0E2C103ED08}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EBABFB82-50D0-41A2-AEAC-F0E2C103ED08}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EBABFB82-50D0-41A2-AEAC-F0E2C103ED08}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EBABFB82-50D0-41A2-AEAC-F0E2C103ED08}.Release|Any CPU.Build.0 = Release|Any CPU + {ECBFDD7F-E60F-48C4-8279-DD6B3B03D27A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ECBFDD7F-E60F-48C4-8279-DD6B3B03D27A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ECBFDD7F-E60F-48C4-8279-DD6B3B03D27A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ECBFDD7F-E60F-48C4-8279-DD6B3B03D27A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {CAAAD1EF-5375-4B71-92CB-237692D60262} = {1FCFDF4F-D0E2-4E30-8ABC-FE6DC3628228} - {EB3F4056-691C-4D22-903B-A2467D93D552} = {1FCFDF4F-D0E2-4E30-8ABC-FE6DC3628228} - {C2EC4331-4C8A-4121-A68C-4C96A7D40A98} = {1FCFDF4F-D0E2-4E30-8ABC-FE6DC3628228} {5FF21FE4-19B9-4BD2-958D-EAFEDE72D7F8} = {9CA909ED-E3A6-4D76-A4F3-250E93942813} {85D42214-D223-4F13-9A19-9CFA0D3C1981} = {9CA909ED-E3A6-4D76-A4F3-250E93942813} {5EB4ADA4-5258-4964-BD7D-11D4F926E344} = {B19F2F64-4B22-48C2-B2F8-7672F84F758D} {5AFD1454-E625-451D-A615-CEB7BB09AA65} = {B19F2F64-4B22-48C2-B2F8-7672F84F758D} {37EC09C7-702D-4539-B98D-F67B15E1E6CE} = {327AEBD6-C8A6-4851-BA42-632F8014CFC5} {4BEB4DC4-DE33-4DF1-8A2F-CE76C1D72A4A} = {327AEBD6-C8A6-4851-BA42-632F8014CFC5} + {4386E123-F4CA-4607-BD8F-8EB11D92458C} = {1FCFDF4F-D0E2-4E30-8ABC-FE6DC3628228} + {EBABFB82-50D0-41A2-AEAC-F0E2C103ED08} = {1FCFDF4F-D0E2-4E30-8ABC-FE6DC3628228} + {ECBFDD7F-E60F-48C4-8279-DD6B3B03D27A} = {1FCFDF4F-D0E2-4E30-8ABC-FE6DC3628228} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C153A625-42F7-49A7-B99A-6A78B4B866B2} diff --git a/sample/Ardalis.Sample.App1/Ardalis.Sample.App1.csproj b/sample/Ardalis.Sample.App1/Ardalis.Sample.App1.csproj new file mode 100644 index 00000000..993af2c4 --- /dev/null +++ b/sample/Ardalis.Sample.App1/Ardalis.Sample.App1.csproj @@ -0,0 +1,22 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + + + + + diff --git a/sample/Ardalis.Sample.App1/Program.cs b/sample/Ardalis.Sample.App1/Program.cs new file mode 100644 index 00000000..cc039e17 --- /dev/null +++ b/sample/Ardalis.Sample.App1/Program.cs @@ -0,0 +1,145 @@ +using Ardalis.Sample.Domain; +using Ardalis.Sample.Domain.Specs; +using Ardalis.Specification; +using Ardalis.Specification.EntityFrameworkCore; +using AutoMapper; +using Microsoft.EntityFrameworkCore; + +var builder = WebApplication.CreateBuilder(args); + +var connectionString = builder.Configuration.GetConnectionString("DbConnection"); +builder.Services.AddDbContext(options => options.UseSqlServer(connectionString)); +builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>)); +builder.Services.AddScoped(typeof(IReadRepository<>), typeof(ReadRepository<>)); + +builder.Services.AddAutoMapper(typeof(MappingProfile).Assembly); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); +var app = builder.Build(); +app.UseSwagger(); +app.UseSwaggerUI(); +app.UseHttpsRedirection(); + + +app.MapGet("/customers", async (IReadRepository repo, IMapper mapper, CancellationToken cancellationToken) => +{ + var spec = new CustomerSpec(); + var customers = await repo.ListAsync(spec, cancellationToken); + var customersDto = mapper.Map>(customers); + return Results.Ok(customersDto); +}); + +app.MapGet("/customers/{id}", async (IReadRepository repo, IMapper mapper, int id, CancellationToken cancellationToken) => +{ + var spec = new CustomerByIdSpec(id); + var customer = await repo.FirstOrDefaultAsync(spec, cancellationToken); + if (customer is null) return Results.NotFound(); + var customerDto = mapper.Map(customer); + return Results.Ok(customerDto); +}); + +app.MapPost("/customers", async (IRepository repo, IMapper mapper, CustomerCreateDto customerCreateDto, CancellationToken cancellationToken) => +{ + var customer = new Customer + { + Name = customerCreateDto.Name, + Age = customerCreateDto.Age, + Addresses = customerCreateDto.Addresses.Select(a => new Address { Street = a.Street }).ToList() + }; + await repo.AddAsync(customer, cancellationToken); + var customerDto = mapper.Map(customer); + return Results.Ok(customerDto); +}); + +app.MapPut("/customers/{id}", async (IRepository repo, IMapper mapper, int id, CustomerUpdateDto customerUpdate, CancellationToken cancellationToken) => +{ + var spec = new CustomerByIdSpec(id); + var customer = await repo.FirstOrDefaultAsync(spec, cancellationToken); + if (customer is null) return Results.NotFound(); + customer.Name = customerUpdate.Name; + customer.Age = customerUpdate.Age; + await repo.UpdateAsync(customer, cancellationToken); + var customerDto = mapper.Map(customer); + return Results.Ok(customerDto); +}); + +await app.InitializeDbAsync(); +app.Run(); + +public record AddressDto(int Id, string Street, int CustomerId); +public record AddressCreateDto(string Street); +public record CustomerDto(int Id, string Name, int Age, List Addresses); +public record CustomerCreateDto(string Name, int Age, List Addresses); +public record CustomerUpdateDto(string Name, int Age); + +public class AppDbContext : DbContext +{ + public DbSet Customers => Set(); + public AppDbContext(DbContextOptions options) : base(options) + { + } +} + +public interface IRepository : IRepositoryBase where T : class, IAggregateRoot +{ +} +public interface IReadRepository : IReadRepositoryBase where T : class +{ +} +public class Repository : RepositoryBase, IRepository where T : class, IAggregateRoot +{ + public Repository(AppDbContext dbContext) : base(dbContext) + { + } +} +public class ReadRepository : RepositoryBase, IReadRepository where T : class +{ + public ReadRepository(AppDbContext dbContext) : base(dbContext) + { + } +} + +public class MappingProfile : Profile +{ + public MappingProfile() + { + CreateMap(); + CreateMap(); + } +} + +public static class WebApplicationExtensions +{ + public static async Task InitializeDbAsync(this WebApplication app) + { + using var scope = app.Services.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + await dbContext.Database.EnsureDeletedAsync(); + await dbContext.Database.EnsureCreatedAsync(); + var customers = new List() + { + new() + { + Name = "Customer1", + Age = 20, + Addresses = new() + { + new() { Street = "Street1_1" }, + new() { Street = "Street1_2" } + } + }, + new() + { + Name = "Customer2", + Age = 30, + Addresses = new() + { + new() { Street = "Street2_1" }, + new() { Street = "Street3_2" } + } + } + }; + dbContext.Customers.AddRange(customers); + await dbContext.SaveChangesAsync(); + } +} diff --git a/sample/Ardalis.Sample.App1/appsettings.Development.json b/sample/Ardalis.Sample.App1/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/sample/Ardalis.Sample.App1/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/sample/Ardalis.Sample.App1/appsettings.json b/sample/Ardalis.Sample.App1/appsettings.json new file mode 100644 index 00000000..93642730 --- /dev/null +++ b/sample/Ardalis.Sample.App1/appsettings.json @@ -0,0 +1,12 @@ +{ + "ConnectionStrings": { + "DbConnection": "Server=(localdb)\\mssqllocaldb;Database=SpecificationSampleApp1;Trusted_Connection=True;TrustServerCertificate=Yes" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/sample/Ardalis.Sample.App2/AppSpecificationEvaluator.cs b/sample/Ardalis.Sample.App2/AppSpecificationEvaluator.cs new file mode 100644 index 00000000..8bf1737e --- /dev/null +++ b/sample/Ardalis.Sample.App2/AppSpecificationEvaluator.cs @@ -0,0 +1,13 @@ +using Ardalis.Specification.EntityFrameworkCore; + +namespace Ardalis.Sample.App2; + +public class AppSpecificationEvaluator : SpecificationEvaluator +{ + public static AppSpecificationEvaluator Instance { get; } = new AppSpecificationEvaluator(); + + public AppSpecificationEvaluator() : base() + { + Evaluators.Add(QueryTagEvaluator.Instance); + } +} diff --git a/sample/Ardalis.Sample.App2/Ardalis.Sample.App2.csproj b/sample/Ardalis.Sample.App2/Ardalis.Sample.App2.csproj new file mode 100644 index 00000000..993af2c4 --- /dev/null +++ b/sample/Ardalis.Sample.App2/Ardalis.Sample.App2.csproj @@ -0,0 +1,22 @@ + + + + net7.0 + enable + enable + + + + + + + + + + + + + + + + diff --git a/sample/Ardalis.Sample.App2/IReadRepository.cs b/sample/Ardalis.Sample.App2/IReadRepository.cs new file mode 100644 index 00000000..9a1f3a21 --- /dev/null +++ b/sample/Ardalis.Sample.App2/IReadRepository.cs @@ -0,0 +1,24 @@ +using Ardalis.Sample.Domain.Filters; +using Ardalis.Specification; + +namespace Ardalis.Sample.App2; + +public interface IReadRepository where T : class +{ + Task FindAsync(TId id, CancellationToken cancellationToken = default) where TId : notnull; + Task FirstOrDefaultAsync(ISpecification specification, CancellationToken cancellationToken = default); + Task FirstOrDefaultAsync(ISpecification specification, CancellationToken cancellationToken = default); + Task SingleOrDefaultAsync(ISpecification specification, CancellationToken cancellationToken = default); + Task SingleOrDefaultAsync(ISpecification specification, CancellationToken cancellationToken = default); + Task> ListAsync(CancellationToken cancellationToken = default); + Task> ListAsync(ISpecification specification, CancellationToken cancellationToken = default); + Task> ListAsync(ISpecification specification, CancellationToken cancellationToken = default); + Task CountAsync(CancellationToken cancellationToken = default); + Task CountAsync(ISpecification specification, CancellationToken cancellationToken = default); + Task AnyAsync(CancellationToken cancellationToken = default); + Task AnyAsync(ISpecification specification, CancellationToken cancellationToken = default); + + Task ProjectToFirstOrDefaultAsync(ISpecification specification, CancellationToken cancellationToken); + Task> ProjectToListAsync(ISpecification specification, CancellationToken cancellationToken); + Task> ProjectToListAsync(ISpecification specification, BaseFilter filter, CancellationToken cancellationToken); +} diff --git a/sample/Ardalis.Sample.App2/IRepository.cs b/sample/Ardalis.Sample.App2/IRepository.cs new file mode 100644 index 00000000..58f95ed0 --- /dev/null +++ b/sample/Ardalis.Sample.App2/IRepository.cs @@ -0,0 +1,27 @@ +using Ardalis.Sample.Domain; +using Ardalis.Specification; + +namespace Ardalis.Sample.App2; + +public interface IRepository where T : class, IAggregateRoot +{ + Task AddAsync(T entity, CancellationToken cancellationToken = default); + Task> AddRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default); + Task UpdateAsync(T entity, CancellationToken cancellationToken = default); + Task DeleteAsync(T entity, CancellationToken cancellationToken = default); + Task DeleteRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default); + Task SaveChangesAsync(CancellationToken cancellationToken = default); + + Task FindAsync(TId id, CancellationToken cancellationToken = default) where TId : notnull; + Task FirstOrDefaultAsync(ISpecification specification, CancellationToken cancellationToken = default); + Task FirstOrDefaultAsync(ISpecification specification, CancellationToken cancellationToken = default); + Task SingleOrDefaultAsync(ISpecification specification, CancellationToken cancellationToken = default); + Task SingleOrDefaultAsync(ISpecification specification, CancellationToken cancellationToken = default); + Task> ListAsync(CancellationToken cancellationToken = default); + Task> ListAsync(ISpecification specification, CancellationToken cancellationToken = default); + Task> ListAsync(ISpecification specification, CancellationToken cancellationToken = default); + Task CountAsync(CancellationToken cancellationToken = default); + Task CountAsync(ISpecification specification, CancellationToken cancellationToken = default); + Task AnyAsync(CancellationToken cancellationToken = default); + Task AnyAsync(ISpecification specification, CancellationToken cancellationToken = default); +} diff --git a/sample/Ardalis.Sample.App2/PagedResponse.cs b/sample/Ardalis.Sample.App2/PagedResponse.cs new file mode 100644 index 00000000..27ac1ef5 --- /dev/null +++ b/sample/Ardalis.Sample.App2/PagedResponse.cs @@ -0,0 +1,14 @@ +namespace Ardalis.Sample.App2; + +public class PagedResponse +{ + public Pagination Pagination { get; } + public List Data { get; } + + public PagedResponse(List data, Pagination pagination) + { + Data = data; + Pagination = pagination; + } +} + diff --git a/sample/Ardalis.Sample.App2/Pagination.cs b/sample/Ardalis.Sample.App2/Pagination.cs new file mode 100644 index 00000000..109f45e1 --- /dev/null +++ b/sample/Ardalis.Sample.App2/Pagination.cs @@ -0,0 +1,95 @@ +using Ardalis.Sample.Domain.Filters; +using System.Text.Json.Serialization; + +namespace Ardalis.Sample.App2; + +public class Pagination +{ + private readonly PaginationSettings _paginationSettings; + + public int TotalItems { get; } + public int TotalPages { get; } + public int PageSize { get; } + public int Page { get; } + public int StartItem { get; } + public int EndItem { get; } + public bool HasPrevious { get; } + public bool HasNext { get; } + + [JsonIgnore] + public int Take { get; } + [JsonIgnore] + public int Skip { get; } + + [JsonConstructor] + public Pagination(int totalItems, int totalPages, int pageSize, int page, int startItem, int endItem, bool hasPrevious, bool hasNext) + { + _paginationSettings = default!; + TotalItems = totalItems; + TotalPages = totalPages; + PageSize = pageSize; + Page = page; + StartItem = startItem; + EndItem = endItem; + HasPrevious = hasPrevious; + HasNext = hasNext; + } + + public Pagination(int itemsCount, BaseFilter baseFilter) + : this(new PaginationSettings(10, 100), itemsCount, baseFilter.PageSize, baseFilter.Page) + { + } + + public Pagination(PaginationSettings paginationSettings, int itemsCount, BaseFilter baseFilter) + : this(paginationSettings, itemsCount, baseFilter.PageSize, baseFilter.Page) + { + } + + public Pagination(PaginationSettings paginationSettings, int itemsCount, int? pageSize, int? page) + { + _paginationSettings = paginationSettings; + + // The order of actions is important + TotalItems = GetHandledTotalItems(itemsCount); + PageSize = GetHandledPageSize(pageSize); + TotalPages = GetHandledTotalPages(); + Page = GetHandledPage(page); + + HasNext = Page != TotalPages; + HasPrevious = Page != 1; + + StartItem = TotalItems == 0 ? 0 : (PageSize * (Page - 1)) + 1; + EndItem = (PageSize * Page) > TotalItems ? TotalItems : PageSize * Page; + + Take = PageSize; + Skip = PageSize * (Page - 1); + } + + private int GetHandledTotalItems(int itemsCount) + { + return itemsCount < 0 ? 0 : itemsCount; + } + + private int GetHandledPageSize(int? pageSize) + { + if (!pageSize.HasValue || pageSize <= 0) return _paginationSettings.DefaultPageSize; + + if (pageSize > _paginationSettings.DefaultPageSizeLimit) return _paginationSettings.DefaultPageSizeLimit; + + return pageSize.Value; + } + + private int GetHandledTotalPages() + { + return TotalItems == 0 ? 1 : (int)Math.Ceiling((decimal)TotalItems / GetHandledPageSize(PageSize)); + } + + private int GetHandledPage(int? page) + { + if (!page.HasValue || page <= 0) return _paginationSettings.DefaultPage; + + if (page.Value > TotalPages) return TotalPages; + + return page.Value; + } +} diff --git a/sample/Ardalis.Sample.App2/PaginationSettings.cs b/sample/Ardalis.Sample.App2/PaginationSettings.cs new file mode 100644 index 00000000..7a772be2 --- /dev/null +++ b/sample/Ardalis.Sample.App2/PaginationSettings.cs @@ -0,0 +1,14 @@ +namespace Ardalis.Sample.App2; + +public class PaginationSettings +{ + public int DefaultPage { get; } = 1; + public int DefaultPageSize { get; } + public int DefaultPageSizeLimit { get; } + + public PaginationSettings(int defaultPageSize, int defaultPageSizeLimit) + { + DefaultPageSize = defaultPageSize; + DefaultPageSizeLimit = defaultPageSizeLimit; + } +} diff --git a/sample/Ardalis.Sample.App2/Program.cs b/sample/Ardalis.Sample.App2/Program.cs new file mode 100644 index 00000000..3956f3d4 --- /dev/null +++ b/sample/Ardalis.Sample.App2/Program.cs @@ -0,0 +1,139 @@ +using Ardalis.Sample.App2; +using Ardalis.Sample.Domain; +using Ardalis.Sample.Domain.Filters; +using Ardalis.Sample.Domain.Specs; +using AutoMapper; +using Microsoft.EntityFrameworkCore; + +var builder = WebApplication.CreateBuilder(args); + +var connectionString = builder.Configuration.GetConnectionString("DbConnection"); +builder.Services.AddDbContext(options => options.UseSqlServer(connectionString)); +builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>)); +builder.Services.AddScoped(typeof(IReadRepository<>), typeof(ReadRepository<>)); + +builder.Services.AddAutoMapper(typeof(MappingProfile).Assembly); +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); +var app = builder.Build(); +app.UseSwagger(); +app.UseSwaggerUI(); +app.UseHttpsRedirection(); + + +app.MapGet("/customers", async (IReadRepository repo, [AsParameters] CustomerFilter filter, CancellationToken cancellationToken) => +{ + var spec = new CustomerSpec(filter); + var result = await repo.ProjectToListAsync(spec, filter, cancellationToken); + return Results.Ok(result); +}); + +app.MapGet("/customers/{id}", async (IReadRepository repo, int id, CancellationToken cancellationToken) => +{ + var spec = new CustomerByIdSpec(id); + var result = await repo.ProjectToFirstOrDefaultAsync(spec, cancellationToken); + if (result is null) return Results.NotFound(); + return Results.Ok(result); +}); + +app.MapPost("/customers", async (IRepository repo, IMapper mapper, CustomerCreateDto customerCreateDto, CancellationToken cancellationToken) => +{ + var customer = new Customer + { + Name = customerCreateDto.Name, + Age = customerCreateDto.Age, + Addresses = customerCreateDto.Addresses.Select(a => new Address { Street = a.Street }).ToList() + }; + await repo.AddAsync(customer, cancellationToken); + var customerDto = mapper.Map(customer); + return Results.Ok(customerDto); +}); + +app.MapPut("/customers/{id}", async (IRepository repo, IMapper mapper, int id, CustomerUpdateDto customerUpdate, CancellationToken cancellationToken) => +{ + var spec = new CustomerByIdSpec(id); + var customer = await repo.FirstOrDefaultAsync(spec, cancellationToken); + if (customer is null) return Results.NotFound(); + customer.Name = customerUpdate.Name; + customer.Age = customerUpdate.Age; + await repo.UpdateAsync(customer, cancellationToken); + var customerDto = mapper.Map(customer); + return Results.Ok(customerDto); +}); + +await app.InitializeDbAsync(); +app.Run(); + +public record AddressDto(int Id, string Street, int CustomerId); +public record AddressCreateDto(string Street); +public record CustomerDto(int Id, string Name, int Age, List Addresses); +public record CustomerCreateDto(string Name, int Age, List Addresses); +public record CustomerUpdateDto(string Name, int Age); + +public class AppDbContext : DbContext +{ + public DbSet Customers => Set(); + public AppDbContext(DbContextOptions options) : base(options) + { + } +} + +public class Repository : RepositoryBase, IRepository where T : class, IAggregateRoot +{ + public Repository(AppDbContext dbContext, IMapper mapper) : base(dbContext, mapper) + { + } +} + +public class ReadRepository : RepositoryBase, IReadRepository where T : class +{ + public ReadRepository(AppDbContext dbContext, IMapper mapper) : base(dbContext, mapper) + { + } +} + +public class MappingProfile : Profile +{ + public MappingProfile() + { + CreateMap(); + CreateMap(); + } +} + +public static class HelperExtensions +{ + public static async Task InitializeDbAsync(this WebApplication app) + { + using var scope = app.Services.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); + await dbContext.Database.EnsureDeletedAsync(); + await dbContext.Database.EnsureCreatedAsync(); + var customers = new List() + { + new() + { + Name = "Customer1", + Age = 20, + Addresses = new() + { + new() { Street = "Street1_1" }, + new() { Street = "Street1_2" } + } + }, + new() + { + Name = "Customer2", + Age = 30, + Addresses = new() + { + new() { Street = "Street2_1" }, + new() { Street = "Street3_2" } + } + } + }; + dbContext.Customers.AddRange(customers); + await dbContext.SaveChangesAsync(); + } +} + diff --git a/sample/Ardalis.Sample.App2/QueryTagEvaluator.cs b/sample/Ardalis.Sample.App2/QueryTagEvaluator.cs new file mode 100644 index 00000000..d59d6557 --- /dev/null +++ b/sample/Ardalis.Sample.App2/QueryTagEvaluator.cs @@ -0,0 +1,22 @@ +using Ardalis.Specification; +using Microsoft.EntityFrameworkCore; + +namespace Ardalis.Sample.App2; + +public class QueryTagEvaluator : IEvaluator +{ + private QueryTagEvaluator() { } + public static QueryTagEvaluator Instance { get; } = new QueryTagEvaluator(); + + public bool IsCriteriaEvaluator { get; } = true; + + public IQueryable GetQuery(IQueryable query, ISpecification specification) where T : class + { + if (specification.Items.TryGetValue("TagWith", out var value) && value is string tag) + { + query = query.TagWith(tag); + } + + return query; + } +} diff --git a/sample/Ardalis.Sample.App2/RepositoryBase.cs b/sample/Ardalis.Sample.App2/RepositoryBase.cs new file mode 100644 index 00000000..a0ae3729 --- /dev/null +++ b/sample/Ardalis.Sample.App2/RepositoryBase.cs @@ -0,0 +1,156 @@ +using Ardalis.Sample.Domain.Filters; +using Ardalis.Specification; +using Ardalis.Specification.EntityFrameworkCore; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using Microsoft.EntityFrameworkCore; + +namespace Ardalis.Sample.App2; + +public abstract class RepositoryBase : IReadRepository where T : class +{ + private readonly DbContext _dbContext; + private readonly AutoMapper.IConfigurationProvider _configurationProvider; + protected ISpecificationEvaluator Evaluator { get; } + + protected RepositoryBase(DbContext dbContext, IMapper mapper) + : this(dbContext, AppSpecificationEvaluator.Instance, mapper) + { + } + + protected RepositoryBase(DbContext dbContext, ISpecificationEvaluator specificationEvaluator, IMapper mapper) + { + _dbContext = dbContext; + Evaluator = specificationEvaluator; + _configurationProvider = mapper.ConfigurationProvider; + } + + public virtual async Task AddAsync(T entity, CancellationToken cancellationToken = default) + { + _dbContext.Set().Add(entity); + + await SaveChangesAsync(cancellationToken); + + return entity; + } + public virtual async Task> AddRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default) + { + _dbContext.Set().AddRange(entities); + + await SaveChangesAsync(cancellationToken); + + return entities; + } + public virtual async Task UpdateAsync(T entity, CancellationToken cancellationToken = default) + { + //dbContext.Set().Update(entity); + + await SaveChangesAsync(cancellationToken); + } + public virtual async Task DeleteAsync(T entity, CancellationToken cancellationToken = default) + { + _dbContext.Set().Remove(entity); + + await SaveChangesAsync(cancellationToken); + } + public virtual async Task DeleteRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default) + { + _dbContext.Set().RemoveRange(entities); + + await SaveChangesAsync(cancellationToken); + } + public virtual async Task SaveChangesAsync(CancellationToken cancellationToken = default) + { + return await _dbContext.SaveChangesAsync(cancellationToken); + } + + public virtual async Task FindAsync(TId id, CancellationToken cancellationToken = default) where TId : notnull + { + return await _dbContext.Set().FindAsync(new object[] { id }, cancellationToken: cancellationToken); + } + public async Task FirstOrDefaultAsync(ISpecification specification, CancellationToken cancellationToken = default) + { + return await ApplySpecification(specification).FirstOrDefaultAsync(cancellationToken); + } + public async Task FirstOrDefaultAsync(ISpecification specification, CancellationToken cancellationToken = default) + { + return await ApplySpecification(specification).FirstOrDefaultAsync(cancellationToken); + } + public async Task SingleOrDefaultAsync(ISpecification specification, CancellationToken cancellationToken = default) + { + return await ApplySpecification(specification).SingleOrDefaultAsync(cancellationToken); + } + public async Task SingleOrDefaultAsync(ISpecification specification, CancellationToken cancellationToken = default) + { + return await ApplySpecification(specification).SingleOrDefaultAsync(cancellationToken); + } + public virtual async Task> ListAsync(CancellationToken cancellationToken = default) + { + return await _dbContext.Set().ToListAsync(cancellationToken); + } + public virtual async Task> ListAsync(ISpecification specification, CancellationToken cancellationToken = default) + { + var queryResult = await ApplySpecification(specification).ToListAsync(cancellationToken); + + return specification.PostProcessingAction == null ? queryResult : specification.PostProcessingAction(queryResult).ToList(); + } + public virtual async Task> ListAsync(ISpecification specification, CancellationToken cancellationToken = default) + { + var queryResult = await ApplySpecification(specification).ToListAsync(cancellationToken); + + return specification.PostProcessingAction == null ? queryResult : specification.PostProcessingAction(queryResult).ToList(); + } + public virtual async Task CountAsync(ISpecification specification, CancellationToken cancellationToken = default) + { + return await ApplySpecification(specification, true).CountAsync(cancellationToken); + } + public virtual async Task CountAsync(CancellationToken cancellationToken = default) + { + return await _dbContext.Set().CountAsync(cancellationToken); + } + public virtual async Task AnyAsync(ISpecification specification, CancellationToken cancellationToken = default) + { + return await ApplySpecification(specification, true).AnyAsync(cancellationToken); + } + public virtual async Task AnyAsync(CancellationToken cancellationToken = default) + { + return await _dbContext.Set().AnyAsync(cancellationToken); + } + + public async Task ProjectToFirstOrDefaultAsync(ISpecification specification, CancellationToken cancellationToken) + { + return await ApplySpecification(specification) + .ProjectTo(_configurationProvider) + .FirstOrDefaultAsync(cancellationToken); + } + + public async Task> ProjectToListAsync(ISpecification specification, CancellationToken cancellationToken) + { + return await ApplySpecification(specification) + .ProjectTo(_configurationProvider) + .ToListAsync(cancellationToken); + } + + public async Task> ProjectToListAsync(ISpecification specification, BaseFilter filter, CancellationToken cancellationToken) + { + var count = await ApplySpecification(specification).CountAsync(cancellationToken); + var pagination = new Pagination(count, filter); + + var data = await ApplySpecification(specification) + .Skip(pagination.Skip) + .Take(pagination.Take) + .ProjectTo(_configurationProvider) + .ToListAsync(cancellationToken); + + return new PagedResponse(data, pagination); + } + + protected virtual IQueryable ApplySpecification(ISpecification specification, bool evaluateCriteriaOnly = false) + { + return Evaluator.GetQuery(_dbContext.Set(), specification, evaluateCriteriaOnly); + } + protected virtual IQueryable ApplySpecification(ISpecification specification) + { + return Evaluator.GetQuery(_dbContext.Set(), specification); + } +} diff --git a/sample/Ardalis.Sample.App2/appsettings.Development.json b/sample/Ardalis.Sample.App2/appsettings.Development.json new file mode 100644 index 00000000..0c208ae9 --- /dev/null +++ b/sample/Ardalis.Sample.App2/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/sample/Ardalis.Sample.App2/appsettings.json b/sample/Ardalis.Sample.App2/appsettings.json new file mode 100644 index 00000000..93642730 --- /dev/null +++ b/sample/Ardalis.Sample.App2/appsettings.json @@ -0,0 +1,12 @@ +{ + "ConnectionStrings": { + "DbConnection": "Server=(localdb)\\mssqllocaldb;Database=SpecificationSampleApp1;Trusted_Connection=True;TrustServerCertificate=Yes" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/sample/Ardalis.Sample.Domain/Address.cs b/sample/Ardalis.Sample.Domain/Address.cs new file mode 100644 index 00000000..af003216 --- /dev/null +++ b/sample/Ardalis.Sample.Domain/Address.cs @@ -0,0 +1,9 @@ +namespace Ardalis.Sample.Domain; + +public class Address +{ + public int Id { get; set; } + public required string Street { get; set; } + + public int CustomerId { get; set; } +} diff --git a/sample/Ardalis.SampleApp.Core/Ardalis.SampleApp.Core.csproj b/sample/Ardalis.Sample.Domain/Ardalis.Sample.Domain.csproj similarity index 54% rename from sample/Ardalis.SampleApp.Core/Ardalis.SampleApp.Core.csproj rename to sample/Ardalis.Sample.Domain/Ardalis.Sample.Domain.csproj index ca9121ea..8cacdb6f 100644 --- a/sample/Ardalis.SampleApp.Core/Ardalis.SampleApp.Core.csproj +++ b/sample/Ardalis.Sample.Domain/Ardalis.Sample.Domain.csproj @@ -1,14 +1,11 @@ - net6.0 - 11.0 + net7.0 + enable + enable - - - - diff --git a/sample/Ardalis.Sample.Domain/Customer.cs b/sample/Ardalis.Sample.Domain/Customer.cs new file mode 100644 index 00000000..9c8e17b2 --- /dev/null +++ b/sample/Ardalis.Sample.Domain/Customer.cs @@ -0,0 +1,10 @@ +namespace Ardalis.Sample.Domain; + +public class Customer : IAggregateRoot +{ + public int Id { get; set; } + public required string Name { get; set; } + public required int Age { get; set; } + + public List
Addresses { get; set; } = new(); +} diff --git a/sample/Ardalis.Sample.Domain/Filters/BaseFilter.cs b/sample/Ardalis.Sample.Domain/Filters/BaseFilter.cs new file mode 100644 index 00000000..d04494db --- /dev/null +++ b/sample/Ardalis.Sample.Domain/Filters/BaseFilter.cs @@ -0,0 +1,9 @@ +namespace Ardalis.Sample.Domain.Filters; + +public record BaseFilter +{ + public int? Page { get; set; } + public int? PageSize { get; set; } + public string? SortBy { get; set; } + public string? OrderBy { get; set; } +} diff --git a/sample/Ardalis.Sample.Domain/Filters/CustomerFilter.cs b/sample/Ardalis.Sample.Domain/Filters/CustomerFilter.cs new file mode 100644 index 00000000..c3cb5605 --- /dev/null +++ b/sample/Ardalis.Sample.Domain/Filters/CustomerFilter.cs @@ -0,0 +1,8 @@ +namespace Ardalis.Sample.Domain.Filters; + +public record CustomerFilter : BaseFilter +{ + public int? AgeFrom { get; set; } + public int? AgeTo { get; set; } + public string? Name { get; set; } +} diff --git a/sample/Ardalis.Sample.Domain/IAggregateRoot.cs b/sample/Ardalis.Sample.Domain/IAggregateRoot.cs new file mode 100644 index 00000000..fb46af47 --- /dev/null +++ b/sample/Ardalis.Sample.Domain/IAggregateRoot.cs @@ -0,0 +1,5 @@ +namespace Ardalis.Sample.Domain; + +public interface IAggregateRoot +{ +} diff --git a/sample/Ardalis.Sample.Domain/Specs/CustomerByIdSpec.cs b/sample/Ardalis.Sample.Domain/Specs/CustomerByIdSpec.cs new file mode 100644 index 00000000..0b7f465c --- /dev/null +++ b/sample/Ardalis.Sample.Domain/Specs/CustomerByIdSpec.cs @@ -0,0 +1,12 @@ +using Ardalis.Specification; + +namespace Ardalis.Sample.Domain.Specs; + +public class CustomerByIdSpec : Specification +{ + public CustomerByIdSpec(int id) + { + Query.Where(x => x.Id == id) + .Include(x => x.Addresses); + } +} diff --git a/sample/Ardalis.Sample.Domain/Specs/CustomerSpec.cs b/sample/Ardalis.Sample.Domain/Specs/CustomerSpec.cs new file mode 100644 index 00000000..956d956b --- /dev/null +++ b/sample/Ardalis.Sample.Domain/Specs/CustomerSpec.cs @@ -0,0 +1,24 @@ +using Ardalis.Sample.Domain.Filters; +using Ardalis.Specification; + +namespace Ardalis.Sample.Domain.Specs; + +public class CustomerSpec : Specification +{ + public CustomerSpec() + { + Query.Include(c => c.Addresses) + .ApplyOrdering() + .TagWith("List all customers."); + } + + public CustomerSpec(CustomerFilter filter) + { + Query.Include(x => x.Addresses) + .Where(c => c.Age >= filter.AgeFrom, filter.AgeFrom is not null) + .Where(c => c.Age <= filter.AgeTo, filter.AgeTo is not null) + .Where(c => c.Name == filter.Name, filter.Name is not null) + .ApplyOrdering(filter) + .TagWith("List customers filtered by user inputs."); + } +} diff --git a/sample/Ardalis.Sample.Domain/Specs/CustomerSpecExtensions.cs b/sample/Ardalis.Sample.Domain/Specs/CustomerSpecExtensions.cs new file mode 100644 index 00000000..54540ee5 --- /dev/null +++ b/sample/Ardalis.Sample.Domain/Specs/CustomerSpecExtensions.cs @@ -0,0 +1,36 @@ +using Ardalis.Sample.Domain.Filters; +using Ardalis.Specification; + +namespace Ardalis.Sample.Domain.Specs; + +// Examples how to add extend specifications. +// These extensions are applied in Ardalis.Sample.Domain.Specs.CustomerSpec +public static class CustomerSpecExtensions +{ + // Let's assume we want to apply ordering for customers. + // Conveniently, we can create add an extension method, and use it in any customer spec. + public static ISpecificationBuilder ApplyOrdering(this ISpecificationBuilder builder, BaseFilter? filter = null) + { + // If there is no filter apply default ordering; + if (filter is null) return builder.OrderBy(x => x.Id); + + // We want the "asc" to be the default, that's why the condition is reverted. + var isAscending = !(filter.OrderBy?.Equals("desc", StringComparison.OrdinalIgnoreCase) ?? false); + + return filter.SortBy switch + { + nameof(Customer.Name) => isAscending ? builder.OrderBy(x => x.Name) : builder.OrderByDescending(x => x.Name), + nameof(Customer.Age) => isAscending ? builder.OrderBy(x => x.Age) : builder.OrderByDescending(x => x.Age), + _ => builder.OrderBy(x => x.Id) + }; + } + + // More complex scenario would be if we want to add a new feature. + // Let's say we want to add a capability for query tags. We can utilize the Items dictionary in the specification to store the tag. + // Once we have this in place, we would also need to add an evaluator in SpecificationEvaluator (check the example in Sample.App2) + public static ISpecificationBuilder TagWith(this ISpecificationBuilder builder, string tag) + { + builder.Specification.Items.TryAdd("TagWith", tag); + return builder; + } +} diff --git a/sample/Ardalis.SampleApp.Core/Entities/CustomerAggregate/Customer.cs b/sample/Ardalis.SampleApp.Core/Entities/CustomerAggregate/Customer.cs deleted file mode 100644 index 82f2ef97..00000000 --- a/sample/Ardalis.SampleApp.Core/Entities/CustomerAggregate/Customer.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Ardalis.GuardClauses; -using Ardalis.SampleApp.Core.Interfaces; -using System.Collections.Generic; -using System.Linq; - -namespace Ardalis.SampleApp.Core.Entities.CustomerAggregate; - -public class Customer : IAggregateRoot -{ - public int Id { get; private set; } - public string Name { get; private set; } - public string Email { get; private set; } - public string Address { get; private set; } - - public IEnumerable Stores => _stores.AsEnumerable(); - private readonly List _stores = new(); - - public Customer(string name, string email, string address) - { - Guard.Against.NullOrEmpty(name, nameof(name)); - Guard.Against.NullOrEmpty(email, nameof(email)); - - Name = name; - Email = email; - Address = address; - } - - public Store GetStore(int storeId) - { - var store = Stores.FirstOrDefault(x => x.Id == storeId); - - Guard.Against.Null(store, nameof(store)); - - return store; - } - - public Store AddStore(Store store) - { - Guard.Against.Null(store, nameof(store)); - - // Do some other operation while adding it. - - _stores.Add(store); - - return store; - } - - public void DeleteStore(int storeID) - { - var store = GetStore(storeID); - - _stores.Remove(store); - } -} diff --git a/sample/Ardalis.SampleApp.Core/Entities/CustomerAggregate/Store.cs b/sample/Ardalis.SampleApp.Core/Entities/CustomerAggregate/Store.cs deleted file mode 100644 index d23ce684..00000000 --- a/sample/Ardalis.SampleApp.Core/Entities/CustomerAggregate/Store.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Ardalis.GuardClauses; - -namespace Ardalis.SampleApp.Core.Entities.CustomerAggregate; - -public class Store -{ - public int Id { get; private set; } - public string Name { get; private set; } - public string Address { get; private set; } - - public int CustomerId { get; private set; } - - public Store(string name, string address) - { - Guard.Against.NullOrEmpty(name, nameof(name)); - - this.Name = name; - this.Address = address; - } -} diff --git a/sample/Ardalis.SampleApp.Core/Entities/Seeds/CustomerSeed.cs b/sample/Ardalis.SampleApp.Core/Entities/Seeds/CustomerSeed.cs deleted file mode 100644 index 52186909..00000000 --- a/sample/Ardalis.SampleApp.Core/Entities/Seeds/CustomerSeed.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Ardalis.SampleApp.Core.Entities.CustomerAggregate; -using System.Collections.Generic; - -namespace Ardalis.SampleApp.Core.Entities.Seeds; - -public static class CustomerSeed -{ - public static List Seed() - { - List output = new List(); - - for (int i = 1; i <= 100000; i++) - { - var customer = new Customer($"Customer{i}", $"Email{i}@local", $"Customer{i} address"); - customer.AddStore(new Store($"Store{i}-1", $"Store{i}-1 address")); - customer.AddStore(new Store($"Store{i}-2", $"Store{i}-2 address")); - - output.Add(customer); - } - - return output; - } -} diff --git a/sample/Ardalis.SampleApp.Core/Interfaces/IAggregateRoot.cs b/sample/Ardalis.SampleApp.Core/Interfaces/IAggregateRoot.cs deleted file mode 100644 index 13cf2d12..00000000 --- a/sample/Ardalis.SampleApp.Core/Interfaces/IAggregateRoot.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Ardalis.SampleApp.Core.Interfaces; - -public interface IAggregateRoot -{ -} diff --git a/sample/Ardalis.SampleApp.Core/Interfaces/ICustomerRepository.cs b/sample/Ardalis.SampleApp.Core/Interfaces/ICustomerRepository.cs deleted file mode 100644 index 41f00172..00000000 --- a/sample/Ardalis.SampleApp.Core/Interfaces/ICustomerRepository.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Ardalis.SampleApp.Core.Entities.CustomerAggregate; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Ardalis.SampleApp.Core.Interfaces; - -public interface ICustomerRepository -{ - // This is just to demonstrate that at anytime you can create custom repositories, and use to create some complex queries working directly with EF or your ORM. - Task> GetCustomers(string addressSearchTerm); -} diff --git a/sample/Ardalis.SampleApp.Core/Interfaces/IRepository.cs b/sample/Ardalis.SampleApp.Core/Interfaces/IRepository.cs deleted file mode 100644 index 1e6aa7ba..00000000 --- a/sample/Ardalis.SampleApp.Core/Interfaces/IRepository.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Ardalis.Specification; - -namespace Ardalis.SampleApp.Core.Interfaces; - -/// -public interface IRepository : IRepositoryBase where T : class, IAggregateRoot -{ -} - -/// -public interface IReadRepository : IReadRepositoryBase where T : class, IAggregateRoot -{ -} diff --git a/sample/Ardalis.SampleApp.Core/Specifications/CustomerByNameSpec.cs b/sample/Ardalis.SampleApp.Core/Specifications/CustomerByNameSpec.cs deleted file mode 100644 index f91178ae..00000000 --- a/sample/Ardalis.SampleApp.Core/Specifications/CustomerByNameSpec.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Ardalis.SampleApp.Core.Entities.CustomerAggregate; -using Ardalis.Specification; - -namespace Ardalis.SampleApp.Core.Specifications; - -/// -/// This specification expects customer names to be unique - change the base type if you want to support multiple results -/// -public class CustomerByNameSpec : SingleResultSpecification -{ - public CustomerByNameSpec(string name) - { - Query.Where(x => x.Name == name) - .OrderBy(x => x.Name) - .ThenByDescending(x => x.Address); - } -} diff --git a/sample/Ardalis.SampleApp.Core/Specifications/CustomerByNameWithStoresSpec.cs b/sample/Ardalis.SampleApp.Core/Specifications/CustomerByNameWithStoresSpec.cs deleted file mode 100644 index a6e6f80f..00000000 --- a/sample/Ardalis.SampleApp.Core/Specifications/CustomerByNameWithStoresSpec.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Ardalis.SampleApp.Core.Entities.CustomerAggregate; -using Ardalis.Specification; - -namespace Ardalis.SampleApp.Core.Specifications; - -/// -/// This specification expects customer names to be unique - change the base type if you want to support multiple results -/// -public class CustomerByNameWithStoresSpec : SingleResultSpecification -{ - public CustomerByNameWithStoresSpec(string name) - { - Query.Where(x => x.Name == name) - .Include(x => x.Stores) - .EnableCache(nameof(CustomerByNameWithStoresSpec), name); - } -} diff --git a/sample/Ardalis.SampleApp.Core/Specifications/CustomerSpec.cs b/sample/Ardalis.SampleApp.Core/Specifications/CustomerSpec.cs deleted file mode 100644 index 97964b1a..00000000 --- a/sample/Ardalis.SampleApp.Core/Specifications/CustomerSpec.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Ardalis.SampleApp.Core.Entities.CustomerAggregate; -using Ardalis.SampleApp.Core.Specifications.Filters; -using Ardalis.Specification; - -namespace Ardalis.SampleApp.Core.Specifications; - -/// -/// This specification expects 0 to many results -/// -public class CustomerSpec : Specification -{ - public CustomerSpec(CustomerFilter filter) - { - Query.OrderBy(x => x.Name) - .ThenByDescending(x => x.Address); - - if (filter.LoadChildren) - Query.Include(x => x.Stores); - - if (filter.IsPagingEnabled) - Query.Skip(PaginationHelper.CalculateSkip(filter)) - .Take(PaginationHelper.CalculateTake(filter)); - - if (!string.IsNullOrEmpty(filter.Name)) - Query.Where(x => x.Name == filter.Name); - - if (!string.IsNullOrEmpty(filter.Email)) - Query.Where(x => x.Email == filter.Email); - - if (!string.IsNullOrEmpty(filter.Address)) - Query.Search(x => x.Address, "%" + filter.Address + "%"); - } -} diff --git a/sample/Ardalis.SampleApp.Core/Specifications/Filters/BaseFilter.cs b/sample/Ardalis.SampleApp.Core/Specifications/Filters/BaseFilter.cs deleted file mode 100644 index c767a762..00000000 --- a/sample/Ardalis.SampleApp.Core/Specifications/Filters/BaseFilter.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ardalis.SampleApp.Core.Specifications.Filters; - -public class BaseFilter -{ - public bool LoadChildren { get; set; } - public bool IsPagingEnabled { get; set; } - - public int Page { get; set; } - public int PageSize { get; set; } -} diff --git a/sample/Ardalis.SampleApp.Core/Specifications/Filters/CustomerFilter.cs b/sample/Ardalis.SampleApp.Core/Specifications/Filters/CustomerFilter.cs deleted file mode 100644 index ae5b0d03..00000000 --- a/sample/Ardalis.SampleApp.Core/Specifications/Filters/CustomerFilter.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ardalis.SampleApp.Core.Specifications.Filters; - -public class CustomerFilter : BaseFilter -{ - public string Name { get; set; } - public string Email { get; set; } - public string Address { get; set; } -} diff --git a/sample/Ardalis.SampleApp.Core/Specifications/PaginationHelper.cs b/sample/Ardalis.SampleApp.Core/Specifications/PaginationHelper.cs deleted file mode 100644 index a8b81b92..00000000 --- a/sample/Ardalis.SampleApp.Core/Specifications/PaginationHelper.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Ardalis.SampleApp.Core.Specifications.Filters; - -namespace Ardalis.SampleApp.Core.Specifications; - -public static class PaginationHelper -{ - public static int DefaultPage => 1; - public static int DefaultPageSize => 10; - - public static int CalculateTake(int pageSize) - { - return pageSize <= 0 ? DefaultPageSize : pageSize; - } - public static int CalculateSkip(int pageSize, int page) - { - page = page <= 0 ? DefaultPage : page; - - return CalculateTake(pageSize) * (page - 1); - } - - public static int CalculateTake(BaseFilter baseFilter) - { - return CalculateTake(baseFilter.PageSize); - } - public static int CalculateSkip(BaseFilter baseFilter) - { - return CalculateSkip(baseFilter.PageSize, baseFilter.Page); - } -} diff --git a/sample/Ardalis.SampleApp.Infrastructure/Ardalis.SampleApp.Infrastructure.csproj b/sample/Ardalis.SampleApp.Infrastructure/Ardalis.SampleApp.Infrastructure.csproj deleted file mode 100644 index 7bf8883f..00000000 --- a/sample/Ardalis.SampleApp.Infrastructure/Ardalis.SampleApp.Infrastructure.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - net6.0 - 11.0 - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - diff --git a/sample/Ardalis.SampleApp.Infrastructure/Data/CachedCustomerRepository.cs b/sample/Ardalis.SampleApp.Infrastructure/Data/CachedCustomerRepository.cs deleted file mode 100644 index 2d2339a2..00000000 --- a/sample/Ardalis.SampleApp.Infrastructure/Data/CachedCustomerRepository.cs +++ /dev/null @@ -1,158 +0,0 @@ -using Ardalis.SampleApp.Core.Interfaces; -using Ardalis.Specification; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace Ardalis.SampleApp.Infrastructure.Data; - -/// -public class CachedRepository : IReadRepository where T : class, IAggregateRoot -{ - private readonly IMemoryCache _cache; - private readonly ILogger> _logger; - private readonly MyRepository _sourceRepository; - private readonly MemoryCacheEntryOptions _cacheOptions; - - public CachedRepository(IMemoryCache cache, - ILogger> logger, - MyRepository sourceRepository) - { - _cache = cache; - _logger = logger; - _sourceRepository = sourceRepository; - - _cacheOptions = new MemoryCacheEntryOptions() - .SetAbsoluteExpiration(relative: TimeSpan.FromSeconds(10)); - } - - /// - public virtual IAsyncEnumerable AsAsyncEnumerable(ISpecification specification) - { - return _sourceRepository.AsAsyncEnumerable(specification); - } - - /// - public Task AnyAsync(Specification.ISpecification specification, CancellationToken cancellationToken = default) - { - // TODO: Add Caching - return _sourceRepository.AnyAsync(specification, cancellationToken); - } - - /// - public Task AnyAsync(CancellationToken cancellationToken = default) - { - // TODO: Add Caching - return _sourceRepository.AnyAsync(cancellationToken); - } - - /// - public Task CountAsync(Specification.ISpecification specification, CancellationToken cancellationToken = default) - { - // TODO: Add Caching - return _sourceRepository.CountAsync(specification, cancellationToken); - } - - /// - public Task CountAsync(CancellationToken cancellationToken = default) - { - // TODO: Add Caching - return _sourceRepository.CountAsync(cancellationToken); - } - - /// - public Task GetByIdAsync(int id, CancellationToken cancellationToken = default) - { - return _sourceRepository.GetByIdAsync(id, cancellationToken); - } - - /// - public Task GetByIdAsync(TId id, CancellationToken cancellationToken = default) - { - return _sourceRepository.GetByIdAsync(id, cancellationToken); - } - - /// - public Task FirstOrDefaultAsync(ISpecification specification, CancellationToken cancellationToken = default) - { - if (specification.CacheEnabled) - { - var key = $"{specification.CacheKey}-GetBySpecAsync"; - _logger.LogInformation("Checking cache for {cache_key}", key); - return _cache.GetOrCreate(key, entry => - { - entry.SetOptions(_cacheOptions); - _logger.LogWarning("Fetching source data for {cache_key}", key); - return _sourceRepository.FirstOrDefaultAsync(specification, cancellationToken); - }); - } - return _sourceRepository.FirstOrDefaultAsync(specification, cancellationToken); - } - - /// - public Task GetBySpecAsync(ISpecification specification, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - /// - public virtual async Task FirstOrDefaultAsync(ISpecification specification, CancellationToken cancellationToken = default) - { - return await _sourceRepository.FirstOrDefaultAsync(specification, cancellationToken); - } - - /// - public virtual async Task SingleOrDefaultAsync(ISingleResultSpecification specification, CancellationToken cancellationToken = default) - { - return await _sourceRepository.SingleOrDefaultAsync(specification, cancellationToken); - } - - /// - public virtual async Task SingleOrDefaultAsync(ISingleResultSpecification specification, CancellationToken cancellationToken = default) - { - return await _sourceRepository.SingleOrDefaultAsync(specification, cancellationToken); - } - - /// - public Task> ListAsync(CancellationToken cancellationToken = default) - { - var key = $"{nameof(T)}-ListAsync"; - return _cache.GetOrCreate(key, entry => - { - entry.SetOptions(_cacheOptions); - return _sourceRepository.ListAsync(cancellationToken); - }); - } - - /// - public Task> ListAsync(ISpecification specification, CancellationToken cancellationToken = default) - { - if (specification.CacheEnabled) - { - var key = $"{specification.CacheKey}-ListAsync"; - _logger.LogInformation("Checking cache for {cache_key}", key); - return _cache.GetOrCreate(key, entry => - { - entry.SetOptions(_cacheOptions); - _logger.LogWarning("Fetching source data for {cache_key}", key); - return _sourceRepository.ListAsync(specification, cancellationToken); - }); - } - return _sourceRepository.ListAsync(specification, cancellationToken); - } - - /// - public Task> ListAsync(ISpecification specification, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } - - /// - public Task GetBySpecAsync(ISpecification specification, CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } -} diff --git a/sample/Ardalis.SampleApp.Infrastructure/Data/CustomerRepository.cs b/sample/Ardalis.SampleApp.Infrastructure/Data/CustomerRepository.cs deleted file mode 100644 index d604f10d..00000000 --- a/sample/Ardalis.SampleApp.Infrastructure/Data/CustomerRepository.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Ardalis.SampleApp.Core.Entities.CustomerAggregate; -using Ardalis.SampleApp.Core.Interfaces; -using Ardalis.SampleApp.Infrastructure.DataAccess; -using Microsoft.EntityFrameworkCore; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace Ardalis.SampleApp.Infrastructure.Data; - -// This is just to demonstrate that at anytime you can create custom repositories, and use to create some complex queries working directly with EF or your ORM. -public class CustomerRepository : ICustomerRepository -{ - private readonly SampleDbContext dbContext; - - public CustomerRepository(SampleDbContext dbContext) - { - this.dbContext = dbContext; - } - - public Task> GetCustomers(string addressSearchTerm) - { - return dbContext.Customers - .Take(10) - .Where(x => EF.Functions.Like(x.Address, "%" + addressSearchTerm + "%")) - .ToListAsync(); - } -} diff --git a/sample/Ardalis.SampleApp.Infrastructure/Data/MyRepository.cs b/sample/Ardalis.SampleApp.Infrastructure/Data/MyRepository.cs deleted file mode 100644 index f60f8279..00000000 --- a/sample/Ardalis.SampleApp.Infrastructure/Data/MyRepository.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Ardalis.SampleApp.Core.Interfaces; -using Ardalis.SampleApp.Infrastructure.DataAccess; -using Ardalis.Specification.EntityFrameworkCore; - -namespace Ardalis.SampleApp.Infrastructure.Data; - -/// -public class MyRepository : RepositoryBase, IRepository where T : class, IAggregateRoot -{ - private readonly SampleDbContext dbContext; - - public MyRepository(SampleDbContext dbContext) : base(dbContext) - { - this.dbContext = dbContext; - } - - // Not required to implement anything. Add additional functionalities if required. -} diff --git a/sample/Ardalis.SampleApp.Infrastructure/DataAccess/Configurations/CustomerConfiguration.cs b/sample/Ardalis.SampleApp.Infrastructure/DataAccess/Configurations/CustomerConfiguration.cs deleted file mode 100644 index 828fc518..00000000 --- a/sample/Ardalis.SampleApp.Infrastructure/DataAccess/Configurations/CustomerConfiguration.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Ardalis.SampleApp.Core.Entities.CustomerAggregate; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Ardalis.SampleApp.Infrastructure.DataAccess.Configurations; - -public class CustomerConfiguration : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Customer)); - - builder.Metadata.FindNavigation(nameof(Customer.Stores)) - .SetPropertyAccessMode(PropertyAccessMode.Field); - - builder.HasKey(x => x.Id); - } -} diff --git a/sample/Ardalis.SampleApp.Infrastructure/DataAccess/Configurations/StoreConfiguration.cs b/sample/Ardalis.SampleApp.Infrastructure/DataAccess/Configurations/StoreConfiguration.cs deleted file mode 100644 index 380782e8..00000000 --- a/sample/Ardalis.SampleApp.Infrastructure/DataAccess/Configurations/StoreConfiguration.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Ardalis.SampleApp.Core.Entities.CustomerAggregate; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace Ardalis.SampleApp.Infrastructure.DataAccess.Configurations; - -public class StoreConfiguration : IEntityTypeConfiguration -{ - public void Configure(EntityTypeBuilder builder) - { - builder.ToTable(nameof(Store)); - - builder.HasKey(x => x.Id); - } -} diff --git a/sample/Ardalis.SampleApp.Infrastructure/DataAccess/SampleDbContext.cs b/sample/Ardalis.SampleApp.Infrastructure/DataAccess/SampleDbContext.cs deleted file mode 100644 index c7d98f9a..00000000 --- a/sample/Ardalis.SampleApp.Infrastructure/DataAccess/SampleDbContext.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Ardalis.SampleApp.Core.Entities.CustomerAggregate; -using Ardalis.SampleApp.Infrastructure.DataAccess.Configurations; -using Microsoft.EntityFrameworkCore; - -namespace Ardalis.SampleApp.Infrastructure.DataAccess; - -public class SampleDbContext : DbContext -{ - public DbSet Customers { get; set; } - - public SampleDbContext(DbContextOptions options) - : base(options) - { - } - - protected override void OnModelCreating(ModelBuilder builder) - { - base.OnModelCreating(builder); - - builder.ApplyConfiguration(new CustomerConfiguration()); - builder.ApplyConfiguration(new StoreConfiguration()); - } -} diff --git a/sample/Ardalis.SampleApp.Infrastructure/DataAccess/SampleDbContextSeed.cs b/sample/Ardalis.SampleApp.Infrastructure/DataAccess/SampleDbContextSeed.cs deleted file mode 100644 index 5ecbfd68..00000000 --- a/sample/Ardalis.SampleApp.Infrastructure/DataAccess/SampleDbContextSeed.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Ardalis.SampleApp.Core.Entities.Seeds; -using Microsoft.EntityFrameworkCore; -using System; -using System.Threading.Tasks; - -namespace Ardalis.SampleApp.Infrastructure.DataAccess; - -public class SampleDbContextSeed -{ - private readonly SampleDbContext dbContext; - - public SampleDbContextSeed(SampleDbContext dbContext) - { - this.dbContext = dbContext; - } - - public async Task SeedAsync(int retry = 0) - { - try - { - dbContext.Database.Migrate(); - - if (await dbContext.Customers.CountAsync() == 0) - { - foreach (var customer in CustomerSeed.Seed()) - { - dbContext.Customers.Add(customer); - } - } - - await dbContext.SaveChangesAsync(); - - } - catch (Exception) - { - if (retry > 0) - { - await SeedAsync(retry - 1); - } - } - } -} diff --git a/sample/Ardalis.SampleApp.Infrastructure/Migrations/20201102224526_SampleDB-v1.Designer.cs b/sample/Ardalis.SampleApp.Infrastructure/Migrations/20201102224526_SampleDB-v1.Designer.cs deleted file mode 100644 index bba057e4..00000000 --- a/sample/Ardalis.SampleApp.Infrastructure/Migrations/20201102224526_SampleDB-v1.Designer.cs +++ /dev/null @@ -1,78 +0,0 @@ -// -using Ardalis.SampleApp.Infrastructure.DataAccess; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -namespace Ardalis.SampleApp.Infrastructure.Migrations -{ - [DbContext(typeof(SampleDbContext))] - [Migration("20201102224526_SampleDB-v1")] - partial class SampleDBv1 - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "3.1.9") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("Ardalis.SampleApp.Core.Entitites.CustomerAggregate.Customer", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("Address") - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Customer"); - }); - - modelBuilder.Entity("Ardalis.SampleApp.Core.Entitites.CustomerAggregate.Store", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("Address") - .HasColumnType("nvarchar(max)"); - - b.Property("CustomerId") - .HasColumnType("int"); - - b.Property("Name") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("CustomerId"); - - b.ToTable("Store"); - }); - - modelBuilder.Entity("Ardalis.SampleApp.Core.Entitites.CustomerAggregate.Store", b => - { - b.HasOne("Ardalis.SampleApp.Core.Entitites.CustomerAggregate.Customer", null) - .WithMany("Stores") - .HasForeignKey("CustomerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/sample/Ardalis.SampleApp.Infrastructure/Migrations/20201102224526_SampleDB-v1.cs b/sample/Ardalis.SampleApp.Infrastructure/Migrations/20201102224526_SampleDB-v1.cs deleted file mode 100644 index 637c74bd..00000000 --- a/sample/Ardalis.SampleApp.Infrastructure/Migrations/20201102224526_SampleDB-v1.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -namespace Ardalis.SampleApp.Infrastructure.Migrations; - -public partial class SampleDBv1 : Migration -{ - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Customer", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Name = table.Column(nullable: true), - Email = table.Column(nullable: true), - Address = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Customer", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Store", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("SqlServer:Identity", "1, 1"), - Name = table.Column(nullable: true), - Address = table.Column(nullable: true), - CustomerId = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Store", x => x.Id); - table.ForeignKey( - name: "FK_Store_Customer_CustomerId", - column: x => x.CustomerId, - principalTable: "Customer", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateIndex( - name: "IX_Store_CustomerId", - table: "Store", - column: "CustomerId"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Store"); - - migrationBuilder.DropTable( - name: "Customer"); - } -} diff --git a/sample/Ardalis.SampleApp.Infrastructure/Migrations/SampleDbContextModelSnapshot.cs b/sample/Ardalis.SampleApp.Infrastructure/Migrations/SampleDbContextModelSnapshot.cs deleted file mode 100644 index db689c6e..00000000 --- a/sample/Ardalis.SampleApp.Infrastructure/Migrations/SampleDbContextModelSnapshot.cs +++ /dev/null @@ -1,76 +0,0 @@ -// -using Ardalis.SampleApp.Infrastructure.DataAccess; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -namespace Ardalis.SampleApp.Infrastructure.Migrations -{ - [DbContext(typeof(SampleDbContext))] - partial class SampleDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "3.1.9") - .HasAnnotation("Relational:MaxIdentifierLength", 128) - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - modelBuilder.Entity("Ardalis.SampleApp.Core.Entitites.CustomerAggregate.Customer", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("Address") - .HasColumnType("nvarchar(max)"); - - b.Property("Email") - .HasColumnType("nvarchar(max)"); - - b.Property("Name") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.ToTable("Customer"); - }); - - modelBuilder.Entity("Ardalis.SampleApp.Core.Entitites.CustomerAggregate.Store", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("int") - .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); - - b.Property("Address") - .HasColumnType("nvarchar(max)"); - - b.Property("CustomerId") - .HasColumnType("int"); - - b.Property("Name") - .HasColumnType("nvarchar(max)"); - - b.HasKey("Id"); - - b.HasIndex("CustomerId"); - - b.ToTable("Store"); - }); - - modelBuilder.Entity("Ardalis.SampleApp.Core.Entitites.CustomerAggregate.Store", b => - { - b.HasOne("Ardalis.SampleApp.Core.Entitites.CustomerAggregate.Customer", null) - .WithMany("Stores") - .HasForeignKey("CustomerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/sample/Ardalis.SampleApp.Web/Ardalis.SampleApp.Web.csproj b/sample/Ardalis.SampleApp.Web/Ardalis.SampleApp.Web.csproj deleted file mode 100644 index ecd94194..00000000 --- a/sample/Ardalis.SampleApp.Web/Ardalis.SampleApp.Web.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - - net6.0 - 11.0 - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - - - 1701;1702;1591;1573;0612 - - - diff --git a/sample/Ardalis.SampleApp.Web/AutomapperMaps.cs b/sample/Ardalis.SampleApp.Web/AutomapperMaps.cs deleted file mode 100644 index a1fdc69f..00000000 --- a/sample/Ardalis.SampleApp.Web/AutomapperMaps.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Ardalis.SampleApp.Core.Entities.CustomerAggregate; -using Ardalis.SampleApp.Core.Specifications.Filters; -using Ardalis.SampleApp.Web.Models; -using AutoMapper; - - -namespace Ardalis.SampleApp.Web; - -public class AutomapperMaps : Profile -{ - public AutomapperMaps() - { - CreateMap().IncludeAllDerived().ReverseMap(); - CreateMap().ReverseMap(); - - CreateMap(); - CreateMap(); - } -} diff --git a/sample/Ardalis.SampleApp.Web/Controllers/CustomersController.cs b/sample/Ardalis.SampleApp.Web/Controllers/CustomersController.cs deleted file mode 100644 index 47162c72..00000000 --- a/sample/Ardalis.SampleApp.Web/Controllers/CustomersController.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Ardalis.SampleApp.Web.Interfaces; -using Ardalis.SampleApp.Web.Models; -using Microsoft.AspNetCore.Mvc; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Ardalis.SampleApp.Web.Controllers; - -[ApiController] -[Route("[controller]")] -public class CustomersController : ControllerBase -{ - private readonly ICustomerUiService customerUiService; - - public CustomersController(ICustomerUiService customerUiService) - { - this.customerUiService = customerUiService; - } - - [HttpGet("{Id}")] - public Task Get(int Id) - { - return customerUiService.GetCustomer(Id); - } - - [HttpGet("{name}")] - public Task Get(string name) - { - return customerUiService.GetCustomer(name); - } - - [HttpGet] - public Task> Get([FromQuery] CustomerFilterDto filter) - { - filter = filter ?? new CustomerFilterDto(); - - // Here you can decide if you want the collections as well - - filter.LoadChildren = true; - filter.IsPagingEnabled = true; - - return customerUiService.GetCustomers(filter); - } -} diff --git a/sample/Ardalis.SampleApp.Web/Interfaces/ICustomerUiService.cs b/sample/Ardalis.SampleApp.Web/Interfaces/ICustomerUiService.cs deleted file mode 100644 index 52727ec4..00000000 --- a/sample/Ardalis.SampleApp.Web/Interfaces/ICustomerUiService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Ardalis.SampleApp.Web.Models; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Ardalis.SampleApp.Web.Interfaces; - -public interface ICustomerUiService -{ - Task GetCustomer(int customerId); - Task GetCustomer(string customerName); - Task GetCustomerWithStores(string customerName); - - Task> GetCustomers(CustomerFilterDto filterDto); -} diff --git a/sample/Ardalis.SampleApp.Web/Models/BaseFilterDto.cs b/sample/Ardalis.SampleApp.Web/Models/BaseFilterDto.cs deleted file mode 100644 index c538991c..00000000 --- a/sample/Ardalis.SampleApp.Web/Models/BaseFilterDto.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.ComponentModel; -using System.Text.Json.Serialization; - -namespace Ardalis.SampleApp.Web.Models; - -public class BaseFilterDto -{ - [Browsable(false)] - [JsonIgnore] - public bool LoadChildren { get; set; } = false; - - [Browsable(false)] - [JsonIgnore] - public bool IsPagingEnabled { get; set; } = true; - - public int Page { get; set; } = 1; - public int PageSize { get; set; } = 10; -} diff --git a/sample/Ardalis.SampleApp.Web/Models/CustomerDto.cs b/sample/Ardalis.SampleApp.Web/Models/CustomerDto.cs deleted file mode 100644 index 456de349..00000000 --- a/sample/Ardalis.SampleApp.Web/Models/CustomerDto.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; - -namespace Ardalis.SampleApp.Web.Models; - -public class CustomerDto -{ - public int Id { get; set; } - public string Name { get; set; } - public string Email { get; set; } - public string Address { get; set; } - - public List Stores { get; set; } = new List(); -} diff --git a/sample/Ardalis.SampleApp.Web/Models/CustomerFilterDto.cs b/sample/Ardalis.SampleApp.Web/Models/CustomerFilterDto.cs deleted file mode 100644 index 2dba8243..00000000 --- a/sample/Ardalis.SampleApp.Web/Models/CustomerFilterDto.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ardalis.SampleApp.Web.Models; - -public class CustomerFilterDto : BaseFilterDto -{ - public string Name { get; set; } - public string Email { get; set; } - public string Address { get; set; } -} diff --git a/sample/Ardalis.SampleApp.Web/Models/StoreDto.cs b/sample/Ardalis.SampleApp.Web/Models/StoreDto.cs deleted file mode 100644 index f790a60f..00000000 --- a/sample/Ardalis.SampleApp.Web/Models/StoreDto.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ardalis.SampleApp.Web.Models; - -public class StoreDto -{ - public int Id { get; set; } - public string Name { get; set; } - public string Address { get; set; } -} diff --git a/sample/Ardalis.SampleApp.Web/Pages/Index.cshtml b/sample/Ardalis.SampleApp.Web/Pages/Index.cshtml deleted file mode 100644 index 4577f1e1..00000000 --- a/sample/Ardalis.SampleApp.Web/Pages/Index.cshtml +++ /dev/null @@ -1,24 +0,0 @@ -@page -@model Ardalis.SampleApp.Web.Pages.IndexModel -@{ -} - -

Customer List

- -Loading customers took @Model.ElapsedTimeMilliseconds ms. - - - - - - - - @foreach(var customer in Model.Customers.Take(100)) - { - - - - - } - -
IdName
@customer.Id@customer.Name
\ No newline at end of file diff --git a/sample/Ardalis.SampleApp.Web/Pages/Index.cshtml.cs b/sample/Ardalis.SampleApp.Web/Pages/Index.cshtml.cs deleted file mode 100644 index 1c0d68cd..00000000 --- a/sample/Ardalis.SampleApp.Web/Pages/Index.cshtml.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Ardalis.SampleApp.Core.Entities.CustomerAggregate; -using Ardalis.SampleApp.Core.Interfaces; -using Ardalis.SampleApp.Core.Specifications; -using Microsoft.AspNetCore.Mvc.RazorPages; -using Microsoft.Extensions.Logging; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading.Tasks; - -namespace Ardalis.SampleApp.Web.Pages; - -public class IndexModel : PageModel -{ - private readonly IReadRepository _customerRepository; - private readonly ILogger _logger; - - public IndexModel(IReadRepository customerRepository, - ILogger logger) - { - _customerRepository = customerRepository; - _logger = logger; - } - - public List Customers { get; set; } - public long ElapsedTimeMilliseconds { get; set; } - - public async Task OnGet() - { - var timer = Stopwatch.StartNew(); - var spec = new CustomerByNameWithStoresSpec(name: "Customer66"); - Customers = await _customerRepository.ListAsync(spec); - timer.Stop(); - ElapsedTimeMilliseconds = timer.ElapsedMilliseconds; - } -} diff --git a/sample/Ardalis.SampleApp.Web/Program.cs b/sample/Ardalis.SampleApp.Web/Program.cs deleted file mode 100644 index cafa2469..00000000 --- a/sample/Ardalis.SampleApp.Web/Program.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Ardalis.SampleApp.Infrastructure.DataAccess; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using System; -using System.Threading.Tasks; - -namespace Ardalis.SampleApp.Web; - -public class Program -{ - public static async Task Main(string[] args) - { - var host = CreateHostBuilder(args).Build(); - - using (var scope = host.Services.CreateScope()) - { - var services = scope.ServiceProvider; - try - { - var dbContext = services.GetRequiredService(); - - await new SampleDbContextSeed(dbContext).SeedAsync(); - } - catch (Exception) - { - } - } - - host.Run(); - } - - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.ConfigureLogging(config => - config.AddConsole()); - webBuilder.UseStartup(); - }); -} diff --git a/sample/Ardalis.SampleApp.Web/Services/CustomerUiService.cs b/sample/Ardalis.SampleApp.Web/Services/CustomerUiService.cs deleted file mode 100644 index 1fe98986..00000000 --- a/sample/Ardalis.SampleApp.Web/Services/CustomerUiService.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Ardalis.GuardClauses; -using Ardalis.SampleApp.Core.Entities.CustomerAggregate; -using Ardalis.SampleApp.Core.Interfaces; -using Ardalis.SampleApp.Core.Specifications; -using Ardalis.SampleApp.Core.Specifications.Filters; -using Ardalis.SampleApp.Web.Interfaces; -using Ardalis.SampleApp.Web.Models; -using AutoMapper; -using System.Collections.Generic; -using System.Threading.Tasks; - -namespace Ardalis.SampleApp.Web.Services; - -public class CustomerUiService : ICustomerUiService -{ - private readonly IMapper _mapper; - private readonly IReadRepository _customerRepository; - - public CustomerUiService(IMapper mapper, - IReadRepository customerRepository) - { - _mapper = mapper; - _customerRepository = customerRepository; - } - - - // Here I'm just writing various usages, not necessarily you'll need all of them. - - public async Task GetCustomer(int customerId) - { - var customer = await _customerRepository.GetByIdAsync(customerId); - - Guard.Against.Null(customer, nameof(customer)); - - return _mapper.Map(customer); - } - - public async Task GetCustomer(string customerName) - { - var customer = await _customerRepository.GetBySpecAsync(new CustomerByNameSpec(customerName)); - - Guard.Against.Null(customer, nameof(customer)); - - return _mapper.Map(customer); - } - - public async Task GetCustomerWithStores(string customerName) - { - var customer = await _customerRepository.GetBySpecAsync(new CustomerByNameWithStoresSpec(customerName)); - - Guard.Against.Null(customer, nameof(customer)); - - return _mapper.Map(customer); - } - - public async Task> GetCustomers(CustomerFilterDto filterDto) - { - var spec = new CustomerSpec(_mapper.Map(filterDto)); - var customers = await _customerRepository.ListAsync(spec); - - return _mapper.Map>(customers); - } -} diff --git a/sample/Ardalis.SampleApp.Web/Startup.cs b/sample/Ardalis.SampleApp.Web/Startup.cs deleted file mode 100644 index 70b70f79..00000000 --- a/sample/Ardalis.SampleApp.Web/Startup.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Ardalis.SampleApp.Core.Interfaces; -using Ardalis.SampleApp.Infrastructure.Data; -using Ardalis.SampleApp.Infrastructure.DataAccess; -using Ardalis.SampleApp.Web.Interfaces; -using Ardalis.SampleApp.Web.Services; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Ardalis.SampleApp.Web; - -public class Startup -{ - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("MyDbConnection"))); - - services.AddAutoMapper(typeof(AutomapperMaps)); - - services.AddScoped(typeof(IReadRepository<>), typeof(CachedRepository<>)); - services.AddScoped(typeof(MyRepository<>)); - services.AddScoped(); - - // services.AddControllers(); - services.AddMvc(); - services.AddLogging(); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseHttpsRedirection(); - - app.UseRouting(); - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - endpoints.MapRazorPages(); - }); - } -} diff --git a/sample/Ardalis.SampleApp.Web/appsettings.Development.json b/sample/Ardalis.SampleApp.Web/appsettings.Development.json deleted file mode 100644 index 6adcf5d9..00000000 --- a/sample/Ardalis.SampleApp.Web/appsettings.Development.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "ConnectionStrings": { - "MyDbConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=SSPI;Initial Catalog=SpecSampleApp;" - }, - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - } -} diff --git a/sample/Ardalis.SampleApp.Web/appsettings.json b/sample/Ardalis.SampleApp.Web/appsettings.json deleted file mode 100644 index ddea991b..00000000 --- a/sample/Ardalis.SampleApp.Web/appsettings.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "ConnectionStrings": { - "MyDbConnection": "Server=(localdb)\\mssqllocaldb;Integrated Security=SSPI;Initial Catalog=SpecSampleApp;" - }, - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "AllowedHosts": "*" -}