Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sample applications. #347

Merged
merged 1 commit into from
Jul 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions Ardalis.Specification.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down Expand Up @@ -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
Expand All @@ -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}
Expand Down
22 changes: 22 additions & 0 deletions sample/Ardalis.Sample.App1/Ardalis.Sample.App1.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.9" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\Specification.EntityFrameworkCore\src\Ardalis.Specification.EntityFrameworkCore\Ardalis.Specification.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\Ardalis.Sample.Domain\Ardalis.Sample.Domain.csproj" />
</ItemGroup>

</Project>
145 changes: 145 additions & 0 deletions sample/Ardalis.Sample.App1/Program.cs
Original file line number Diff line number Diff line change
@@ -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<AppDbContext>(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<Customer> repo, IMapper mapper, CancellationToken cancellationToken) =>
{
var spec = new CustomerSpec();
var customers = await repo.ListAsync(spec, cancellationToken);
var customersDto = mapper.Map<List<CustomerDto>>(customers);
return Results.Ok(customersDto);
});

app.MapGet("/customers/{id}", async (IReadRepository<Customer> 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<CustomerDto>(customer);
return Results.Ok(customerDto);
});

app.MapPost("/customers", async (IRepository<Customer> 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<CustomerDto>(customer);
return Results.Ok(customerDto);
});

app.MapPut("/customers/{id}", async (IRepository<Customer> 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<CustomerDto>(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<AddressDto> Addresses);
public record CustomerCreateDto(string Name, int Age, List<AddressCreateDto> Addresses);
public record CustomerUpdateDto(string Name, int Age);

public class AppDbContext : DbContext
{
public DbSet<Customer> Customers => Set<Customer>();
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
}

public interface IRepository<T> : IRepositoryBase<T> where T : class, IAggregateRoot
{
}
public interface IReadRepository<T> : IReadRepositoryBase<T> where T : class
{
}
public class Repository<T> : RepositoryBase<T>, IRepository<T> where T : class, IAggregateRoot
{
public Repository(AppDbContext dbContext) : base(dbContext)
{
}
}
public class ReadRepository<T> : RepositoryBase<T>, IReadRepository<T> where T : class
{
public ReadRepository(AppDbContext dbContext) : base(dbContext)
{
}
}

public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Address, AddressDto>();
CreateMap<Customer, CustomerDto>();
}
}

public static class WebApplicationExtensions
{
public static async Task InitializeDbAsync(this WebApplication app)
{
using var scope = app.Services.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await dbContext.Database.EnsureDeletedAsync();
await dbContext.Database.EnsureCreatedAsync();
var customers = new List<Customer>()
{
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();
}
}
8 changes: 8 additions & 0 deletions sample/Ardalis.Sample.App1/appsettings.Development.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
12 changes: 12 additions & 0 deletions sample/Ardalis.Sample.App1/appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"ConnectionStrings": {
"DbConnection": "Server=(localdb)\\mssqllocaldb;Database=SpecificationSampleApp1;Trusted_Connection=True;TrustServerCertificate=Yes"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
13 changes: 13 additions & 0 deletions sample/Ardalis.Sample.App2/AppSpecificationEvaluator.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
22 changes: 22 additions & 0 deletions sample/Ardalis.Sample.App2/Ardalis.Sample.App2.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="AutoMapper" Version="12.0.1" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.9" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\Specification.EntityFrameworkCore\src\Ardalis.Specification.EntityFrameworkCore\Ardalis.Specification.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\Ardalis.Sample.Domain\Ardalis.Sample.Domain.csproj" />
</ItemGroup>

</Project>
24 changes: 24 additions & 0 deletions sample/Ardalis.Sample.App2/IReadRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Ardalis.Sample.Domain.Filters;
using Ardalis.Specification;

namespace Ardalis.Sample.App2;

public interface IReadRepository<T> where T : class
{
Task<T?> FindAsync<TId>(TId id, CancellationToken cancellationToken = default) where TId : notnull;
Task<T?> FirstOrDefaultAsync(ISpecification<T> specification, CancellationToken cancellationToken = default);
Task<TResult?> FirstOrDefaultAsync<TResult>(ISpecification<T, TResult> specification, CancellationToken cancellationToken = default);
Task<T?> SingleOrDefaultAsync(ISpecification<T> specification, CancellationToken cancellationToken = default);
Task<TResult?> SingleOrDefaultAsync<TResult>(ISpecification<T, TResult> specification, CancellationToken cancellationToken = default);
Task<List<T>> ListAsync(CancellationToken cancellationToken = default);
Task<List<T>> ListAsync(ISpecification<T> specification, CancellationToken cancellationToken = default);
Task<List<TResult>> ListAsync<TResult>(ISpecification<T, TResult> specification, CancellationToken cancellationToken = default);
Task<int> CountAsync(CancellationToken cancellationToken = default);
Task<int> CountAsync(ISpecification<T> specification, CancellationToken cancellationToken = default);
Task<bool> AnyAsync(CancellationToken cancellationToken = default);
Task<bool> AnyAsync(ISpecification<T> specification, CancellationToken cancellationToken = default);

Task<TResult?> ProjectToFirstOrDefaultAsync<TResult>(ISpecification<T> specification, CancellationToken cancellationToken);
Task<List<TResult>> ProjectToListAsync<TResult>(ISpecification<T> specification, CancellationToken cancellationToken);
Task<PagedResponse<TResult>> ProjectToListAsync<TResult>(ISpecification<T> specification, BaseFilter filter, CancellationToken cancellationToken);
}
27 changes: 27 additions & 0 deletions sample/Ardalis.Sample.App2/IRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Ardalis.Sample.Domain;
using Ardalis.Specification;

namespace Ardalis.Sample.App2;

public interface IRepository<T> where T : class, IAggregateRoot
{
Task<T> AddAsync(T entity, CancellationToken cancellationToken = default);
Task<IEnumerable<T>> AddRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default);
Task UpdateAsync(T entity, CancellationToken cancellationToken = default);
Task DeleteAsync(T entity, CancellationToken cancellationToken = default);
Task DeleteRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default);
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);

Task<T?> FindAsync<TId>(TId id, CancellationToken cancellationToken = default) where TId : notnull;
Task<T?> FirstOrDefaultAsync(ISpecification<T> specification, CancellationToken cancellationToken = default);
Task<TResult?> FirstOrDefaultAsync<TResult>(ISpecification<T, TResult> specification, CancellationToken cancellationToken = default);
Task<T?> SingleOrDefaultAsync(ISpecification<T> specification, CancellationToken cancellationToken = default);
Task<TResult?> SingleOrDefaultAsync<TResult>(ISpecification<T, TResult> specification, CancellationToken cancellationToken = default);
Task<List<T>> ListAsync(CancellationToken cancellationToken = default);
Task<List<T>> ListAsync(ISpecification<T> specification, CancellationToken cancellationToken = default);
Task<List<TResult>> ListAsync<TResult>(ISpecification<T, TResult> specification, CancellationToken cancellationToken = default);
Task<int> CountAsync(CancellationToken cancellationToken = default);
Task<int> CountAsync(ISpecification<T> specification, CancellationToken cancellationToken = default);
Task<bool> AnyAsync(CancellationToken cancellationToken = default);
Task<bool> AnyAsync(ISpecification<T> specification, CancellationToken cancellationToken = default);
}
14 changes: 14 additions & 0 deletions sample/Ardalis.Sample.App2/PagedResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Ardalis.Sample.App2;

public class PagedResponse<T>
{
public Pagination Pagination { get; }
public List<T> Data { get; }

public PagedResponse(List<T> data, Pagination pagination)
{
Data = data;
Pagination = pagination;
}
}

Loading