From f4a9d18ac1f5b67b30550da8e1391c6a08231236 Mon Sep 17 00:00:00 2001 From: Kasper Kristensen Date: Tue, 19 Mar 2024 14:44:58 +0100 Subject: [PATCH] Add web api for management. --- .../Abstractions/ICommand.cs | 7 ++ .../Abstractions/ICommandHandler.cs | 7 ++ .../Abstractions/IQuery.cs | 6 ++ .../Abstractions/IQueryHandler.cs | 6 ++ .../AssemblyReference.cs | 8 +++ .../Football.Management.Application.csproj | 18 +++++ .../CreatePlayer/CreatePlayerCommand.cs | 6 ++ .../CreatePlayer/CreatePlayerCommandHander.cs | 72 +++++++++++++++++++ .../GetPlayerById/GetPlayerByIdQuery.cs | 7 ++ .../GetPlayerByIdQueryHandler.cs | 28 ++++++++ .../Repositories/IPlayerRepository.cs | 10 +++ .../AssemblyReference.cs | 8 +++ .../Football.Management.Persistence.csproj | 13 ++++ .../Repositories/PlayerRepositoryInMemory.cs | 40 +++++++++++ .../AssemblyReference.cs | 8 +++ .../Football.Management.Presentation.csproj | 18 +++++ .../Players/Controllers/PlayersController.cs | 49 +++++++++++++ .../Players/Requests/CreatePlayerRequest.cs | 3 + .../Responses/GetPlayerByIdResponse.cs | 3 + .../Football.Management.Web.csproj | 21 ++++++ .../Football.Management.Web.http | 6 ++ Football.Management.Web/Program.cs | 34 +++++++++ .../Properties/launchSettings.json | 41 +++++++++++ .../appsettings.Development.json | 8 +++ Football.Management.Web/appsettings.json | 9 +++ Management.sln | 24 +++++++ 26 files changed, 460 insertions(+) create mode 100644 Football.Management.Application/Abstractions/ICommand.cs create mode 100644 Football.Management.Application/Abstractions/ICommandHandler.cs create mode 100644 Football.Management.Application/Abstractions/IQuery.cs create mode 100644 Football.Management.Application/Abstractions/IQueryHandler.cs create mode 100644 Football.Management.Application/AssemblyReference.cs create mode 100644 Football.Management.Application/Football.Management.Application.csproj create mode 100644 Football.Management.Application/Players/Commands/CreatePlayer/CreatePlayerCommand.cs create mode 100644 Football.Management.Application/Players/Commands/CreatePlayer/CreatePlayerCommandHander.cs create mode 100644 Football.Management.Application/Players/Queries/GetPlayerById/GetPlayerByIdQuery.cs create mode 100644 Football.Management.Application/Players/Queries/GetPlayerById/GetPlayerByIdQueryHandler.cs create mode 100644 Football.Management.Domain/Repositories/IPlayerRepository.cs create mode 100644 Football.Management.Persistence/AssemblyReference.cs create mode 100644 Football.Management.Persistence/Football.Management.Persistence.csproj create mode 100644 Football.Management.Persistence/Repositories/PlayerRepositoryInMemory.cs create mode 100644 Football.Management.Presentation/AssemblyReference.cs create mode 100644 Football.Management.Presentation/Football.Management.Presentation.csproj create mode 100644 Football.Management.Presentation/Players/Controllers/PlayersController.cs create mode 100644 Football.Management.Presentation/Players/Requests/CreatePlayerRequest.cs create mode 100644 Football.Management.Presentation/Players/Responses/GetPlayerByIdResponse.cs create mode 100644 Football.Management.Web/Football.Management.Web.csproj create mode 100644 Football.Management.Web/Football.Management.Web.http create mode 100644 Football.Management.Web/Program.cs create mode 100644 Football.Management.Web/Properties/launchSettings.json create mode 100644 Football.Management.Web/appsettings.Development.json create mode 100644 Football.Management.Web/appsettings.json diff --git a/Football.Management.Application/Abstractions/ICommand.cs b/Football.Management.Application/Abstractions/ICommand.cs new file mode 100644 index 0000000..56731db --- /dev/null +++ b/Football.Management.Application/Abstractions/ICommand.cs @@ -0,0 +1,7 @@ +using FluentResults; +using MediatR; + +namespace Football.Management.Application.Abstractions; + +public interface ICommand : IRequest {} +public interface ICommand : IRequest> {} diff --git a/Football.Management.Application/Abstractions/ICommandHandler.cs b/Football.Management.Application/Abstractions/ICommandHandler.cs new file mode 100644 index 0000000..6917ebc --- /dev/null +++ b/Football.Management.Application/Abstractions/ICommandHandler.cs @@ -0,0 +1,7 @@ +using FluentResults; +using MediatR; + +namespace Football.Management.Application.Abstractions; + +public interface ICommandHandler : IRequestHandler where TCommand : ICommand {} +public interface ICommandHandler : IRequestHandler> where TCommand : ICommand {} diff --git a/Football.Management.Application/Abstractions/IQuery.cs b/Football.Management.Application/Abstractions/IQuery.cs new file mode 100644 index 0000000..0e3c3d6 --- /dev/null +++ b/Football.Management.Application/Abstractions/IQuery.cs @@ -0,0 +1,6 @@ +using FluentResults; +using MediatR; + +namespace Football.Management.Application.Abstractions; + +public interface IQuery : IRequest> {} diff --git a/Football.Management.Application/Abstractions/IQueryHandler.cs b/Football.Management.Application/Abstractions/IQueryHandler.cs new file mode 100644 index 0000000..0876848 --- /dev/null +++ b/Football.Management.Application/Abstractions/IQueryHandler.cs @@ -0,0 +1,6 @@ +using FluentResults; +using MediatR; + +namespace Football.Management.Application.Abstractions; + +public interface IQueryHandler : IRequestHandler> where TQuery : IQuery {} \ No newline at end of file diff --git a/Football.Management.Application/AssemblyReference.cs b/Football.Management.Application/AssemblyReference.cs new file mode 100644 index 0000000..c5c0cec --- /dev/null +++ b/Football.Management.Application/AssemblyReference.cs @@ -0,0 +1,8 @@ +using System.Reflection; + +namespace Football.Management.Application; + +public class AssemblyReference +{ + public static readonly Assembly Assembly = typeof(AssemblyReference).Assembly; +} diff --git a/Football.Management.Application/Football.Management.Application.csproj b/Football.Management.Application/Football.Management.Application.csproj new file mode 100644 index 0000000..eca6746 --- /dev/null +++ b/Football.Management.Application/Football.Management.Application.csproj @@ -0,0 +1,18 @@ + + + + + + + + + + + + + net8.0 + enable + enable + + + diff --git a/Football.Management.Application/Players/Commands/CreatePlayer/CreatePlayerCommand.cs b/Football.Management.Application/Players/Commands/CreatePlayer/CreatePlayerCommand.cs new file mode 100644 index 0000000..b869f5d --- /dev/null +++ b/Football.Management.Application/Players/Commands/CreatePlayer/CreatePlayerCommand.cs @@ -0,0 +1,6 @@ +using Football.Management.Application.Abstractions; +using Football.Management.Domain.Identities; + +namespace Football.Management.Application.Players.Commands.CreatePlayer; + +public sealed record CreatePlayerCommand(string FirstName, string LastName, int AttackSkill, int MidfieldSkill, int DefenseSkill) : ICommand; diff --git a/Football.Management.Application/Players/Commands/CreatePlayer/CreatePlayerCommandHander.cs b/Football.Management.Application/Players/Commands/CreatePlayer/CreatePlayerCommandHander.cs new file mode 100644 index 0000000..07627af --- /dev/null +++ b/Football.Management.Application/Players/Commands/CreatePlayer/CreatePlayerCommandHander.cs @@ -0,0 +1,72 @@ +using FluentResults; +using Football.Management.Application.Abstractions; +using Football.Management.Domain.Entities; +using Football.Management.Domain.Identities; +using Football.Management.Domain.Repositories; +using Football.Management.Domain.ValueObjects; + +namespace Football.Management.Application.Players.Commands.CreatePlayer; + +internal sealed class CreatePlayerCommandHander : ICommandHandler +{ + private readonly IPlayerRepository _playerRepository; + + public CreatePlayerCommandHander(IPlayerRepository playerRepository) + { + _playerRepository = playerRepository; + } + + public async Task> Handle(CreatePlayerCommand request, CancellationToken cancellationToken) + { + var firstName = PlayerFirstName.Create(request.FirstName); + + if (firstName.IsFailed) + { + return firstName.ToResult(); + } + + var lastName = PlayerLastName.Create(request.LastName); + + if (lastName.IsFailed) + { + return lastName.ToResult(); + } + + var attackSkill = PlayerAttackSkill.Create(request.AttackSkill); + + if (attackSkill.IsFailed) + { + return attackSkill.ToResult(); + } + + var midfieldSkill = PlayerMidfieldSkill.Create(request.MidfieldSkill); + + if (midfieldSkill.IsFailed) + { + return midfieldSkill.ToResult(); + } + + var defenseSkill = PlayerDefenseSkill.Create(request.DefenseSkill); + + if (defenseSkill.IsFailed) + { + return defenseSkill.ToResult(); + } + + var player = Player.Create( + firstName.ValueOrDefault, + lastName.ValueOrDefault, + attackSkill.ValueOrDefault, + midfieldSkill.ValueOrDefault, + defenseSkill.ValueOrDefault); + + if (player.IsFailed) + { + return player.ToResult(); + } + + await _playerRepository.AddAsync(player.Value, cancellationToken); + + return player.Value.Id; + } +} diff --git a/Football.Management.Application/Players/Queries/GetPlayerById/GetPlayerByIdQuery.cs b/Football.Management.Application/Players/Queries/GetPlayerById/GetPlayerByIdQuery.cs new file mode 100644 index 0000000..fbf7e98 --- /dev/null +++ b/Football.Management.Application/Players/Queries/GetPlayerById/GetPlayerByIdQuery.cs @@ -0,0 +1,7 @@ +using Football.Management.Application.Abstractions; +using Football.Management.Domain.Entities; +using Football.Management.Domain.Identities; + +namespace Football.Management.Application.Players.Queries.GetPlayerById; + +public sealed record GetPlayerByIdQuery(PlayerId PlayerId) : IQuery; diff --git a/Football.Management.Application/Players/Queries/GetPlayerById/GetPlayerByIdQueryHandler.cs b/Football.Management.Application/Players/Queries/GetPlayerById/GetPlayerByIdQueryHandler.cs new file mode 100644 index 0000000..c9d8343 --- /dev/null +++ b/Football.Management.Application/Players/Queries/GetPlayerById/GetPlayerByIdQueryHandler.cs @@ -0,0 +1,28 @@ +using FluentResults; +using Football.Management.Application.Abstractions; +using Football.Management.Domain.Entities; +using Football.Management.Domain.Repositories; + +namespace Football.Management.Application.Players.Queries.GetPlayerById; + +public class GetPlayerByIdQueryHandler : IQueryHandler +{ + private readonly IPlayerRepository _playerRepository; + + public GetPlayerByIdQueryHandler(IPlayerRepository playerRepository) + { + _playerRepository = playerRepository; + } + + public async Task> Handle(GetPlayerByIdQuery request, CancellationToken cancellationToken) + { + var player = await _playerRepository.GetAsync(request.PlayerId, cancellationToken); + + if (player is null) + { + return Result.Fail("Player was not found."); + } + + return player; + } +} diff --git a/Football.Management.Domain/Repositories/IPlayerRepository.cs b/Football.Management.Domain/Repositories/IPlayerRepository.cs new file mode 100644 index 0000000..d247873 --- /dev/null +++ b/Football.Management.Domain/Repositories/IPlayerRepository.cs @@ -0,0 +1,10 @@ +using Football.Management.Domain.Entities; +using Football.Management.Domain.Identities; + +namespace Football.Management.Domain.Repositories; + +public interface IPlayerRepository +{ + Task AddAsync(Player player, CancellationToken cancellationToken); + Task GetAsync(PlayerId playerId, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/Football.Management.Persistence/AssemblyReference.cs b/Football.Management.Persistence/AssemblyReference.cs new file mode 100644 index 0000000..6fc11e9 --- /dev/null +++ b/Football.Management.Persistence/AssemblyReference.cs @@ -0,0 +1,8 @@ +using System.Reflection; + +namespace Football.Management.Persistence; + +public class AssemblyReference +{ + public static readonly Assembly Assembly = typeof(AssemblyReference).Assembly; +} diff --git a/Football.Management.Persistence/Football.Management.Persistence.csproj b/Football.Management.Persistence/Football.Management.Persistence.csproj new file mode 100644 index 0000000..be4a95d --- /dev/null +++ b/Football.Management.Persistence/Football.Management.Persistence.csproj @@ -0,0 +1,13 @@ + + + + + + + + net8.0 + enable + enable + + + diff --git a/Football.Management.Persistence/Repositories/PlayerRepositoryInMemory.cs b/Football.Management.Persistence/Repositories/PlayerRepositoryInMemory.cs new file mode 100644 index 0000000..bf23708 --- /dev/null +++ b/Football.Management.Persistence/Repositories/PlayerRepositoryInMemory.cs @@ -0,0 +1,40 @@ +using Football.Management.Domain.Entities; +using Football.Management.Domain.Identities; +using Football.Management.Domain.Repositories; +using Football.Management.Domain.ValueObjects; + +namespace Football.Management.Persistence.Repositories; + +public sealed class PlayerRepositoryInMemory : IPlayerRepository +{ + private List _players = new(); + + public PlayerRepositoryInMemory() + { + var player = Player.Create( + PlayerFirstName.Create("Kamil").ValueOrDefault, + PlayerLastName.Create("Grabara").ValueOrDefault, + PlayerAttackSkill.Create(0).ValueOrDefault, + PlayerMidfieldSkill.Create(5).ValueOrDefault, + PlayerDefenseSkill.Create(10).ValueOrDefault); + + _players.Add(player.Value); + } + + public async Task AddAsync(Player player, CancellationToken cancellationToken) + { + var result = await GetAsync(player.Id, cancellationToken); + + if (result is not null) + { + return; + } + + _players.Add(player); + } + + public Task GetAsync(PlayerId playerId, CancellationToken cancellationToken) + { + return Task.FromResult(_players.FirstOrDefault(i => i.Id.Id == playerId.Id)); + } +} \ No newline at end of file diff --git a/Football.Management.Presentation/AssemblyReference.cs b/Football.Management.Presentation/AssemblyReference.cs new file mode 100644 index 0000000..97c7288 --- /dev/null +++ b/Football.Management.Presentation/AssemblyReference.cs @@ -0,0 +1,8 @@ +using System.Reflection; + +namespace Football.Management.Presentation; + +public class AssemblyReference +{ + public static readonly Assembly Assembly = typeof(AssemblyReference).Assembly; +} diff --git a/Football.Management.Presentation/Football.Management.Presentation.csproj b/Football.Management.Presentation/Football.Management.Presentation.csproj new file mode 100644 index 0000000..d6dd7a8 --- /dev/null +++ b/Football.Management.Presentation/Football.Management.Presentation.csproj @@ -0,0 +1,18 @@ + + + + + + + + net8.0 + enable + enable + + + + + + + + diff --git a/Football.Management.Presentation/Players/Controllers/PlayersController.cs b/Football.Management.Presentation/Players/Controllers/PlayersController.cs new file mode 100644 index 0000000..131d83a --- /dev/null +++ b/Football.Management.Presentation/Players/Controllers/PlayersController.cs @@ -0,0 +1,49 @@ +using Microsoft.AspNetCore.Mvc; +using MediatR; +using Football.Management.Application.Players.Commands.CreatePlayer; +using Football.Management.Presentation.Players.Requests; +using Football.Management.Domain.Identities; +using Football.Management.Application.Players.Queries.GetPlayerById; +using Football.Management.Presentation.Players.Responses; + +namespace Football.Management.Presentation.Players.Controllers; + +[ApiController] +[Route("api/players")] +public sealed class PlayersController : ControllerBase +{ + private readonly ISender _sender; + + public PlayersController(ISender sender) + { + _sender = sender; + } + + [HttpPost] + public async Task CreatePlayer(CreatePlayerRequest request, CancellationToken cancellationToken) + { + var command = new CreatePlayerCommand(request.FirstName, request.LastName, request.AttackSkill, request.MidfieldSkill, request.DefenseSkill); + var result = await _sender.Send(command, cancellationToken); + return result.IsSuccess ? Ok(result.Value) : BadRequest(result.Errors); + } + + [HttpGet("{playerId}")] + public async Task GetPlayerById(Guid playerId, CancellationToken cancellationToken) + { + var query = new GetPlayerByIdQuery(new PlayerId(playerId)); + var result = await _sender.Send(query, cancellationToken); + if (result.IsSuccess) + { + var player = result.Value; + var response = new GetPlayerByIdResponse( + player.Id.Id, + player.FirstName.Value, + player.LastName.Value, + player.AttackSkill.Value, + player.MidfieldSkill.Value, + player.DefenseSkill.Value); + return Ok(response); + } + return NotFound(result.Errors); + } +} diff --git a/Football.Management.Presentation/Players/Requests/CreatePlayerRequest.cs b/Football.Management.Presentation/Players/Requests/CreatePlayerRequest.cs new file mode 100644 index 0000000..8cafdb3 --- /dev/null +++ b/Football.Management.Presentation/Players/Requests/CreatePlayerRequest.cs @@ -0,0 +1,3 @@ +namespace Football.Management.Presentation.Players.Requests; + +public sealed record CreatePlayerRequest(string FirstName, string LastName, int AttackSkill, int MidfieldSkill, int DefenseSkill); \ No newline at end of file diff --git a/Football.Management.Presentation/Players/Responses/GetPlayerByIdResponse.cs b/Football.Management.Presentation/Players/Responses/GetPlayerByIdResponse.cs new file mode 100644 index 0000000..17f0964 --- /dev/null +++ b/Football.Management.Presentation/Players/Responses/GetPlayerByIdResponse.cs @@ -0,0 +1,3 @@ +namespace Football.Management.Presentation.Players.Responses; + +public sealed record GetPlayerByIdResponse(Guid Id, string FirstName, string LastName, int AttackSkill, int MidfieldSkill, int DefenseSkill); diff --git a/Football.Management.Web/Football.Management.Web.csproj b/Football.Management.Web/Football.Management.Web.csproj new file mode 100644 index 0000000..220ffd5 --- /dev/null +++ b/Football.Management.Web/Football.Management.Web.csproj @@ -0,0 +1,21 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + + + diff --git a/Football.Management.Web/Football.Management.Web.http b/Football.Management.Web/Football.Management.Web.http new file mode 100644 index 0000000..eac297a --- /dev/null +++ b/Football.Management.Web/Football.Management.Web.http @@ -0,0 +1,6 @@ +@Football.Management.Web_HostAddress = http://localhost:5137 + +GET {{Football.Management.Web_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/Football.Management.Web/Program.cs b/Football.Management.Web/Program.cs new file mode 100644 index 0000000..6db8d8a --- /dev/null +++ b/Football.Management.Web/Program.cs @@ -0,0 +1,34 @@ +using Football.Management.Domain.Repositories; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +// builder +// .Services +// .Scan( +// selector => selector +// .FromAssemblies( +// // Infrastructure.AssemblyReference.Assembly, +// Football.Management.Persistence.AssemblyReference.Assembly) +// .AddClasses(false) +// .AsImplementedInterfaces() +// .WithScopedLifetime()); +builder.Services.AddSingleton(); +builder.Services.AddMediatR(conf => conf.RegisterServicesFromAssembly(Football.Management.Application.AssemblyReference.Assembly)); +builder.Services.AddControllers().AddApplicationPart(Football.Management.Presentation.AssemblyReference.Assembly); +builder.Services.AddSwaggerGen(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseHttpsRedirection(); +app.MapControllers(); +app.Run(); diff --git a/Football.Management.Web/Properties/launchSettings.json b/Football.Management.Web/Properties/launchSettings.json new file mode 100644 index 0000000..22bb0f0 --- /dev/null +++ b/Football.Management.Web/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:38230", + "sslPort": 44384 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5137", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7076;http://localhost:5137", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Football.Management.Web/appsettings.Development.json b/Football.Management.Web/appsettings.Development.json new file mode 100644 index 0000000..ff66ba6 --- /dev/null +++ b/Football.Management.Web/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Football.Management.Web/appsettings.json b/Football.Management.Web/appsettings.json new file mode 100644 index 0000000..4d56694 --- /dev/null +++ b/Football.Management.Web/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/Management.sln b/Management.sln index 111198c..db368d3 100644 --- a/Management.sln +++ b/Management.sln @@ -7,6 +7,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Football.Management.Domain" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Football.Management.Domain.Test", "Football.Management.Domain.Test\Football.Management.Domain.Test.csproj", "{3CC56C31-6C3F-46B7-9700-4FB39FE65D1E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Football.Management.Application", "Football.Management.Application\Football.Management.Application.csproj", "{70BF5828-437C-48F5-A3DD-36B49B0B22E4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Football.Management.Persistence", "Football.Management.Persistence\Football.Management.Persistence.csproj", "{F5308157-053D-4722-AD0E-3570FF224ECE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Football.Management.Presentation", "Football.Management.Presentation\Football.Management.Presentation.csproj", "{9016AF8A-0830-441E-83D8-F6600BB21B9E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Football.Management.Web", "Football.Management.Web\Football.Management.Web.csproj", "{55945F70-9E95-4760-BCE4-E6F42AE9A29D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -24,5 +32,21 @@ Global {3CC56C31-6C3F-46B7-9700-4FB39FE65D1E}.Debug|Any CPU.Build.0 = Debug|Any CPU {3CC56C31-6C3F-46B7-9700-4FB39FE65D1E}.Release|Any CPU.ActiveCfg = Release|Any CPU {3CC56C31-6C3F-46B7-9700-4FB39FE65D1E}.Release|Any CPU.Build.0 = Release|Any CPU + {70BF5828-437C-48F5-A3DD-36B49B0B22E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {70BF5828-437C-48F5-A3DD-36B49B0B22E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {70BF5828-437C-48F5-A3DD-36B49B0B22E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {70BF5828-437C-48F5-A3DD-36B49B0B22E4}.Release|Any CPU.Build.0 = Release|Any CPU + {F5308157-053D-4722-AD0E-3570FF224ECE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F5308157-053D-4722-AD0E-3570FF224ECE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F5308157-053D-4722-AD0E-3570FF224ECE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F5308157-053D-4722-AD0E-3570FF224ECE}.Release|Any CPU.Build.0 = Release|Any CPU + {9016AF8A-0830-441E-83D8-F6600BB21B9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9016AF8A-0830-441E-83D8-F6600BB21B9E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9016AF8A-0830-441E-83D8-F6600BB21B9E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9016AF8A-0830-441E-83D8-F6600BB21B9E}.Release|Any CPU.Build.0 = Release|Any CPU + {55945F70-9E95-4760-BCE4-E6F42AE9A29D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55945F70-9E95-4760-BCE4-E6F42AE9A29D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55945F70-9E95-4760-BCE4-E6F42AE9A29D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55945F70-9E95-4760-BCE4-E6F42AE9A29D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal