diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Contracts/ReserveTicketsRequest.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Contracts/ReserveTicketsRequest.cs
deleted file mode 100644
index 43839a1b5..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Contracts/ReserveTicketsRequest.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-using System.ComponentModel.DataAnnotations;
-
-namespace EventTicketing.Api.Contracts;
-
-public sealed record ReserveTicketsRequest([property: Range(1, 20)] int Quantity);
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Endpoints/EventEndpoints.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/EventEndpoints.cs
similarity index 78%
rename from csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Endpoints/EventEndpoints.cs
rename to csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/EventEndpoints.cs
index 0f649dc29..a345a61d2 100644
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Endpoints/EventEndpoints.cs
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/EventEndpoints.cs
@@ -1,12 +1,13 @@
-using EventTicketing.Api.Contracts;
-using EventTicketing.Api.Extensions;
-using EventTicketing.Application.Events;
+using System.ComponentModel.DataAnnotations;
+using EventTicketing.Application;
-namespace EventTicketing.Api.Endpoints;
+namespace EventTicketing.Api;
+
+public sealed record ReserveTicketsRequest([property: Range(1, 20)] int Quantity);
public static class EventEndpoints
{
- public static IEndpointRouteBuilder MapEventEndpoints(this IEndpointRouteBuilder app)
+ public static void MapEventEndpoints(this IEndpointRouteBuilder app)
{
var events = app.MapGroup("/api/events");
@@ -31,7 +32,5 @@ public static IEndpointRouteBuilder MapEventEndpoints(this IEndpointRouteBuilder
return result.IsSuccess ? Results.Ok(result.Value) : result.ToProblem();
});
-
- return app;
}
}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/EventTicketing.Api.csproj b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/EventTicketing.Api.csproj
index 08002b12d..3f3c38cb2 100644
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/EventTicketing.Api.csproj
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/EventTicketing.Api.csproj
@@ -5,10 +5,6 @@
-
-
-
-
net10.0
enable
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/EventTicketing.Api.http b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/EventTicketing.Api.http
new file mode 100644
index 000000000..2801b7444
--- /dev/null
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/EventTicketing.Api.http
@@ -0,0 +1,26 @@
+GET http://localhost:5000/api/events/1
+
+###
+
+POST http://localhost:5000/api/events/1/reservations
+Content-Type: application/json
+
+{ "quantity": 3 }
+
+###
+
+POST http://localhost:5000/api/events/2/reservations
+Content-Type: application/json
+
+{ "quantity": 3 }
+
+###
+
+POST http://localhost:5000/api/events/1/reservations
+Content-Type: application/json
+
+{ "quantity": 0 }
+
+###
+
+GET http://localhost:5000/api/events/99
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Extensions/ResultExtensions.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Extensions/ResultExtensions.cs
deleted file mode 100644
index 8e65ecdab..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Extensions/ResultExtensions.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using EventTicketing.Domain.Common;
-
-namespace EventTicketing.Api.Extensions;
-
-public static class ResultExtensions
-{
- public static IResult ToProblem(this Result result)
- {
- if (result.IsSuccess)
- throw new InvalidOperationException("A successful result is not a problem.");
-
- var statusCode = result.Error.Type switch
- {
- ErrorType.Validation => StatusCodes.Status400BadRequest,
- ErrorType.NotFound => StatusCodes.Status404NotFound,
- ErrorType.Conflict => StatusCodes.Status409Conflict,
- _ => StatusCodes.Status500InternalServerError
- };
-
- return TypedResults.Problem(
- statusCode: statusCode,
- title: result.Error.Code,
- detail: result.Error.Description);
- }
-}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Program.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Program.cs
index fcd329890..4befcf897 100644
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Program.cs
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Program.cs
@@ -1,7 +1,6 @@
-using EventTicketing.Api.Endpoints;
+using EventTicketing.Api;
using EventTicketing.Application;
using EventTicketing.Infrastructure;
-using EventTicketing.Infrastructure.Persistence;
var builder = WebApplication.CreateBuilder(args);
@@ -10,16 +9,15 @@
builder.Services.AddProblemDetails();
builder.Services.AddValidation();
-builder.Services.AddOpenApi();
var app = builder.Build();
app.UseExceptionHandler();
-if (app.Environment.IsDevelopment())
+using (var scope = app.Services.CreateScope())
{
- app.MapOpenApi();
- await app.Services.SeedDatabaseAsync();
+ var dbContext = scope.ServiceProvider.GetRequiredService();
+ dbContext.Database.EnsureCreated();
}
app.MapEventEndpoints();
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Properties/launchSettings.json b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Properties/launchSettings.json
index 4fa2de2c5..b0ac7136a 100644
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Properties/launchSettings.json
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/Properties/launchSettings.json
@@ -4,11 +4,20 @@
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
- "launchBrowser": false,
+ "launchBrowser": true,
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "https://localhost:7052;http://localhost:5000",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
}
}
}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/ResultExtensions.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/ResultExtensions.cs
new file mode 100644
index 000000000..f893963bd
--- /dev/null
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Api/ResultExtensions.cs
@@ -0,0 +1,21 @@
+using EventTicketing.Domain;
+
+namespace EventTicketing.Api;
+
+public static class ResultExtensions
+{
+ public static IResult ToProblem(this Result result)
+ {
+ var error = result.Error
+ ?? throw new InvalidOperationException("A successful result is not a problem.");
+
+ var statusCode = error.Type switch
+ {
+ ErrorType.NotFound => StatusCodes.Status404NotFound,
+ ErrorType.Conflict => StatusCodes.Status409Conflict,
+ _ => StatusCodes.Status500InternalServerError
+ };
+
+ return TypedResults.Problem(statusCode: statusCode, title: error.Code, detail: error.Description);
+ }
+}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/Abstractions/IUnitOfWork.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/Abstractions/IUnitOfWork.cs
deleted file mode 100644
index c56b8fcf6..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/Abstractions/IUnitOfWork.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace EventTicketing.Application.Abstractions;
-
-public interface IUnitOfWork
-{
- Task SaveChangesAsync(CancellationToken cancellationToken = default);
-}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/DependencyInjection.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/DependencyInjection.cs
index 66c8325f4..9e37c77a4 100644
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/DependencyInjection.cs
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/DependencyInjection.cs
@@ -1,4 +1,3 @@
-using EventTicketing.Application.Events;
using Microsoft.Extensions.DependencyInjection;
namespace EventTicketing.Application;
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/Events/GetEventAvailability.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/Events/GetEventAvailability.cs
deleted file mode 100644
index b1ffb87d3..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/Events/GetEventAvailability.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using EventTicketing.Domain.Common;
-using EventTicketing.Domain.Events;
-
-namespace EventTicketing.Application.Events;
-
-public sealed record GetEventAvailabilityQuery(int EventId);
-
-public sealed record EventAvailabilityResponse(int EventId, string Name, int Capacity, int TicketsLeft);
-
-public sealed class GetEventAvailabilityHandler(IEventReadRepository reads)
-{
- public async Task> HandleAsync(
- GetEventAvailabilityQuery query, CancellationToken cancellationToken = default)
- {
- var availability = await reads.GetAvailabilityAsync(query.EventId, cancellationToken);
- if (availability is null)
- return EventErrors.NotFound(query.EventId);
-
- return availability;
- }
-}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/Events/IEventReadRepository.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/Events/IEventReadRepository.cs
deleted file mode 100644
index cda14d952..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/Events/IEventReadRepository.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace EventTicketing.Application.Events;
-
-public interface IEventReadRepository
-{
- Task GetAvailabilityAsync(
- int eventId, CancellationToken cancellationToken = default);
-}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/Events/IEventRepository.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/Events/IEventRepository.cs
deleted file mode 100644
index 024f94fe7..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/Events/IEventRepository.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using EventTicketing.Domain.Events;
-
-namespace EventTicketing.Application.Events;
-
-public interface IEventRepository
-{
- Task GetByIdAsync(int eventId, CancellationToken cancellationToken = default);
-}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/GetEventAvailability.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/GetEventAvailability.cs
new file mode 100644
index 000000000..c38c2d44b
--- /dev/null
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/GetEventAvailability.cs
@@ -0,0 +1,20 @@
+using EventTicketing.Domain;
+
+namespace EventTicketing.Application;
+
+public sealed record GetEventAvailabilityQuery(int EventId);
+
+public sealed record EventAvailability(int EventId, string Name, int TicketsLeft);
+
+public sealed class GetEventAvailabilityHandler(IEventRepository events)
+{
+ public async Task> HandleAsync(
+ GetEventAvailabilityQuery query, CancellationToken cancellationToken = default)
+ {
+ var availability = await events.GetAvailabilityAsync(query.EventId, cancellationToken);
+ if (availability is null)
+ return EventErrors.NotFound(query.EventId);
+
+ return availability;
+ }
+}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/IEventRepository.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/IEventRepository.cs
new file mode 100644
index 000000000..1e2d895e8
--- /dev/null
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/IEventRepository.cs
@@ -0,0 +1,12 @@
+using EventTicketing.Domain;
+
+namespace EventTicketing.Application;
+
+public interface IEventRepository
+{
+ Task GetByIdAsync(int eventId, CancellationToken cancellationToken = default);
+
+ Task GetAvailabilityAsync(int eventId, CancellationToken cancellationToken = default);
+
+ Task SaveChangesAsync(CancellationToken cancellationToken = default);
+}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/Events/ReserveTickets.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/ReserveTickets.cs
similarity index 68%
rename from csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/Events/ReserveTickets.cs
rename to csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/ReserveTickets.cs
index a00c95183..56ec774a1 100644
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/Events/ReserveTickets.cs
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Application/ReserveTickets.cs
@@ -1,14 +1,12 @@
-using EventTicketing.Application.Abstractions;
-using EventTicketing.Domain.Common;
-using EventTicketing.Domain.Events;
+using EventTicketing.Domain;
-namespace EventTicketing.Application.Events;
+namespace EventTicketing.Application;
public sealed record ReserveTicketsCommand(int EventId, int Quantity);
public sealed record ReservationResponse(int EventId, int TicketsReserved, int TicketsLeft);
-public sealed class ReserveTicketsHandler(IEventRepository events, IUnitOfWork unitOfWork)
+public sealed class ReserveTicketsHandler(IEventRepository events)
{
public async Task> HandleAsync(
ReserveTicketsCommand command, CancellationToken cancellationToken = default)
@@ -18,10 +16,10 @@ public async Task> HandleAsync(
return EventErrors.NotFound(command.EventId);
var reservation = ev.Reserve(command.Quantity);
- if (reservation.IsFailure)
- return reservation.Error;
+ if (!reservation.IsSuccess)
+ return reservation.Error!;
- await unitOfWork.SaveChangesAsync(cancellationToken);
+ await events.SaveChangesAsync(cancellationToken);
return new ReservationResponse(ev.Id, command.Quantity, ev.TicketsLeft);
}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.ArchitectureTests/DependencyRuleTests.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.ArchitectureTests/DependencyRuleTests.cs
index 76f335748..76a3edb32 100644
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.ArchitectureTests/DependencyRuleTests.cs
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.ArchitectureTests/DependencyRuleTests.cs
@@ -1,26 +1,20 @@
-using EventTicketing.Application.Events;
-using EventTicketing.Domain.Events;
-using EventTicketing.Infrastructure.Persistence;
+using EventTicketing.Application;
+using EventTicketing.Domain;
using NetArchTest.Rules;
-using TestResult = NetArchTest.Rules.TestResult;
namespace EventTicketing.ArchitectureTests;
public class DependencyRuleTests
{
- private const string Application = "EventTicketing.Application";
- private const string Infrastructure = "EventTicketing.Infrastructure";
- private const string Api = "EventTicketing.Api";
-
[Fact]
public void Domain_DependsOnNoOtherLayer()
{
var result = Types.InAssembly(typeof(Event).Assembly)
.ShouldNot()
- .HaveDependencyOnAny(Application, Infrastructure, Api)
+ .HaveDependencyOnAny("EventTicketing.Application", "EventTicketing.Infrastructure", "EventTicketing.Api")
.GetResult();
- Assert.True(result.IsSuccessful, Describe(result));
+ Assert.True(result.IsSuccessful, "Offending types: " + string.Join(", ", result.FailingTypeNames ?? []));
}
[Fact]
@@ -28,23 +22,10 @@ public void Application_DoesNotDependOnInfrastructureOrTheWeb()
{
var result = Types.InAssembly(typeof(ReserveTicketsHandler).Assembly)
.ShouldNot()
- .HaveDependencyOnAny(Infrastructure, Api, "Microsoft.EntityFrameworkCore", "Microsoft.AspNetCore")
+ .HaveDependencyOnAny("EventTicketing.Infrastructure", "EventTicketing.Api",
+ "Microsoft.EntityFrameworkCore", "Microsoft.AspNetCore")
.GetResult();
- Assert.True(result.IsSuccessful, Describe(result));
+ Assert.True(result.IsSuccessful, "Offending types: " + string.Join(", ", result.FailingTypeNames ?? []));
}
-
- [Fact]
- public void Infrastructure_DoesNotDependOnTheApi()
- {
- var result = Types.InAssembly(typeof(TicketingDbContext).Assembly)
- .ShouldNot()
- .HaveDependencyOn(Api)
- .GetResult();
-
- Assert.True(result.IsSuccessful, Describe(result));
- }
-
- private static string Describe(TestResult result) =>
- "Offending types: " + string.Join(", ", result.FailingTypeNames ?? []);
}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.ArchitectureTests/EventTicketing.ArchitectureTests.csproj b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.ArchitectureTests/EventTicketing.ArchitectureTests.csproj
index 1e027d8a6..788e2905f 100644
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.ArchitectureTests/EventTicketing.ArchitectureTests.csproj
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.ArchitectureTests/EventTicketing.ArchitectureTests.csproj
@@ -20,7 +20,7 @@
-
+
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Common/DomainException.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Common/DomainException.cs
deleted file mode 100644
index 0ea30629b..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Common/DomainException.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-namespace EventTicketing.Domain.Common;
-
-public sealed class DomainException(string message) : Exception(message);
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Common/Error.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Common/Error.cs
deleted file mode 100644
index c0e7687a2..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Common/Error.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace EventTicketing.Domain.Common;
-
-public enum ErrorType
-{
- Failure,
- Validation,
- NotFound,
- Conflict
-}
-
-public sealed record Error(string Code, string Description, ErrorType Type)
-{
- public static readonly Error None = new(string.Empty, string.Empty, ErrorType.Failure);
-
- public static Error Validation(string code, string description) =>
- new(code, description, ErrorType.Validation);
-
- public static Error NotFound(string code, string description) =>
- new(code, description, ErrorType.NotFound);
-
- public static Error Conflict(string code, string description) =>
- new(code, description, ErrorType.Conflict);
-}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Common/Result.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Common/Result.cs
deleted file mode 100644
index 67147a661..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Common/Result.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-namespace EventTicketing.Domain.Common;
-
-public class Result
-{
- protected Result(bool isSuccess, Error error)
- {
- if (isSuccess && error != Error.None)
- throw new ArgumentException("A successful result cannot carry an error.", nameof(error));
-
- if (!isSuccess && error == Error.None)
- throw new ArgumentException("A failed result needs an error.", nameof(error));
-
- IsSuccess = isSuccess;
- Error = error;
- }
-
- public bool IsSuccess { get; }
- public bool IsFailure => !IsSuccess;
- public Error Error { get; }
-
- public static Result Success() => new(true, Error.None);
- public static Result Failure(Error error) => new(false, error);
-
- public static implicit operator Result(Error error) => Failure(error);
-}
-
-public sealed class Result : Result
-{
- private readonly TValue? _value;
-
- private Result(TValue? value, bool isSuccess, Error error)
- : base(isSuccess, error) => _value = value;
-
- public TValue Value => IsSuccess
- ? _value!
- : throw new InvalidOperationException("A failed result has no value.");
-
- public static implicit operator Result(TValue value) =>
- new(value, true, Error.None);
-
- public static implicit operator Result(Error error) =>
- new(default, false, error);
-}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Events/Event.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Event.cs
similarity index 65%
rename from csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Events/Event.cs
rename to csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Event.cs
index 4b4caa21c..8c2b7c2ed 100644
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Events/Event.cs
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Event.cs
@@ -1,6 +1,4 @@
-using EventTicketing.Domain.Common;
-
-namespace EventTicketing.Domain.Events;
+namespace EventTicketing.Domain;
public sealed class Event
{
@@ -19,19 +17,15 @@ private Event(string name, int capacity)
public static Event Create(string name, int capacity)
{
- if (string.IsNullOrWhiteSpace(name))
- throw new DomainException("An event needs a name.");
-
- if (capacity <= 0)
- throw new DomainException("Capacity must be positive.");
+ ArgumentException.ThrowIfNullOrWhiteSpace(name);
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(capacity);
return new Event(name, capacity);
}
public Result Reserve(int quantity)
{
- if (quantity <= 0)
- throw new DomainException("Quantity must be positive.");
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(quantity);
if (quantity > TicketsLeft)
return EventErrors.SoldOut(TicketsLeft, quantity);
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/EventErrors.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/EventErrors.cs
new file mode 100644
index 000000000..24d232a0c
--- /dev/null
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/EventErrors.cs
@@ -0,0 +1,10 @@
+namespace EventTicketing.Domain;
+
+public static class EventErrors
+{
+ public static Error NotFound(int eventId) =>
+ new("Event.NotFound", $"Event {eventId} was not found.", ErrorType.NotFound);
+
+ public static Error SoldOut(int ticketsLeft, int requested) =>
+ new("Event.SoldOut", $"Only {ticketsLeft} tickets left, {requested} requested.", ErrorType.Conflict);
+}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Events/EventErrors.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Events/EventErrors.cs
deleted file mode 100644
index c3f9286a7..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Events/EventErrors.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using EventTicketing.Domain.Common;
-
-namespace EventTicketing.Domain.Events;
-
-public static class EventErrors
-{
- public static Error NotFound(int eventId) =>
- Error.NotFound("Event.NotFound", $"Event {eventId} was not found.");
-
- public static Error SoldOut(int ticketsLeft, int requested) =>
- Error.Conflict("Event.SoldOut", $"Only {ticketsLeft} tickets left, {requested} requested.");
-}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Result.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Result.cs
new file mode 100644
index 000000000..7f406bb58
--- /dev/null
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Domain/Result.cs
@@ -0,0 +1,39 @@
+namespace EventTicketing.Domain;
+
+public enum ErrorType
+{
+ NotFound,
+ Conflict
+}
+
+public sealed record Error(string Code, string Description, ErrorType Type);
+
+public class Result
+{
+ protected Result(Error? error) => Error = error;
+
+ public Error? Error { get; }
+ public bool IsSuccess => Error is null;
+
+ public static Result Success() => new(null);
+
+ public static implicit operator Result(Error error) => new(error);
+}
+
+public sealed class Result : Result
+{
+ private readonly TValue? _value;
+
+ private Result(TValue? value, Error? error)
+ : base(error) => _value = value;
+
+ public TValue Value => IsSuccess
+ ? _value!
+ : throw new InvalidOperationException("A failed result has no value.");
+
+ public static implicit operator Result(TValue value) =>
+ new(value, null);
+
+ public static implicit operator Result(Error error) =>
+ new(default, error);
+}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/DependencyInjection.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/DependencyInjection.cs
index 050854fe7..dc5b86a5b 100644
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/DependencyInjection.cs
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/DependencyInjection.cs
@@ -1,7 +1,4 @@
-using EventTicketing.Application.Abstractions;
-using EventTicketing.Application.Events;
-using EventTicketing.Infrastructure.Events;
-using EventTicketing.Infrastructure.Persistence;
+using EventTicketing.Application;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
@@ -13,10 +10,7 @@ public static IServiceCollection AddInfrastructure(
this IServiceCollection services, string connectionString)
{
services.AddDbContext(options => options.UseSqlite(connectionString));
-
services.AddScoped();
- services.AddScoped();
- services.AddScoped();
return services;
}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/EventRepository.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/EventRepository.cs
new file mode 100644
index 000000000..dea7a45f1
--- /dev/null
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/EventRepository.cs
@@ -0,0 +1,21 @@
+using EventTicketing.Application;
+using EventTicketing.Domain;
+using Microsoft.EntityFrameworkCore;
+
+namespace EventTicketing.Infrastructure;
+
+public sealed class EventRepository(TicketingDbContext dbContext) : IEventRepository
+{
+ public Task GetByIdAsync(int eventId, CancellationToken cancellationToken = default) =>
+ dbContext.Events.FirstOrDefaultAsync(e => e.Id == eventId, cancellationToken);
+
+ public Task GetAvailabilityAsync(int eventId, CancellationToken cancellationToken = default) =>
+ dbContext.Events
+ .AsNoTracking()
+ .Where(e => e.Id == eventId)
+ .Select(e => new EventAvailability(e.Id, e.Name, e.Capacity - e.TicketsSold))
+ .FirstOrDefaultAsync(cancellationToken);
+
+ public Task SaveChangesAsync(CancellationToken cancellationToken = default) =>
+ dbContext.SaveChangesAsync(cancellationToken);
+}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/Events/EventReadRepository.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/Events/EventReadRepository.cs
deleted file mode 100644
index 9db8ad92c..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/Events/EventReadRepository.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using EventTicketing.Application.Events;
-using EventTicketing.Infrastructure.Persistence;
-using Microsoft.EntityFrameworkCore;
-
-namespace EventTicketing.Infrastructure.Events;
-
-public sealed class EventReadRepository(TicketingDbContext dbContext) : IEventReadRepository
-{
- public Task GetAvailabilityAsync(
- int eventId, CancellationToken cancellationToken = default) =>
- dbContext.Events
- .AsNoTracking()
- .Where(e => e.Id == eventId)
- .Select(e => new EventAvailabilityResponse(
- e.Id, e.Name, e.Capacity, e.Capacity - e.TicketsSold))
- .FirstOrDefaultAsync(cancellationToken);
-}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/Events/EventRepository.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/Events/EventRepository.cs
deleted file mode 100644
index 0309acfba..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/Events/EventRepository.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using EventTicketing.Application.Events;
-using EventTicketing.Domain.Events;
-using EventTicketing.Infrastructure.Persistence;
-using Microsoft.EntityFrameworkCore;
-
-namespace EventTicketing.Infrastructure.Events;
-
-public sealed class EventRepository(TicketingDbContext dbContext) : IEventRepository
-{
- public Task GetByIdAsync(int eventId, CancellationToken cancellationToken = default) =>
- dbContext.Events.FirstOrDefaultAsync(e => e.Id == eventId, cancellationToken);
-}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/Persistence/DatabaseSeeder.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/Persistence/DatabaseSeeder.cs
deleted file mode 100644
index 90fde2841..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/Persistence/DatabaseSeeder.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using EventTicketing.Domain.Events;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.DependencyInjection;
-
-namespace EventTicketing.Infrastructure.Persistence;
-
-public static class DatabaseSeeder
-{
- public static async Task SeedDatabaseAsync(this IServiceProvider services)
- {
- using var scope = services.CreateScope();
- var dbContext = scope.ServiceProvider.GetRequiredService();
-
- await dbContext.Database.EnsureCreatedAsync();
-
- if (!await dbContext.Events.AnyAsync())
- {
- dbContext.Events.AddRange(
- Event.Create("Clean Architecture Live", capacity: 100),
- Event.Create("Tiny Jazz Club Night", capacity: 2));
-
- await dbContext.SaveChangesAsync();
- }
- }
-}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/Persistence/UnitOfWork.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/Persistence/UnitOfWork.cs
deleted file mode 100644
index dddcbcdef..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/Persistence/UnitOfWork.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using EventTicketing.Application.Abstractions;
-
-namespace EventTicketing.Infrastructure.Persistence;
-
-public sealed class UnitOfWork(TicketingDbContext dbContext) : IUnitOfWork
-{
- public Task SaveChangesAsync(CancellationToken cancellationToken = default) =>
- dbContext.SaveChangesAsync(cancellationToken);
-}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/Persistence/TicketingDbContext.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/TicketingDbContext.cs
similarity index 58%
rename from csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/Persistence/TicketingDbContext.cs
rename to csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/TicketingDbContext.cs
index bb9e6806b..93502dbf3 100644
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/Persistence/TicketingDbContext.cs
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.Infrastructure/TicketingDbContext.cs
@@ -1,7 +1,7 @@
-using EventTicketing.Domain.Events;
+using EventTicketing.Domain;
using Microsoft.EntityFrameworkCore;
-namespace EventTicketing.Infrastructure.Persistence;
+namespace EventTicketing.Infrastructure;
public sealed class TicketingDbContext(DbContextOptions options)
: DbContext(options)
@@ -12,8 +12,11 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity(builder =>
{
- builder.HasKey(e => e.Id);
builder.Property(e => e.Name).HasMaxLength(200);
+
+ builder.HasData(
+ new { Id = 1, Name = "Clean Architecture Live", Capacity = 100, TicketsSold = 0 },
+ new { Id = 2, Name = "Tiny Jazz Club Night", Capacity = 2, TicketsSold = 0 });
});
}
}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.UnitTests/Application/Fakes.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.UnitTests/Application/Fakes.cs
deleted file mode 100644
index a2b91f2c2..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.UnitTests/Application/Fakes.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-using EventTicketing.Application.Abstractions;
-using EventTicketing.Application.Events;
-using EventTicketing.Domain.Events;
-
-namespace EventTicketing.UnitTests.Application;
-
-internal sealed class FakeEventRepository(Event? ev) : IEventRepository
-{
- public Task GetByIdAsync(int eventId, CancellationToken cancellationToken = default) =>
- Task.FromResult(ev);
-}
-
-internal sealed class FakeUnitOfWork : IUnitOfWork
-{
- public int SaveCount { get; private set; }
-
- public Task SaveChangesAsync(CancellationToken cancellationToken = default)
- {
- SaveCount++;
- return Task.CompletedTask;
- }
-}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.UnitTests/Application/ReserveTicketsHandlerTests.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.UnitTests/Application/ReserveTicketsHandlerTests.cs
deleted file mode 100644
index 12a26bd66..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.UnitTests/Application/ReserveTicketsHandlerTests.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using EventTicketing.Application.Events;
-using EventTicketing.Domain.Events;
-
-namespace EventTicketing.UnitTests.Application;
-
-public class ReserveTicketsHandlerTests
-{
- [Fact]
- public async Task HandleAsync_WhenTicketsAreAvailable_ReservesAndSaves()
- {
- var unitOfWork = new FakeUnitOfWork();
- var ev = Event.Create("Clean Architecture Live", capacity: 100);
- var handler = new ReserveTicketsHandler(new FakeEventRepository(ev), unitOfWork);
-
- var result = await handler.HandleAsync(
- new ReserveTicketsCommand(EventId: 1, Quantity: 2), TestContext.Current.CancellationToken);
-
- Assert.True(result.IsSuccess);
- Assert.Equal(98, result.Value.TicketsLeft);
- Assert.Equal(1, unitOfWork.SaveCount);
- }
-
- [Fact]
- public async Task HandleAsync_WhenEventIsSoldOut_ReturnsConflictAndSavesNothing()
- {
- var unitOfWork = new FakeUnitOfWork();
- var ev = Event.Create("Clean Architecture Live", capacity: 1);
- var handler = new ReserveTicketsHandler(new FakeEventRepository(ev), unitOfWork);
-
- var result = await handler.HandleAsync(
- new ReserveTicketsCommand(EventId: 1, Quantity: 2), TestContext.Current.CancellationToken);
-
- Assert.True(result.IsFailure);
- Assert.Equal("Event.SoldOut", result.Error.Code);
- Assert.Equal(0, unitOfWork.SaveCount);
- }
-
- [Fact]
- public async Task HandleAsync_WhenEventDoesNotExist_ReturnsNotFound()
- {
- var handler = new ReserveTicketsHandler(new FakeEventRepository(null), new FakeUnitOfWork());
-
- var result = await handler.HandleAsync(
- new ReserveTicketsCommand(EventId: 42, Quantity: 2), TestContext.Current.CancellationToken);
-
- Assert.True(result.IsFailure);
- Assert.Equal("Event.NotFound", result.Error.Code);
- }
-}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.UnitTests/Domain/EventTests.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.UnitTests/Domain/EventTests.cs
deleted file mode 100644
index ada11ff2a..000000000
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.UnitTests/Domain/EventTests.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using EventTicketing.Domain.Common;
-using EventTicketing.Domain.Events;
-
-namespace EventTicketing.UnitTests.Domain;
-
-public class EventTests
-{
- [Fact]
- public void Reserve_WhenEnoughTicketsAreLeft_SellsThem()
- {
- var ev = Event.Create("Clean Architecture Live", capacity: 100);
-
- var result = ev.Reserve(3);
-
- Assert.True(result.IsSuccess);
- Assert.Equal(97, ev.TicketsLeft);
- }
-
- [Fact]
- public void Reserve_WhenTooFewTicketsAreLeft_ReturnsSoldOutAndSellsNothing()
- {
- var ev = Event.Create("Clean Architecture Live", capacity: 2);
-
- var result = ev.Reserve(3);
-
- Assert.True(result.IsFailure);
- Assert.Equal("Event.SoldOut", result.Error.Code);
- Assert.Equal(0, ev.TicketsSold);
- }
-
- [Fact]
- public void Reserve_WithNonPositiveQuantity_Throws()
- {
- var ev = Event.Create("Clean Architecture Live", capacity: 100);
-
- Assert.Throws(() => ev.Reserve(0));
- }
-}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.UnitTests/EventTests.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.UnitTests/EventTests.cs
new file mode 100644
index 000000000..e838af944
--- /dev/null
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.UnitTests/EventTests.cs
@@ -0,0 +1,29 @@
+using EventTicketing.Domain;
+
+namespace EventTicketing.UnitTests;
+
+public class EventTests
+{
+ [Fact]
+ public void Reserve_WhenEnoughTicketsAreLeft_SellsThem()
+ {
+ var ev = Event.Create("Clean Architecture Live", capacity: 100);
+
+ var result = ev.Reserve(3);
+
+ Assert.True(result.IsSuccess);
+ Assert.Equal(97, ev.TicketsLeft);
+ }
+
+ [Fact]
+ public void Reserve_WhenTooFewTicketsAreLeft_ReturnsSoldOutAndSellsNothing()
+ {
+ var ev = Event.Create("Tiny Jazz Club Night", capacity: 2);
+
+ var result = ev.Reserve(3);
+
+ Assert.False(result.IsSuccess);
+ Assert.Equal("Event.SoldOut", result.Error!.Code);
+ Assert.Equal(0, ev.TicketsSold);
+ }
+}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.UnitTests/ReserveTicketsHandlerTests.cs b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.UnitTests/ReserveTicketsHandlerTests.cs
new file mode 100644
index 000000000..8d04af3b5
--- /dev/null
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.UnitTests/ReserveTicketsHandlerTests.cs
@@ -0,0 +1,52 @@
+using EventTicketing.Application;
+using EventTicketing.Domain;
+
+namespace EventTicketing.UnitTests;
+
+public class ReserveTicketsHandlerTests
+{
+ [Fact]
+ public async Task HandleAsync_WhenTicketsAreAvailable_ReservesAndSaves()
+ {
+ var events = new FakeEventRepository(Event.Create("Clean Architecture Live", capacity: 100));
+ var handler = new ReserveTicketsHandler(events);
+
+ var result = await handler.HandleAsync(
+ new ReserveTicketsCommand(EventId: 1, Quantity: 2), TestContext.Current.CancellationToken);
+
+ Assert.True(result.IsSuccess);
+ Assert.Equal(98, result.Value.TicketsLeft);
+ Assert.Equal(1, events.SaveCount);
+ }
+
+ [Fact]
+ public async Task HandleAsync_WhenEventIsSoldOut_SavesNothing()
+ {
+ var events = new FakeEventRepository(Event.Create("Tiny Jazz Club Night", capacity: 1));
+ var handler = new ReserveTicketsHandler(events);
+
+ var result = await handler.HandleAsync(
+ new ReserveTicketsCommand(EventId: 1, Quantity: 2), TestContext.Current.CancellationToken);
+
+ Assert.False(result.IsSuccess);
+ Assert.Equal("Event.SoldOut", result.Error!.Code);
+ Assert.Equal(0, events.SaveCount);
+ }
+}
+
+internal sealed class FakeEventRepository(Event? ev) : IEventRepository
+{
+ public int SaveCount { get; private set; }
+
+ public Task GetByIdAsync(int eventId, CancellationToken cancellationToken = default) =>
+ Task.FromResult(ev);
+
+ public Task GetAvailabilityAsync(int eventId, CancellationToken cancellationToken = default) =>
+ Task.FromResult(null);
+
+ public Task SaveChangesAsync(CancellationToken cancellationToken = default)
+ {
+ SaveCount++;
+ return Task.CompletedTask;
+ }
+}
diff --git a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.sln b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.sln
index 629ee5d06..7deab074b 100644
--- a/csharp-architectural-patterns/CleanArchitecture/EventTicketing.sln
+++ b/csharp-architectural-patterns/CleanArchitecture/EventTicketing.sln
@@ -3,17 +3,17 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventTicketing.Domain", "EventTicketing.Domain\EventTicketing.Domain.csproj", "{159B8B27-7ECF-4DEE-AA8E-975ABCDE12C9}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventTicketing.Domain", "EventTicketing.Domain\EventTicketing.Domain.csproj", "{747A3FF7-C320-4377-BBC9-3BFDEFDF66F3}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventTicketing.Application", "EventTicketing.Application\EventTicketing.Application.csproj", "{A446F8F5-A9D2-4B20-B400-634C8601F953}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventTicketing.Application", "EventTicketing.Application\EventTicketing.Application.csproj", "{414CB068-6303-4C6B-88C3-110DF13AB824}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventTicketing.Infrastructure", "EventTicketing.Infrastructure\EventTicketing.Infrastructure.csproj", "{7E3F5A24-15AC-4C27-8E6C-AC8EFB695138}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventTicketing.Infrastructure", "EventTicketing.Infrastructure\EventTicketing.Infrastructure.csproj", "{77F9DCE0-A684-4F19-A12D-D96498877407}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventTicketing.Api", "EventTicketing.Api\EventTicketing.Api.csproj", "{CDDE590D-17CF-4039-A4E5-8A1EC7A245F7}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventTicketing.Api", "EventTicketing.Api\EventTicketing.Api.csproj", "{C3EC8948-3309-4EF1-B5CC-58B242B0BAFA}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventTicketing.UnitTests", "EventTicketing.UnitTests\EventTicketing.UnitTests.csproj", "{DE452D9A-440B-44EE-8D6F-CE2D89B5A6E4}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventTicketing.UnitTests", "EventTicketing.UnitTests\EventTicketing.UnitTests.csproj", "{B766BC5E-484B-4F1B-9B7E-69FA58302923}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventTicketing.ArchitectureTests", "EventTicketing.ArchitectureTests\EventTicketing.ArchitectureTests.csproj", "{2EA7392E-A820-4312-84E1-5F35C413019E}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventTicketing.ArchitectureTests", "EventTicketing.ArchitectureTests\EventTicketing.ArchitectureTests.csproj", "{59EC3582-12E8-4164-B40F-17B4D15B8F61}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -25,78 +25,78 @@ Global
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {159B8B27-7ECF-4DEE-AA8E-975ABCDE12C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {159B8B27-7ECF-4DEE-AA8E-975ABCDE12C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {159B8B27-7ECF-4DEE-AA8E-975ABCDE12C9}.Debug|x64.ActiveCfg = Debug|Any CPU
- {159B8B27-7ECF-4DEE-AA8E-975ABCDE12C9}.Debug|x64.Build.0 = Debug|Any CPU
- {159B8B27-7ECF-4DEE-AA8E-975ABCDE12C9}.Debug|x86.ActiveCfg = Debug|Any CPU
- {159B8B27-7ECF-4DEE-AA8E-975ABCDE12C9}.Debug|x86.Build.0 = Debug|Any CPU
- {159B8B27-7ECF-4DEE-AA8E-975ABCDE12C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {159B8B27-7ECF-4DEE-AA8E-975ABCDE12C9}.Release|Any CPU.Build.0 = Release|Any CPU
- {159B8B27-7ECF-4DEE-AA8E-975ABCDE12C9}.Release|x64.ActiveCfg = Release|Any CPU
- {159B8B27-7ECF-4DEE-AA8E-975ABCDE12C9}.Release|x64.Build.0 = Release|Any CPU
- {159B8B27-7ECF-4DEE-AA8E-975ABCDE12C9}.Release|x86.ActiveCfg = Release|Any CPU
- {159B8B27-7ECF-4DEE-AA8E-975ABCDE12C9}.Release|x86.Build.0 = Release|Any CPU
- {A446F8F5-A9D2-4B20-B400-634C8601F953}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A446F8F5-A9D2-4B20-B400-634C8601F953}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A446F8F5-A9D2-4B20-B400-634C8601F953}.Debug|x64.ActiveCfg = Debug|Any CPU
- {A446F8F5-A9D2-4B20-B400-634C8601F953}.Debug|x64.Build.0 = Debug|Any CPU
- {A446F8F5-A9D2-4B20-B400-634C8601F953}.Debug|x86.ActiveCfg = Debug|Any CPU
- {A446F8F5-A9D2-4B20-B400-634C8601F953}.Debug|x86.Build.0 = Debug|Any CPU
- {A446F8F5-A9D2-4B20-B400-634C8601F953}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A446F8F5-A9D2-4B20-B400-634C8601F953}.Release|Any CPU.Build.0 = Release|Any CPU
- {A446F8F5-A9D2-4B20-B400-634C8601F953}.Release|x64.ActiveCfg = Release|Any CPU
- {A446F8F5-A9D2-4B20-B400-634C8601F953}.Release|x64.Build.0 = Release|Any CPU
- {A446F8F5-A9D2-4B20-B400-634C8601F953}.Release|x86.ActiveCfg = Release|Any CPU
- {A446F8F5-A9D2-4B20-B400-634C8601F953}.Release|x86.Build.0 = Release|Any CPU
- {7E3F5A24-15AC-4C27-8E6C-AC8EFB695138}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7E3F5A24-15AC-4C27-8E6C-AC8EFB695138}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7E3F5A24-15AC-4C27-8E6C-AC8EFB695138}.Debug|x64.ActiveCfg = Debug|Any CPU
- {7E3F5A24-15AC-4C27-8E6C-AC8EFB695138}.Debug|x64.Build.0 = Debug|Any CPU
- {7E3F5A24-15AC-4C27-8E6C-AC8EFB695138}.Debug|x86.ActiveCfg = Debug|Any CPU
- {7E3F5A24-15AC-4C27-8E6C-AC8EFB695138}.Debug|x86.Build.0 = Debug|Any CPU
- {7E3F5A24-15AC-4C27-8E6C-AC8EFB695138}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7E3F5A24-15AC-4C27-8E6C-AC8EFB695138}.Release|Any CPU.Build.0 = Release|Any CPU
- {7E3F5A24-15AC-4C27-8E6C-AC8EFB695138}.Release|x64.ActiveCfg = Release|Any CPU
- {7E3F5A24-15AC-4C27-8E6C-AC8EFB695138}.Release|x64.Build.0 = Release|Any CPU
- {7E3F5A24-15AC-4C27-8E6C-AC8EFB695138}.Release|x86.ActiveCfg = Release|Any CPU
- {7E3F5A24-15AC-4C27-8E6C-AC8EFB695138}.Release|x86.Build.0 = Release|Any CPU
- {CDDE590D-17CF-4039-A4E5-8A1EC7A245F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CDDE590D-17CF-4039-A4E5-8A1EC7A245F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CDDE590D-17CF-4039-A4E5-8A1EC7A245F7}.Debug|x64.ActiveCfg = Debug|Any CPU
- {CDDE590D-17CF-4039-A4E5-8A1EC7A245F7}.Debug|x64.Build.0 = Debug|Any CPU
- {CDDE590D-17CF-4039-A4E5-8A1EC7A245F7}.Debug|x86.ActiveCfg = Debug|Any CPU
- {CDDE590D-17CF-4039-A4E5-8A1EC7A245F7}.Debug|x86.Build.0 = Debug|Any CPU
- {CDDE590D-17CF-4039-A4E5-8A1EC7A245F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CDDE590D-17CF-4039-A4E5-8A1EC7A245F7}.Release|Any CPU.Build.0 = Release|Any CPU
- {CDDE590D-17CF-4039-A4E5-8A1EC7A245F7}.Release|x64.ActiveCfg = Release|Any CPU
- {CDDE590D-17CF-4039-A4E5-8A1EC7A245F7}.Release|x64.Build.0 = Release|Any CPU
- {CDDE590D-17CF-4039-A4E5-8A1EC7A245F7}.Release|x86.ActiveCfg = Release|Any CPU
- {CDDE590D-17CF-4039-A4E5-8A1EC7A245F7}.Release|x86.Build.0 = Release|Any CPU
- {DE452D9A-440B-44EE-8D6F-CE2D89B5A6E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {DE452D9A-440B-44EE-8D6F-CE2D89B5A6E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {DE452D9A-440B-44EE-8D6F-CE2D89B5A6E4}.Debug|x64.ActiveCfg = Debug|Any CPU
- {DE452D9A-440B-44EE-8D6F-CE2D89B5A6E4}.Debug|x64.Build.0 = Debug|Any CPU
- {DE452D9A-440B-44EE-8D6F-CE2D89B5A6E4}.Debug|x86.ActiveCfg = Debug|Any CPU
- {DE452D9A-440B-44EE-8D6F-CE2D89B5A6E4}.Debug|x86.Build.0 = Debug|Any CPU
- {DE452D9A-440B-44EE-8D6F-CE2D89B5A6E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {DE452D9A-440B-44EE-8D6F-CE2D89B5A6E4}.Release|Any CPU.Build.0 = Release|Any CPU
- {DE452D9A-440B-44EE-8D6F-CE2D89B5A6E4}.Release|x64.ActiveCfg = Release|Any CPU
- {DE452D9A-440B-44EE-8D6F-CE2D89B5A6E4}.Release|x64.Build.0 = Release|Any CPU
- {DE452D9A-440B-44EE-8D6F-CE2D89B5A6E4}.Release|x86.ActiveCfg = Release|Any CPU
- {DE452D9A-440B-44EE-8D6F-CE2D89B5A6E4}.Release|x86.Build.0 = Release|Any CPU
- {2EA7392E-A820-4312-84E1-5F35C413019E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2EA7392E-A820-4312-84E1-5F35C413019E}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2EA7392E-A820-4312-84E1-5F35C413019E}.Debug|x64.ActiveCfg = Debug|Any CPU
- {2EA7392E-A820-4312-84E1-5F35C413019E}.Debug|x64.Build.0 = Debug|Any CPU
- {2EA7392E-A820-4312-84E1-5F35C413019E}.Debug|x86.ActiveCfg = Debug|Any CPU
- {2EA7392E-A820-4312-84E1-5F35C413019E}.Debug|x86.Build.0 = Debug|Any CPU
- {2EA7392E-A820-4312-84E1-5F35C413019E}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2EA7392E-A820-4312-84E1-5F35C413019E}.Release|Any CPU.Build.0 = Release|Any CPU
- {2EA7392E-A820-4312-84E1-5F35C413019E}.Release|x64.ActiveCfg = Release|Any CPU
- {2EA7392E-A820-4312-84E1-5F35C413019E}.Release|x64.Build.0 = Release|Any CPU
- {2EA7392E-A820-4312-84E1-5F35C413019E}.Release|x86.ActiveCfg = Release|Any CPU
- {2EA7392E-A820-4312-84E1-5F35C413019E}.Release|x86.Build.0 = Release|Any CPU
+ {747A3FF7-C320-4377-BBC9-3BFDEFDF66F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {747A3FF7-C320-4377-BBC9-3BFDEFDF66F3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {747A3FF7-C320-4377-BBC9-3BFDEFDF66F3}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {747A3FF7-C320-4377-BBC9-3BFDEFDF66F3}.Debug|x64.Build.0 = Debug|Any CPU
+ {747A3FF7-C320-4377-BBC9-3BFDEFDF66F3}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {747A3FF7-C320-4377-BBC9-3BFDEFDF66F3}.Debug|x86.Build.0 = Debug|Any CPU
+ {747A3FF7-C320-4377-BBC9-3BFDEFDF66F3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {747A3FF7-C320-4377-BBC9-3BFDEFDF66F3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {747A3FF7-C320-4377-BBC9-3BFDEFDF66F3}.Release|x64.ActiveCfg = Release|Any CPU
+ {747A3FF7-C320-4377-BBC9-3BFDEFDF66F3}.Release|x64.Build.0 = Release|Any CPU
+ {747A3FF7-C320-4377-BBC9-3BFDEFDF66F3}.Release|x86.ActiveCfg = Release|Any CPU
+ {747A3FF7-C320-4377-BBC9-3BFDEFDF66F3}.Release|x86.Build.0 = Release|Any CPU
+ {414CB068-6303-4C6B-88C3-110DF13AB824}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {414CB068-6303-4C6B-88C3-110DF13AB824}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {414CB068-6303-4C6B-88C3-110DF13AB824}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {414CB068-6303-4C6B-88C3-110DF13AB824}.Debug|x64.Build.0 = Debug|Any CPU
+ {414CB068-6303-4C6B-88C3-110DF13AB824}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {414CB068-6303-4C6B-88C3-110DF13AB824}.Debug|x86.Build.0 = Debug|Any CPU
+ {414CB068-6303-4C6B-88C3-110DF13AB824}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {414CB068-6303-4C6B-88C3-110DF13AB824}.Release|Any CPU.Build.0 = Release|Any CPU
+ {414CB068-6303-4C6B-88C3-110DF13AB824}.Release|x64.ActiveCfg = Release|Any CPU
+ {414CB068-6303-4C6B-88C3-110DF13AB824}.Release|x64.Build.0 = Release|Any CPU
+ {414CB068-6303-4C6B-88C3-110DF13AB824}.Release|x86.ActiveCfg = Release|Any CPU
+ {414CB068-6303-4C6B-88C3-110DF13AB824}.Release|x86.Build.0 = Release|Any CPU
+ {77F9DCE0-A684-4F19-A12D-D96498877407}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {77F9DCE0-A684-4F19-A12D-D96498877407}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {77F9DCE0-A684-4F19-A12D-D96498877407}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {77F9DCE0-A684-4F19-A12D-D96498877407}.Debug|x64.Build.0 = Debug|Any CPU
+ {77F9DCE0-A684-4F19-A12D-D96498877407}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {77F9DCE0-A684-4F19-A12D-D96498877407}.Debug|x86.Build.0 = Debug|Any CPU
+ {77F9DCE0-A684-4F19-A12D-D96498877407}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {77F9DCE0-A684-4F19-A12D-D96498877407}.Release|Any CPU.Build.0 = Release|Any CPU
+ {77F9DCE0-A684-4F19-A12D-D96498877407}.Release|x64.ActiveCfg = Release|Any CPU
+ {77F9DCE0-A684-4F19-A12D-D96498877407}.Release|x64.Build.0 = Release|Any CPU
+ {77F9DCE0-A684-4F19-A12D-D96498877407}.Release|x86.ActiveCfg = Release|Any CPU
+ {77F9DCE0-A684-4F19-A12D-D96498877407}.Release|x86.Build.0 = Release|Any CPU
+ {C3EC8948-3309-4EF1-B5CC-58B242B0BAFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C3EC8948-3309-4EF1-B5CC-58B242B0BAFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C3EC8948-3309-4EF1-B5CC-58B242B0BAFA}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C3EC8948-3309-4EF1-B5CC-58B242B0BAFA}.Debug|x64.Build.0 = Debug|Any CPU
+ {C3EC8948-3309-4EF1-B5CC-58B242B0BAFA}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {C3EC8948-3309-4EF1-B5CC-58B242B0BAFA}.Debug|x86.Build.0 = Debug|Any CPU
+ {C3EC8948-3309-4EF1-B5CC-58B242B0BAFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C3EC8948-3309-4EF1-B5CC-58B242B0BAFA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C3EC8948-3309-4EF1-B5CC-58B242B0BAFA}.Release|x64.ActiveCfg = Release|Any CPU
+ {C3EC8948-3309-4EF1-B5CC-58B242B0BAFA}.Release|x64.Build.0 = Release|Any CPU
+ {C3EC8948-3309-4EF1-B5CC-58B242B0BAFA}.Release|x86.ActiveCfg = Release|Any CPU
+ {C3EC8948-3309-4EF1-B5CC-58B242B0BAFA}.Release|x86.Build.0 = Release|Any CPU
+ {B766BC5E-484B-4F1B-9B7E-69FA58302923}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B766BC5E-484B-4F1B-9B7E-69FA58302923}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B766BC5E-484B-4F1B-9B7E-69FA58302923}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B766BC5E-484B-4F1B-9B7E-69FA58302923}.Debug|x64.Build.0 = Debug|Any CPU
+ {B766BC5E-484B-4F1B-9B7E-69FA58302923}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {B766BC5E-484B-4F1B-9B7E-69FA58302923}.Debug|x86.Build.0 = Debug|Any CPU
+ {B766BC5E-484B-4F1B-9B7E-69FA58302923}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B766BC5E-484B-4F1B-9B7E-69FA58302923}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B766BC5E-484B-4F1B-9B7E-69FA58302923}.Release|x64.ActiveCfg = Release|Any CPU
+ {B766BC5E-484B-4F1B-9B7E-69FA58302923}.Release|x64.Build.0 = Release|Any CPU
+ {B766BC5E-484B-4F1B-9B7E-69FA58302923}.Release|x86.ActiveCfg = Release|Any CPU
+ {B766BC5E-484B-4F1B-9B7E-69FA58302923}.Release|x86.Build.0 = Release|Any CPU
+ {59EC3582-12E8-4164-B40F-17B4D15B8F61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {59EC3582-12E8-4164-B40F-17B4D15B8F61}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {59EC3582-12E8-4164-B40F-17B4D15B8F61}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {59EC3582-12E8-4164-B40F-17B4D15B8F61}.Debug|x64.Build.0 = Debug|Any CPU
+ {59EC3582-12E8-4164-B40F-17B4D15B8F61}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {59EC3582-12E8-4164-B40F-17B4D15B8F61}.Debug|x86.Build.0 = Debug|Any CPU
+ {59EC3582-12E8-4164-B40F-17B4D15B8F61}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {59EC3582-12E8-4164-B40F-17B4D15B8F61}.Release|Any CPU.Build.0 = Release|Any CPU
+ {59EC3582-12E8-4164-B40F-17B4D15B8F61}.Release|x64.ActiveCfg = Release|Any CPU
+ {59EC3582-12E8-4164-B40F-17B4D15B8F61}.Release|x64.Build.0 = Release|Any CPU
+ {59EC3582-12E8-4164-B40F-17B4D15B8F61}.Release|x86.ActiveCfg = Release|Any CPU
+ {59EC3582-12E8-4164-B40F-17B4D15B8F61}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE