From 09321fa9e771f8a65092838a29e4437f380ddda2 Mon Sep 17 00:00:00 2001 From: Ryan Weavers Date: Sun, 11 Jan 2026 21:02:35 +0000 Subject: [PATCH 1/3] submitted for review --- .../ECommerceApp.ConsoleClient.csproj | 12 + ConsoleClient/Handlers/CategoryMenuHandler.cs | 399 +++++++++++++ .../Handlers/ProductMenuHandler.Models.cs | 56 ++ ConsoleClient/Handlers/ProductMenuHandler.cs | 537 ++++++++++++++++++ ConsoleClient/Handlers/SalesMenuHandler.cs | 463 +++++++++++++++ ConsoleClient/Helpers/ConsoleInputHelper.cs | 143 +++++ .../Interfaces/IConsoleMenuHandler.cs | 20 + ConsoleClient/Models/CategoryListQuery.cs | 13 + ConsoleClient/Models/PaginationState.cs | 34 ++ ConsoleClient/Models/SaleListQuery.cs | 16 + ConsoleClient/Program.cs | 175 ++++++ ConsoleClient/Utilities/ApiClient.cs | 183 ++++++ ConsoleClient/Utilities/MenuActionHandler.cs | 30 + ConsoleClient/Utilities/QueryStringBuilder.cs | 38 ++ ConsoleClient/Utilities/TableRenderer.cs | 479 ++++++++++++++++ ConsoleClient/packages.lock.json | 13 + Controllers/CategoriesController.cs | 169 ++++++ Controllers/ControllerResponseExtensions.cs | 27 + Controllers/ProductController.cs | 178 ++++++ Controllers/SalesController.cs | 155 +++++ Data/Configuration/IndexConfiguration.cs | 78 +++ Data/Configuration/ModelConfiguration.cs | 120 ++++ Data/Configurations/CategoryConfiguration.cs | 34 ++ Data/Configurations/ProductConfiguration.cs | 32 ++ Data/Configurations/SaleConfiguration.cs | 24 + Data/Configurations/SaleItemConfiguration.cs | 16 + Data/DTO/ApiRequestDto.cs | 8 + Data/DTO/ApiResponseDto.cs | 53 ++ Data/DTO/CategoryQueryParameters.cs | 30 + Data/DTO/PaginatedResponseDto.cs | 61 ++ Data/DTO/ProductQueryParameters.cs | 30 + Data/DTO/SaleQueryParameters.cs | 32 ++ Data/DTO/SalesSummaryDto.cs | 10 + Data/ECommerceDbContext.cs | 35 ++ Data/ECommerceDbContext.cs.backup | 309 ++++++++++ .../20250924200106_RemoveQuantityFromSales.cs | 92 +++ .../20250924200514_CreateSaleItemsTable.cs | 52 ++ Data/Models/BaseEntity.cs | 27 + Data/Models/Category.cs | 40 ++ Data/Models/Product.cs | 54 ++ Data/Models/Sale.cs | 55 ++ Data/Models/SaleItem.cs | 48 ++ Data/Models/SalesSummary.cs | 33 ++ Data/Seeding/DatabaseSeeder.cs | 279 +++++++++ ECommerceApp.RyanW84.sln | 73 +++ Hosting/DatabaseInitializerHostedService.cs | 59 ++ .../Helpers/ICategoryProcessingHelper.cs | 13 + .../Helpers/IProductProcessingHelper.cs | 11 + Interfaces/Helpers/IProductQueryHelper.cs | 8 + Interfaces/Helpers/ISaleProcessingHelper.cs | 21 + Interfaces/Helpers/ISaleQueryHelper.cs | 11 + Interfaces/ICategoryRepository.cs | 41 ++ Interfaces/ICategoryService.cs | 40 ++ Interfaces/IProductRepository.cs | 33 ++ Interfaces/IProductService.cs | 16 + Interfaces/IRepository.cs | 12 + Interfaces/ISaleRepository.cs | 32 ++ Interfaces/ISaleService.cs | 24 + Interfaces/ISalesSummaryService.cs | 8 + LICENSE | 21 + .../GlobalExceptionHandlerMiddleware.cs | 113 ++++ Options/ScalarUiOptions.cs | 14 + Program.cs | 314 ++++++++++ Properties/launchSettings.json | 13 + Properties/serviceDependencies.json | 12 + Properties/serviceDependencies.local.json | 13 + Repositories/CategoryRepository.cs | 502 ++++++++++++++++ Repositories/CompiledQueries.cs | 68 +++ Repositories/ProductRepository.cs | 513 +++++++++++++++++ Repositories/SaleRepository.cs | 423 ++++++++++++++ Services/CategoryService.cs | 240 ++++++++ Services/Helpers/CategoryProcessingHelper.cs | 40 ++ Services/Helpers/InputSanitizationHelper.cs | 98 ++++ Services/Helpers/ProductProcessingHelper.cs | 36 ++ Services/Helpers/ProductQueryHelper.cs | 15 + Services/Helpers/SaleProcessingHelper.cs | 141 +++++ Services/Helpers/SaleQueryHelper.cs | 33 ++ Services/ProductService.cs | 256 +++++++++ Services/SaleService.cs | 185 ++++++ Services/SalesSummaryService.cs | 43 ++ appsettings.Development.json | 13 + appsettings.json | 18 + dotnet-tools.json | 13 + global.json | 6 + .../ECommerceApp.ArchitectureTests.csproj | 25 + tests/ArchitectureTests/SmokeTests.cs | 12 + 86 files changed, 8274 insertions(+) create mode 100644 ConsoleClient/ECommerceApp.ConsoleClient.csproj create mode 100644 ConsoleClient/Handlers/CategoryMenuHandler.cs create mode 100644 ConsoleClient/Handlers/ProductMenuHandler.Models.cs create mode 100644 ConsoleClient/Handlers/ProductMenuHandler.cs create mode 100644 ConsoleClient/Handlers/SalesMenuHandler.cs create mode 100644 ConsoleClient/Helpers/ConsoleInputHelper.cs create mode 100644 ConsoleClient/Interfaces/IConsoleMenuHandler.cs create mode 100644 ConsoleClient/Models/CategoryListQuery.cs create mode 100644 ConsoleClient/Models/PaginationState.cs create mode 100644 ConsoleClient/Models/SaleListQuery.cs create mode 100644 ConsoleClient/Program.cs create mode 100644 ConsoleClient/Utilities/ApiClient.cs create mode 100644 ConsoleClient/Utilities/MenuActionHandler.cs create mode 100644 ConsoleClient/Utilities/QueryStringBuilder.cs create mode 100644 ConsoleClient/Utilities/TableRenderer.cs create mode 100644 ConsoleClient/packages.lock.json create mode 100644 Controllers/CategoriesController.cs create mode 100644 Controllers/ControllerResponseExtensions.cs create mode 100644 Controllers/ProductController.cs create mode 100644 Controllers/SalesController.cs create mode 100644 Data/Configuration/IndexConfiguration.cs create mode 100644 Data/Configuration/ModelConfiguration.cs create mode 100644 Data/Configurations/CategoryConfiguration.cs create mode 100644 Data/Configurations/ProductConfiguration.cs create mode 100644 Data/Configurations/SaleConfiguration.cs create mode 100644 Data/Configurations/SaleItemConfiguration.cs create mode 100644 Data/DTO/ApiRequestDto.cs create mode 100644 Data/DTO/ApiResponseDto.cs create mode 100644 Data/DTO/CategoryQueryParameters.cs create mode 100644 Data/DTO/PaginatedResponseDto.cs create mode 100644 Data/DTO/ProductQueryParameters.cs create mode 100644 Data/DTO/SaleQueryParameters.cs create mode 100644 Data/DTO/SalesSummaryDto.cs create mode 100644 Data/ECommerceDbContext.cs create mode 100644 Data/ECommerceDbContext.cs.backup create mode 100644 Data/Migrations/20250924200106_RemoveQuantityFromSales.cs create mode 100644 Data/Migrations/20250924200514_CreateSaleItemsTable.cs create mode 100644 Data/Models/BaseEntity.cs create mode 100644 Data/Models/Category.cs create mode 100644 Data/Models/Product.cs create mode 100644 Data/Models/Sale.cs create mode 100644 Data/Models/SaleItem.cs create mode 100644 Data/Models/SalesSummary.cs create mode 100644 Data/Seeding/DatabaseSeeder.cs create mode 100644 ECommerceApp.RyanW84.sln create mode 100644 Hosting/DatabaseInitializerHostedService.cs create mode 100644 Interfaces/Helpers/ICategoryProcessingHelper.cs create mode 100644 Interfaces/Helpers/IProductProcessingHelper.cs create mode 100644 Interfaces/Helpers/IProductQueryHelper.cs create mode 100644 Interfaces/Helpers/ISaleProcessingHelper.cs create mode 100644 Interfaces/Helpers/ISaleQueryHelper.cs create mode 100644 Interfaces/ICategoryRepository.cs create mode 100644 Interfaces/ICategoryService.cs create mode 100644 Interfaces/IProductRepository.cs create mode 100644 Interfaces/IProductService.cs create mode 100644 Interfaces/IRepository.cs create mode 100644 Interfaces/ISaleRepository.cs create mode 100644 Interfaces/ISaleService.cs create mode 100644 Interfaces/ISalesSummaryService.cs create mode 100644 LICENSE create mode 100644 Middleware/GlobalExceptionHandlerMiddleware.cs create mode 100644 Options/ScalarUiOptions.cs create mode 100644 Program.cs create mode 100644 Properties/launchSettings.json create mode 100644 Properties/serviceDependencies.json create mode 100644 Properties/serviceDependencies.local.json create mode 100644 Repositories/CategoryRepository.cs create mode 100644 Repositories/CompiledQueries.cs create mode 100644 Repositories/ProductRepository.cs create mode 100644 Repositories/SaleRepository.cs create mode 100644 Services/CategoryService.cs create mode 100644 Services/Helpers/CategoryProcessingHelper.cs create mode 100644 Services/Helpers/InputSanitizationHelper.cs create mode 100644 Services/Helpers/ProductProcessingHelper.cs create mode 100644 Services/Helpers/ProductQueryHelper.cs create mode 100644 Services/Helpers/SaleProcessingHelper.cs create mode 100644 Services/Helpers/SaleQueryHelper.cs create mode 100644 Services/ProductService.cs create mode 100644 Services/SaleService.cs create mode 100644 Services/SalesSummaryService.cs create mode 100644 appsettings.Development.json create mode 100644 appsettings.json create mode 100644 dotnet-tools.json create mode 100644 global.json create mode 100644 tests/ArchitectureTests/ECommerceApp.ArchitectureTests.csproj create mode 100644 tests/ArchitectureTests/SmokeTests.cs diff --git a/ConsoleClient/ECommerceApp.ConsoleClient.csproj b/ConsoleClient/ECommerceApp.ConsoleClient.csproj new file mode 100644 index 00000000..0c829917 --- /dev/null +++ b/ConsoleClient/ECommerceApp.ConsoleClient.csproj @@ -0,0 +1,12 @@ + + + Exe + net10.0 + enable + enable + + + + + + diff --git a/ConsoleClient/Handlers/CategoryMenuHandler.cs b/ConsoleClient/Handlers/CategoryMenuHandler.cs new file mode 100644 index 00000000..84493c74 --- /dev/null +++ b/ConsoleClient/Handlers/CategoryMenuHandler.cs @@ -0,0 +1,399 @@ +using ECommerceApp.ConsoleClient.Helpers; +using ECommerceApp.ConsoleClient.Interfaces; +using ECommerceApp.ConsoleClient.Models; +using ECommerceApp.ConsoleClient.Utilities; +using Spectre.Console; + +namespace ECommerceApp.ConsoleClient.Handlers; + +/// +/// Handles category menu operations. +/// Encapsulates all category-related UI logic following Single Responsibility Principle. +/// +public class CategoryMenuHandler : IConsoleMenuHandler +{ + public string MenuName => "Categories"; + + public async Task ExecuteAsync(HttpClient http) + { + var actions = new Dictionary> + { + { "List", ListAsync }, + { "Get by Id", GetByIdAsync }, + { "Get by Name", GetByNameAsync }, + { "Create", CreateAsync }, + { "Update", UpdateAsync }, + { "Delete", DeleteAsync }, + }; + + while (true) + { + var choice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("[green]Categories[/] — Choose an action:") + .AddChoices(actions.Keys.Concat(new[] { "Back" }).ToList()) + ); + + if (choice == "Back") + return; + + if (actions.TryGetValue(choice, out var action)) + await action(http); + } + } + + private static async Task ListAsync(HttpClient http) + { + var pageSize = ConsoleInputHelper.PromptPositiveInt("Items per page"); + + // Ask if user wants to apply filters + var applyFilters = AnsiConsole.Confirm("Apply filters?", false); + + string? search = null; + string? sortBy = null; + string? sortDirection = null; + + if (applyFilters) + { + search = ConsoleInputHelper.PromptOptional("Search"); + var (sortByResult, sortDirectionResult) = PromptSortOptions( + new[] { "(none)", "name", "createdat" } + ); + sortBy = sortByResult; + sortDirection = sortDirectionResult; + } + + var query = new CategoryListQuery + { + Page = 1, + PageSize = pageSize, + Search = search, + SortBy = sortBy, + SortDirection = sortDirection, + }; + + await BuildAndExecuteListQueryWithPagination(http, query); + } + + private static async Task BuildAndExecuteListQueryWithPagination( + HttpClient http, + CategoryListQuery query + ) + { + while (true) + { + var response = await FetchCategoryPageAsync(http, query); + if (!HasCategoryResults(response)) + { + AnsiConsole.MarkupLine("[yellow]No categories found[/]"); + break; + } + + var pagination = new PaginationState + { + CurrentPage = query.Page, + PageSize = query.PageSize, + TotalCount = response!.TotalCount, + }; + + TableRenderer.DisplayTable( + response.Data!, + $"Categories (Page {pagination.CurrentPage}/{pagination.TotalPages}, Total: {pagination.TotalCount})", + pagination.IndexOffset + ); + + if (!HandlePaginationNavigation(query, pagination)) + break; + } + } + + private static async Task?> FetchCategoryPageAsync( + HttpClient http, + CategoryListQuery query + ) + { + var qs = new QueryStringBuilder() + .Add("page", query.Page.ToString()) + .Add("pageSize", query.PageSize.ToString()) + .Add("search", query.Search) + .Add("sortBy", query.SortBy == "(none)" || query.SortBy == null ? null : query.SortBy) + .Add("sortDirection", query.SortDirection) + .Build(); + + return await ApiClient.FetchPaginatedAsync(http, $"/api/categories{qs}"); + } + + private static bool HandlePaginationNavigation( + CategoryListQuery query, + PaginationState pagination + ) + { + var navChoice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("Navigation:") + .AddChoices(pagination.GetNavigationChoices()) + ); + + return navChoice switch + { + "Back to Menu" => false, + "Next Page" => IncrementPage(query), + "Previous Page" => DecrementPage(query), + "Jump to Page" => JumpToPage(query, pagination), + _ => true, + }; + } + + private static bool JumpToPage(CategoryListQuery query, PaginationState pagination) + { + int targetPage = ConsoleInputHelper.PromptPositiveInt( + $"Enter page number (1-{pagination.TotalPages})" + ); + if (targetPage >= 1 && targetPage <= pagination.TotalPages) + { + query.Page = targetPage; + return true; + } + + AnsiConsole.MarkupLine( + $"[red]Invalid page number. Valid range: 1-{pagination.TotalPages}[/]" + ); + return true; + } + + private static bool IncrementPage(CategoryListQuery query) + { + query.Page++; + return true; + } + + private static bool DecrementPage(CategoryListQuery query) + { + query.Page--; + return true; + } + + private static bool HasCategoryResults(PaginatedResponse? response) + { + return response?.Data != null && response.Data.Count > 0; + } + + private static async Task GetByIdAsync(HttpClient http) + { + var response = await ApiClient.FetchPaginatedAsync( + http, + "/api/categories?page=1&pageSize=32" + ); + if (response?.Data == null || response.Data.Count == 0) + { + AnsiConsole.MarkupLine("[yellow]No categories available[/]"); + return; + } + + var selected = await TableRenderer.SelectFromPromptAsync( + async (pageNum) => + { + var pageResponse = await ApiClient.FetchPaginatedAsync( + http, + $"/api/categories?page={pageNum}&pageSize=32" + ); + return pageResponse?.Data ?? new List(); + }, + response.TotalCount, + 32, + "Select a Category", + cat => cat.Name + ); + + if (selected != null) + { + TableRenderer.DisplayTable(new[] { selected }.ToList(), "Category Details"); + } + } + + private static async Task GetByNameAsync(HttpClient http) + { + var name = ConsoleInputHelper.PromptRequired("Name"); + var response = await ApiClient.FetchEntityAsync( + http, + $"/api/categories/name/{Uri.EscapeDataString(name)}" + ); + if (response?.Data != null) + { + TableRenderer.DisplayTable(new[] { response.Data }.ToList(), "Category Details"); + } + else + { + AnsiConsole.MarkupLine("[yellow]Category not found[/]"); + } + } + + private static async Task CreateAsync(HttpClient http) + { + var name = ConsoleInputHelper.PromptRequired("Category Name"); + var description = ConsoleInputHelper.PromptRequired("Description"); + + var category = new { name, description }; + var payload = new { payload = category }; + await ApiClient.PostAsync(http, "/api/categories", payload); + } + + private static async Task UpdateAsync(HttpClient http) + { + var response = await ApiClient.FetchPaginatedAsync( + http, + "/api/categories?page=1&pageSize=32" + ); + if (!HasCategoryResults(response)) + { + AnsiConsole.MarkupLine("[yellow]No categories available[/]"); + return; + } + + var selected = await SelectCategoryAsync(http, response!.TotalCount); + if (selected == null) + return; + + var current = await FetchCategoryDetailsAsync(http, selected.CategoryId); + if (current == null) + { + ConsoleInputHelper.DisplayError("Category not found"); + return; + } + + DisplayCurrentCategory(current); + var (name, description) = PromptCategoryUpdates(current); + await ApiClient.PutAsync( + http, + $"/api/categories/{selected.CategoryId}", + BuildCategoryUpdatePayload(selected.CategoryId, name, description) + ); + } + + private static async Task FetchCategoryDetailsAsync( + HttpClient http, + int categoryId + ) + { + var categoryResponse = await ApiClient.FetchEntityAsync( + http, + $"/api/categories/{categoryId}" + ); + return categoryResponse?.Data; + } + + private static async Task SelectCategoryAsync(HttpClient http, int totalCount) + { + return await TableRenderer.SelectFromPromptAsync( + async pageNum => + { + var pageResponse = await ApiClient.FetchPaginatedAsync( + http, + $"/api/categories?page={pageNum}&pageSize=32" + ); + return pageResponse?.Data ?? new List(); + }, + totalCount, + 32, + "Select a Category to Update", + cat => cat.Name + ); + } + + private static void DisplayCurrentCategory(CategoryDto current) + { + AnsiConsole.MarkupLine("[yellow]Current values:[/]"); + AnsiConsole.MarkupLine($" Name: {current.Name}"); + AnsiConsole.MarkupLine($" Description: {current.Description}"); + } + + private static (string? Name, string? Description) PromptCategoryUpdates(CategoryDto current) + { + var name = PromptOptionalField("Category Name", current.Name); + var description = PromptOptionalField("Description", current.Description); + return (name, description); + } + + private static object BuildCategoryUpdatePayload( + int categoryId, + string? name, + string? description + ) + { + return new + { + payload = new + { + categoryId, + name, + description, + }, + }; + } + + private static async Task DeleteAsync(HttpClient http) + { + var response = await ApiClient.FetchPaginatedAsync( + http, + "/api/categories?page=1&pageSize=32" + ); + if (response?.Data == null || response.Data.Count == 0) + { + AnsiConsole.MarkupLine("[yellow]No categories available[/]"); + return; + } + + var selected = await TableRenderer.SelectFromPromptAsync( + async (pageNum) => + { + var pageResponse = await ApiClient.FetchPaginatedAsync( + http, + $"/api/categories?page={pageNum}&pageSize=32" + ); + return pageResponse?.Data ?? new List(); + }, + response.TotalCount, + 32, + "Select a Category to Delete", + cat => cat.Name + ); + + if (selected == null) + return; + + if (!AnsiConsole.Confirm($"[red]Are you sure you want to delete '{selected.Name}'?[/]")) + return; + + await ApiClient.DeleteAsync(http, $"/api/categories/{selected.CategoryId}"); + } + + private static string? PromptOptionalField(string label, string? currentValue) + { + var input = AnsiConsole.Ask($"{label} (leave blank to keep):", string.Empty); + return string.IsNullOrWhiteSpace(input) ? currentValue : input; + } + + private static (string SortBy, string? SortDirection) PromptSortOptions(string[] options) + { + var sortBy = AnsiConsole.Prompt( + new SelectionPrompt().Title("Sort By (optional)").AddChoices(options) + ); + + string? sortDirection = null; + if (sortBy != "(none)") + { + sortDirection = AnsiConsole.Prompt( + new SelectionPrompt().Title("Sort Direction").AddChoices("asc", "desc") + ); + } + + return (sortBy, sortDirection); + } + + private sealed record CategoryDto + { + public int CategoryId { get; init; } + public string Name { get; init; } = string.Empty; + public string? Description { get; init; } + } +} diff --git a/ConsoleClient/Handlers/ProductMenuHandler.Models.cs b/ConsoleClient/Handlers/ProductMenuHandler.Models.cs new file mode 100644 index 00000000..876f68b9 --- /dev/null +++ b/ConsoleClient/Handlers/ProductMenuHandler.Models.cs @@ -0,0 +1,56 @@ +namespace ECommerceApp.ConsoleClient.Handlers; + +public partial class ProductMenuHandler +{ + private sealed record ProductListQuery + { + public int Page { get; set; } + public int PageSize { get; set; } + public string? Search { get; set; } + public decimal? MinPrice { get; set; } + public decimal? MaxPrice { get; set; } + public int? CategoryId { get; set; } + public string SortBy { get; set; } = "(none)"; + public string? SortDirection { get; set; } + } + + private sealed record ProductFilters + { + public static ProductFilters Empty => new() { SortBy = "(none)" }; + + public string? Search { get; init; } + public decimal? MinPrice { get; init; } + public decimal? MaxPrice { get; init; } + public int? CategoryId { get; init; } + public string SortBy { get; init; } = "(none)"; + public string? SortDirection { get; init; } + } + + private sealed record ProductUpdate + { + public string? Name { get; init; } + public string? Description { get; init; } + public decimal Price { get; init; } + public int Stock { get; init; } + public int CategoryId { get; init; } + public bool IsActive { get; init; } + } + + private sealed record CategoryDto + { + public int CategoryId { get; init; } + public string Name { get; init; } = string.Empty; + public string? Description { get; init; } + } + + private sealed record ProductDto + { + public int ProductId { get; init; } + public string Name { get; init; } = string.Empty; + public string? Description { get; init; } + public decimal Price { get; init; } + public int Stock { get; init; } + public bool IsActive { get; init; } + public int CategoryId { get; init; } + } +} diff --git a/ConsoleClient/Handlers/ProductMenuHandler.cs b/ConsoleClient/Handlers/ProductMenuHandler.cs new file mode 100644 index 00000000..b80df544 --- /dev/null +++ b/ConsoleClient/Handlers/ProductMenuHandler.cs @@ -0,0 +1,537 @@ +using ECommerceApp.ConsoleClient.Helpers; +using ECommerceApp.ConsoleClient.Interfaces; +using ECommerceApp.ConsoleClient.Models; +using ECommerceApp.ConsoleClient.Utilities; +using Spectre.Console; + +namespace ECommerceApp.ConsoleClient.Handlers; + +/// +/// Handles product menu operations. +/// Encapsulates all product-related UI logic following Single Responsibility Principle. +/// +public partial class ProductMenuHandler : IConsoleMenuHandler +{ + public string MenuName => "Products"; + + public async Task ExecuteAsync(HttpClient http) + { + var actions = new Dictionary> + { + { "List", ListAsync }, + { "Get by Id", GetByIdAsync }, + { "By Category", GetByCategoryAsync }, + { "Create", CreateAsync }, + { "Update", UpdateAsync }, + { "Delete", DeleteAsync }, + }; + + while (true) + { + var choice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("[green]Products[/] — Choose an action:") + .AddChoices(actions.Keys.Concat(new[] { "Back" }).ToList()) + ); + + if (choice == "Back") + return; + + if (actions.TryGetValue(choice, out var action)) + await action(http); + } + } + + private static async Task GetByIdAsync(HttpClient http) + { + // First, show a list for the user to select from + var response = await ApiClient.FetchPaginatedAsync( + http, + "/api/product?page=1&pageSize=32" + ); + if (response?.Data == null || response.Data.Count == 0) + { + AnsiConsole.MarkupLine("[yellow]No products available[/]"); + return; + } + + var selected = await TableRenderer.SelectFromPromptAsync( + async (pageNum) => + { + var pageResponse = await ApiClient.FetchPaginatedAsync( + http, + $"/api/product?page={pageNum}&pageSize=32" + ); + return pageResponse?.Data ?? new List(); + }, + response.TotalCount, + 32, + "Select a Product", + product => product.Name + ); + + if (selected != null) + { + // Display the selected product directly from the list + TableRenderer.DisplayTable( + new[] { selected }.ToList(), + "Product Details", + 0, + "CategoryId" + ); + } + } + + private static async Task GetByCategoryAsync(HttpClient http) + { + int categoryId = ConsoleInputHelper.PromptPositiveInt("Category ID"); + var qs = new QueryStringBuilder().Add("page", "1").Add("pageSize", "50").Build(); + + var response = await ApiClient.FetchPaginatedAsync( + http, + $"/api/product/category/{categoryId}{qs}" + ); + if (response?.Data != null && response.Data.Count > 0) + { + TableRenderer.DisplayTable( + response.Data, + $"Products in Category {categoryId}", + 0, + "CategoryId" + ); + } + else + { + AnsiConsole.MarkupLine("[yellow]No products found in this category[/]"); + } + } + + private static async Task ListAsync(HttpClient http) + { + var pageSize = ConsoleInputHelper.PromptPositiveInt("Items per page"); + + var filters = AnsiConsole.Confirm("Apply filters?", false) + ? PromptProductFilters() + : ProductFilters.Empty; + + var query = new ProductListQuery + { + Page = 1, + PageSize = pageSize, + Search = filters.Search, + MinPrice = filters.MinPrice, + MaxPrice = filters.MaxPrice, + CategoryId = filters.CategoryId, + SortBy = filters.SortBy, + SortDirection = filters.SortDirection, + }; + + await BuildAndExecuteListQueryWithPagination(http, query); + } + + private static async Task BuildAndExecuteListQueryWithPagination( + HttpClient http, + ProductListQuery query + ) + { + while (true) + { + var response = await FetchProductPageAsync(http, query); + if (!HasProductResults(response)) + { + AnsiConsole.MarkupLine("[yellow]No products found[/]"); + break; + } + + var pagination = new PaginationState + { + CurrentPage = query.Page, + PageSize = query.PageSize, + TotalCount = response!.TotalCount, + }; + + TableRenderer.DisplayTable( + response.Data!, + $"Products (Page {pagination.CurrentPage}/{pagination.TotalPages}, Total: {pagination.TotalCount})", + pagination.IndexOffset, + "CategoryId" + ); + + if (!HandlePaginationNavigation(pagination, query)) + break; + } + } + + private static bool HandlePaginationNavigation( + PaginationState pagination, + ProductListQuery query + ) + { + var navChoice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("Navigation:") + .AddChoices(pagination.GetNavigationChoices()) + ); + + if (navChoice == "Back to Menu") + return false; + + if (navChoice == "Next Page") + { + query.Page++; + } + else if (navChoice == "Previous Page") + { + query.Page--; + } + else if (navChoice == "Jump to Page") + { + int targetPage = ConsoleInputHelper.PromptPositiveInt( + $"Enter page number (1-{pagination.TotalPages})" + ); + if (targetPage >= 1 && targetPage <= pagination.TotalPages) + query.Page = targetPage; + else + AnsiConsole.MarkupLine( + $"[red]Invalid page number. Valid range: 1-{pagination.TotalPages}[/]" + ); + } + + return true; + } + + private static async Task CreateAsync(HttpClient http) + { + // Show list of categories to select from + var categoriesResponse = await ApiClient.FetchPaginatedAsync( + http, + "/api/categories?page=1&pageSize=32" + ); + if (categoriesResponse?.Data == null || categoriesResponse.Data.Count == 0) + { + AnsiConsole.MarkupLine( + "[yellow]No categories available. Please create a category first.[/]" + ); + return; + } + + var selectedCategory = await TableRenderer.SelectFromPromptAsync( + async (pageNum) => + { + var pageResponse = await ApiClient.FetchPaginatedAsync( + http, + $"/api/categories?page={pageNum}&pageSize=32" + ); + return pageResponse?.Data ?? new List(); + }, + categoriesResponse.TotalCount, + 32, + "Select a Category", + cat => cat.Name + ); + + if (selectedCategory == null) + return; + + var name = ConsoleInputHelper.PromptRequired("Product Name"); + var description = ConsoleInputHelper.PromptRequired("Description"); + var price = ConsoleInputHelper.PromptPositiveDecimal("Price"); + var stock = ConsoleInputHelper.PromptNonNegativeInt("Stock"); + + var product = new + { + name, + description, + price, + stock, + categoryId = selectedCategory.CategoryId, + isActive = true, + }; + + var payload = new { payload = product }; + await ApiClient.PostAsync(http, "/api/product", payload); + } + + private static async Task UpdateAsync(HttpClient http) + { + var response = await ApiClient.FetchPaginatedAsync( + http, + "/api/product?page=1&pageSize=32" + ); + if (!HasProductResults(response)) + { + AnsiConsole.MarkupLine("[yellow]No products available[/]"); + return; + } + + var selected = await SelectProductAsync(http, response!.TotalCount); + if (selected == null) + return; + + var current = await FetchProductDetailsAsync(http, selected.ProductId); + if (current == null) + { + ConsoleInputHelper.DisplayError("Product not found"); + return; + } + + DisplayCurrentValues(current); + + var categoryId = await DetermineCategoryAsync(http, current.CategoryId); + var update = PromptProductUpdates(current, categoryId); + + await ApiClient.PutAsync( + http, + $"/api/product/{selected.ProductId}", + BuildProductUpdatePayload(selected.ProductId, update) + ); + } + + private static ProductFilters PromptProductFilters() + { + var search = ConsoleInputHelper.PromptOptional("Search"); + var minPrice = ConsoleInputHelper.PromptOptionalDecimal("Min Price"); + var maxPrice = ConsoleInputHelper.PromptOptionalDecimal("Max Price"); + var categoryId = ConsoleInputHelper.PromptOptionalInt("Category Id"); + var (sortByResult, sortDirectionResult) = PromptSortOptions( + new[] { "(none)", "name", "price", "stock", "createdat", "category" } + ); + + return new ProductFilters + { + Search = search, + MinPrice = minPrice, + MaxPrice = maxPrice, + CategoryId = categoryId, + SortBy = sortByResult, + SortDirection = sortDirectionResult, + }; + } + + private static async Task?> FetchProductPageAsync( + HttpClient http, + ProductListQuery query + ) + { + var qs = new QueryStringBuilder() + .Add("page", query.Page.ToString()) + .Add("pageSize", query.PageSize.ToString()) + .Add("search", query.Search) + .Add("minPrice", query.MinPrice?.ToString()) + .Add("maxPrice", query.MaxPrice?.ToString()) + .Add("categoryId", query.CategoryId?.ToString()) + .Add("sortBy", query.SortBy == "(none)" || query.SortBy == null ? null : query.SortBy) + .Add("sortDirection", query.SortDirection) + .Build(); + + return await ApiClient.FetchPaginatedAsync(http, $"/api/product{qs}"); + } + + private static bool HasProductResults(PaginatedResponse? response) + { + return response?.Data != null && response.Data.Count > 0; + } + + private static async Task SelectProductAsync(HttpClient http, int totalCount) + { + return await TableRenderer.SelectFromPromptAsync( + async pageNum => + { + var pageResponse = await ApiClient.FetchPaginatedAsync( + http, + $"/api/product?page={pageNum}&pageSize=32" + ); + return pageResponse?.Data ?? new List(); + }, + totalCount, + 32, + "Select a Product to Update", + product => product.Name + ); + } + + private static async Task FetchProductDetailsAsync(HttpClient http, int productId) + { + var productResponse = await ApiClient.FetchEntityAsync( + http, + $"/api/product/{productId}" + ); + return productResponse?.Data; + } + + private static async Task DetermineCategoryAsync(HttpClient http, int currentCategoryId) + { + var categoriesResponse = await ApiClient.FetchPaginatedAsync( + http, + "/api/categories?page=1&pageSize=32" + ); + if (!HasCategoryResults(categoriesResponse)) + return currentCategoryId; + + if (!AnsiConsole.Confirm("Change category?", false)) + return currentCategoryId; + + var selectedCategory = await PromptCategorySelectionAsync( + http, + categoriesResponse!.TotalCount + ); + return selectedCategory?.CategoryId ?? currentCategoryId; + } + + private static async Task PromptCategorySelectionAsync( + HttpClient http, + int totalCount + ) + { + return await TableRenderer.SelectFromPromptAsync( + pageNum => FetchCategoryPageAsync(http, pageNum), + totalCount, + 32, + "Select a Category", + cat => cat.Name + ); + } + + private static async Task> FetchCategoryPageAsync( + HttpClient http, + int pageNum + ) + { + var pageResponse = await ApiClient.FetchPaginatedAsync( + http, + $"/api/categories?page={pageNum}&pageSize=32" + ); + return pageResponse?.Data ?? new List(); + } + + private static bool HasCategoryResults(PaginatedResponse? response) + { + return response?.Data != null && response.Data.Count > 0; + } + + private static ProductUpdate PromptProductUpdates(ProductDto current, int categoryId) + { + var name = PromptOptionalField("Product Name", current.Name); + var description = PromptOptionalField("Description", current.Description); + var priceStr = AnsiConsole.Ask("Price (leave blank to keep):", string.Empty); + var stockStr = AnsiConsole.Ask("Stock (leave blank to keep):", string.Empty); + var isActiveStr = AnsiConsole.Ask( + "Active? (yes/no, leave blank to keep):", + string.Empty + ); + + return new ProductUpdate + { + Name = name, + Description = description, + Price = TryParseDecimal(priceStr, current.Price), + Stock = TryParseInt(stockStr, current.Stock), + CategoryId = categoryId, + IsActive = TryParseBool(isActiveStr, current.IsActive), + }; + } + + private static object BuildProductUpdatePayload(int productId, ProductUpdate update) + { + return new + { + payload = new + { + productId, + name = update.Name, + description = update.Description, + price = update.Price, + stock = update.Stock, + categoryId = update.CategoryId, + isActive = update.IsActive, + }, + }; + } + + private static decimal TryParseDecimal(string value, decimal fallback) + { + return decimal.TryParse(value, out var parsed) ? parsed : fallback; + } + + private static int TryParseInt(string value, int fallback) + { + return int.TryParse(value, out var parsed) ? parsed : fallback; + } + + private static bool TryParseBool(string value, bool fallback) + { + if (string.IsNullOrWhiteSpace(value)) + return fallback; + + return value.Equals("yes", StringComparison.OrdinalIgnoreCase) + || value.Equals("true", StringComparison.OrdinalIgnoreCase); + } + + private static async Task DeleteAsync(HttpClient http) + { + // Show list for selection + var qs = new QueryStringBuilder().Add("page", "1").Add("pageSize", "32").Build(); + + var response = await ApiClient.FetchPaginatedAsync(http, $"/api/product{qs}"); + if (response?.Data == null || response.Data.Count == 0) + { + AnsiConsole.MarkupLine("[yellow]No products available[/]"); + return; + } + + var selected = await TableRenderer.SelectFromPromptAsync( + async (pageNum) => + { + var pageResponse = await ApiClient.FetchPaginatedAsync( + http, + $"/api/product?page={pageNum}&pageSize=32" + ); + return pageResponse?.Data ?? new List(); + }, + response.TotalCount, + 32, + "Select a Product to Delete", + product => product.Name + ); + if (selected == null) + return; + + if (!AnsiConsole.Confirm($"[red]Are you sure you want to delete '{selected.Name}'?[/]")) + return; + await ApiClient.DeleteAsync(http, $"/api/product/{selected.ProductId}"); + } + + private static void DisplayCurrentValues(ProductDto current) + { + AnsiConsole.MarkupLine($"[yellow]Current values:[/]"); + AnsiConsole.MarkupLine($" Name: {current.Name}"); + AnsiConsole.MarkupLine($" Description: {current.Description}"); + AnsiConsole.MarkupLine($" Price: {current.Price:0.00}"); + AnsiConsole.MarkupLine($" Stock: {current.Stock}"); + AnsiConsole.MarkupLine($" Active: {current.IsActive}"); + AnsiConsole.MarkupLine($" Category ID: {current.CategoryId}"); + } + + private static string? PromptOptionalField(string label, string? currentValue) + { + var input = AnsiConsole.Ask($"{label} (leave blank to keep):", string.Empty); + return string.IsNullOrWhiteSpace(input) ? currentValue : input; + } + + private static (string SortBy, string? SortDirection) PromptSortOptions(string[] options) + { + var sortBy = AnsiConsole.Prompt( + new SelectionPrompt().Title("Sort By (optional)").AddChoices(options) + ); + + string? sortDirection = null; + if (sortBy != "(none)") + { + sortDirection = AnsiConsole.Prompt( + new SelectionPrompt().Title("Sort Direction").AddChoices("asc", "desc") + ); + } + + return (sortBy, sortDirection); + } +} diff --git a/ConsoleClient/Handlers/SalesMenuHandler.cs b/ConsoleClient/Handlers/SalesMenuHandler.cs new file mode 100644 index 00000000..452edc9d --- /dev/null +++ b/ConsoleClient/Handlers/SalesMenuHandler.cs @@ -0,0 +1,463 @@ +using System.Text.Json; +using ECommerceApp.ConsoleClient.Helpers; +using ECommerceApp.ConsoleClient.Interfaces; +using ECommerceApp.ConsoleClient.Models; +using ECommerceApp.ConsoleClient.Utilities; +using Spectre.Console; + +namespace ECommerceApp.ConsoleClient.Handlers; + +/// +/// Handles sales menu operations. +/// Encapsulates all sales-related UI logic following Single Responsibility Principle. +/// Split into smaller methods to reduce cyclomatic complexity. +/// +public class SalesMenuHandler : IConsoleMenuHandler +{ + public string MenuName => "Sales"; + + public async Task ExecuteAsync(HttpClient http) + { + var actions = new Dictionary> + { + { "List", ListAsync }, + { "Get by Id", h => ApiClient.GetByIdAsync(h, "/api/sales/{id}", "Sale") }, + { + "With Deleted Products (all)", + h => ApiClient.GetAndRenderAsync(h, "/api/sales/with-deleted-products") + }, + { + "With Deleted Products (by Id)", + h => ApiClient.GetByIdAsync(h, "/api/sales/{id}/with-deleted-products", "Sale") + }, + { "Create", CreateAsync }, + { "Update", UpdateAsync }, + { "Delete", DeleteAsync }, + }; + + while (true) + { + var choice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("[green]Sales[/] — Choose an action:") + .AddChoices(actions.Keys.Concat(new[] { "Back" }).ToList()) + ); + + if (choice == "Back") + return; + + if (actions.TryGetValue(choice, out var action)) + await action(http); + } + } + + private static async Task ListAsync(HttpClient http) + { + var pageSize = ConsoleInputHelper.PromptPositiveInt("Items per page"); + + // Ask if user wants to apply filters + var applyFilters = AnsiConsole.Confirm("Apply filters?", false); + + DateTime? start = null; + DateTime? end = null; + string? customerName = null; + string? customerEmail = null; + string? sortBy = null; + string? sortDirection = null; + + if (applyFilters) + { + start = ConsoleInputHelper.PromptOptionalDate("Start Date (yyyy-MM-dd)"); + end = ConsoleInputHelper.PromptOptionalDate("End Date (yyyy-MM-dd)"); + customerName = ConsoleInputHelper.PromptOptional("Customer Name"); + customerEmail = ConsoleInputHelper.PromptOptional("Customer Email"); + var (sortByResult, sortDirectionResult) = PromptSortOptions( + new[] { "(none)", "saledate", "totalamount", "customername" } + ); + sortBy = sortByResult; + sortDirection = sortDirectionResult; + } + + var query = new SaleListQuery + { + Page = 1, + PageSize = pageSize, + StartDate = start, + EndDate = end, + CustomerName = customerName, + CustomerEmail = customerEmail, + SortBy = sortBy, + SortDirection = sortDirection, + }; + + await BuildAndExecuteListQueryWithPagination(http, query); + } + + private static async Task BuildAndExecuteListQueryWithPagination( + HttpClient http, + SaleListQuery query + ) + { + while (true) + { + var qs = BuildSaleQueryString(query); + var response = await ApiClient.FetchPaginatedAsync(http, $"/api/sales{qs}"); + + if (response?.Data == null || response.Data.Count == 0) + { + AnsiConsole.MarkupLine("[yellow]No sales found[/]"); + break; + } + + var pagination = new PaginationState + { + CurrentPage = query.Page, + PageSize = query.PageSize, + TotalCount = response.TotalCount, + }; + + TableRenderer.DisplayTable( + response.Data, + $"Sales (Page {pagination.CurrentPage}/{pagination.TotalPages}, Total: {pagination.TotalCount})", + pagination.IndexOffset + ); + + if (!HandlePaginationNavigation(query, pagination)) + break; + } + } + + private static string BuildSaleQueryString(SaleListQuery query) + { + return new QueryStringBuilder() + .Add("page", query.Page.ToString()) + .Add("pageSize", query.PageSize.ToString()) + .Add("startDate", query.StartDate?.ToString("yyyy-MM-dd")) + .Add("endDate", query.EndDate?.ToString("yyyy-MM-dd")) + .Add("customerName", query.CustomerName) + .Add("customerEmail", query.CustomerEmail) + .Add("sortBy", query.SortBy == "(none)" || query.SortBy == null ? null : query.SortBy) + .Add("sortDirection", query.SortDirection) + .Build(); + } + + private static bool HandlePaginationNavigation(SaleListQuery query, PaginationState pagination) + { + var navChoice = AnsiConsole.Prompt( + new SelectionPrompt() + .Title("Navigation:") + .AddChoices(pagination.GetNavigationChoices()) + ); + + switch (navChoice) + { + case "Back to Menu": + return false; + case "Next Page": + query.Page++; + break; + case "Previous Page": + query.Page--; + break; + case "Jump to Page": + HandleJumpToPage(query, pagination); + break; + } + + return true; + } + + private static void HandleJumpToPage(SaleListQuery query, PaginationState pagination) + { + int targetPage = ConsoleInputHelper.PromptPositiveInt( + $"Enter page number (1-{pagination.TotalPages})" + ); + if (targetPage >= 1 && targetPage <= pagination.TotalPages) + query.Page = targetPage; + else + AnsiConsole.MarkupLine( + $"[red]Invalid page number. Valid range: 1-{pagination.TotalPages}[/]" + ); + } + + private static async Task CreateAsync(HttpClient http) + { + var customerName = ConsoleInputHelper.PromptRequired("Customer Name"); + var customerEmail = ConsoleInputHelper.PromptRequired("Customer Email"); + var customerAddress = ConsoleInputHelper.PromptRequired("Customer Address"); + var saleDate = AnsiConsole.Prompt( + new TextPrompt("Sale Date (yyyy-MM-dd):").DefaultValue(DateTime.Now) + ); + + var saleItems = await PromptSaleItemsAsync(http); + if (saleItems.Count == 0) + { + ConsoleInputHelper.DisplayError("Must add at least one sale item"); + return; + } + + var sale = new + { + customerName, + customerEmail, + customerAddress, + saleDate, + saleItems, + }; + var payload = new { payload = sale }; + await ApiClient.PostAsync(http, "/api/sales", payload); + } + + private static async Task UpdateAsync(HttpClient http) + { + var response = await ApiClient.FetchPaginatedAsync( + http, + "/api/sales?page=1&pageSize=32" + ); + if (!HasSaleResults(response)) + { + AnsiConsole.MarkupLine("[yellow]No sales available[/]"); + return; + } + + var selected = await SelectSaleAsync(http, response!.TotalCount); + if (selected == null) + return; + + var current = await FetchSaleDetailsAsync(http, selected.SaleId); + if (current == null) + { + ConsoleInputHelper.DisplayError("Sale not found"); + return; + } + + DisplayCurrentSaleValues(current); + var update = PromptSaleUpdate(current); + + await ApiClient.PutAsync( + http, + $"/api/sales/{selected.SaleId}", + BuildSaleUpdatePayload(selected.SaleId, update) + ); + } + + private static async Task DeleteAsync(HttpClient http) + { + var response = await ApiClient.FetchPaginatedAsync( + http, + "/api/sales?page=1&pageSize=32" + ); + if (response?.Data == null || response.Data.Count == 0) + { + AnsiConsole.MarkupLine("[yellow]No sales available[/]"); + return; + } + + var selected = await TableRenderer.SelectFromPromptAsync( + async (pageNum) => + { + var pageResponse = await ApiClient.FetchPaginatedAsync( + http, + $"/api/sales?page={pageNum}&pageSize=32" + ); + return pageResponse?.Data ?? new List(); + }, + response.TotalCount, + 32, + "Select a Sale to Delete", + sale => $"{sale.SaleDate:yyyy-MM-dd HH:mm} - {sale.CustomerName}" + ); + + if (selected == null) + return; + + if ( + !AnsiConsole.Confirm( + $"[red]Are you sure you want to delete the sale from '{selected.SaleDate:yyyy-MM-dd HH:mm}' for '{selected.CustomerName}'?[/]" + ) + ) + return; + + await ApiClient.DeleteAsync(http, $"/api/sales/{selected.SaleId}"); + } + + private static async Task> PromptSaleItemsAsync(HttpClient http) + { + var saleItems = new List(); + AnsiConsole.MarkupLine("[yellow]Enter Sale Items[/]"); + + while (AnsiConsole.Confirm("Add a sale item?")) + { + var selectedProduct = await SelectProductForSaleAsync(http); + if (selectedProduct == null) + continue; + + var quantity = ConsoleInputHelper.PromptPositiveInt("Quantity"); + saleItems.Add(new { productId = selectedProduct.ProductId, quantity }); + } + + return saleItems; + } + + private static bool HasSaleResults(PaginatedResponse? response) + { + return response?.Data != null && response.Data.Count > 0; + } + + private static async Task SelectSaleAsync(HttpClient http, int totalCount) + { + return await TableRenderer.SelectFromPromptAsync( + async pageNum => + { + var pageResponse = await ApiClient.FetchPaginatedAsync( + http, + $"/api/sales?page={pageNum}&pageSize=32" + ); + return pageResponse?.Data ?? new List(); + }, + totalCount, + 32, + "Select a Sale to Update", + sale => $"{sale.SaleDate:yyyy-MM-dd HH:mm} - {sale.CustomerName}" + ); + } + + private static async Task FetchSaleDetailsAsync(HttpClient http, int saleId) + { + var saleResponse = await ApiClient.FetchEntityAsync(http, $"/api/sales/{saleId}"); + return saleResponse?.Data; + } + + private static SaleUpdate PromptSaleUpdate(SaleDto current) + { + var customerName = PromptOptionalField("Customer Name", current.CustomerName); + var customerEmail = PromptOptionalField("Email", current.CustomerEmail); + var saleDateStr = AnsiConsole.Ask( + "Sale Date (leave blank to keep, format: yyyy-MM-dd):", + string.Empty + ); + + return new SaleUpdate + { + CustomerName = customerName, + CustomerEmail = customerEmail, + SaleDate = string.IsNullOrWhiteSpace(saleDateStr) + ? current.SaleDate + : DateTime.Parse(saleDateStr), + }; + } + + private static object BuildSaleUpdatePayload(int saleId, SaleUpdate update) + { + return new + { + payload = new + { + saleId, + customerName = update.CustomerName, + customerEmail = update.CustomerEmail, + saleDate = update.SaleDate, + }, + }; + } + + private static async Task SelectProductForSaleAsync(HttpClient http) + { + var productsResponse = await ApiClient.FetchPaginatedAsync( + http, + "/api/product?page=1&pageSize=32" + ); + if (productsResponse?.Data == null || productsResponse.Data.Count == 0) + { + AnsiConsole.MarkupLine("[yellow]No products available[/]"); + return null; + } + + return await TableRenderer.SelectFromPromptAsync( + async pageNum => + { + var pageResponse = await ApiClient.FetchPaginatedAsync( + http, + $"/api/product?page={pageNum}&pageSize=32" + ); + return pageResponse?.Data ?? new List(); + }, + productsResponse.TotalCount, + 32, + "Select a Product", + product => product.Name + ); + } + + private static void DisplayCurrentSaleValues(SaleDto current) + { + AnsiConsole.MarkupLine($"[yellow]Current values:[/]"); + AnsiConsole.MarkupLine($" Customer Name: {current.CustomerName}"); + AnsiConsole.MarkupLine($" Email: {current.CustomerEmail}"); + AnsiConsole.MarkupLine($" Sale Date: {current.SaleDate:yyyy-MM-dd}"); + AnsiConsole.MarkupLine($" Total: {current.TotalAmount:0.00}"); + } + + private static string? PromptOptionalField(string label, string? currentValue) + { + var input = AnsiConsole.Ask($"{label} (leave blank to keep):", string.Empty); + return string.IsNullOrWhiteSpace(input) ? currentValue : input; + } + + private static (string SortBy, string? SortDirection) PromptSortOptions(string[] options) + { + var sortBy = AnsiConsole.Prompt( + new SelectionPrompt().Title("Sort By (optional)").AddChoices(options) + ); + + string? sortDirection = null; + if (sortBy != "(none)") + { + sortDirection = AnsiConsole.Prompt( + new SelectionPrompt().Title("Sort Direction").AddChoices("asc", "desc") + ); + } + + return (sortBy, sortDirection); + } + + private sealed record SaleUpdate + { + public string? CustomerName { get; init; } + public string? CustomerEmail { get; init; } + public DateTime SaleDate { get; init; } + } + + private sealed record SaleDto + { + public int SaleId { get; init; } + public DateTime SaleDate { get; init; } + public string? CustomerName { get; init; } + public string? CustomerEmail { get; init; } + public string? CustomerAddress { get; init; } + public decimal TotalAmount { get; init; } + } + + private sealed record ProductDto + { + public int ProductId { get; init; } + public string Name { get; init; } = string.Empty; + public string? Description { get; init; } + public decimal Price { get; init; } + public int Stock { get; init; } + public bool IsActive { get; init; } + public int CategoryId { get; init; } + } + + private sealed record SaleListQuery + { + public int Page { get; set; } + public int PageSize { get; set; } + public DateTime? StartDate { get; set; } + public DateTime? EndDate { get; set; } + public string? CustomerName { get; set; } + public string? CustomerEmail { get; set; } + public string? SortBy { get; set; } + public string? SortDirection { get; set; } + } +} diff --git a/ConsoleClient/Helpers/ConsoleInputHelper.cs b/ConsoleClient/Helpers/ConsoleInputHelper.cs new file mode 100644 index 00000000..b0c5c16f --- /dev/null +++ b/ConsoleClient/Helpers/ConsoleInputHelper.cs @@ -0,0 +1,143 @@ +using Spectre.Console; + +namespace ECommerceApp.ConsoleClient.Helpers; + +/// +/// Provides common UI and input utilities to eliminate code duplication. +/// Follows DRY principle - centralized prompt and validation logic. +/// +public static class ConsoleInputHelper +{ + /// + /// Prompts for an optional string input. + /// + public static string? PromptOptional(string label) + { + var v = AnsiConsole.Ask($"{label} (optional):", string.Empty); + return string.IsNullOrWhiteSpace(v) ? null : v; + } + + /// + /// Prompts for an optional integer input. + /// + public static int? PromptOptionalInt(string label) + { + var v = AnsiConsole.Ask($"{label} (optional):", string.Empty); + return int.TryParse(v, out var i) ? i : null; + } + + /// + /// Prompts for an optional decimal input. + /// + public static decimal? PromptOptionalDecimal(string label) + { + var v = AnsiConsole.Ask($"{label} (optional):", string.Empty); + return decimal.TryParse(v, out var d) ? d : null; + } + + /// + /// Prompts for an optional date input (yyyy-MM-dd format). + /// + public static DateTime? PromptOptionalDate(string label) + { + var v = AnsiConsole.Ask($"{label}", string.Empty); + return DateTime.TryParse(v, out var dt) ? dt : null; + } + + /// + /// Prompts for a required positive integer. + /// + public static int PromptPositiveInt(string label, string errorMessage = "Must be > 0") + { + return AnsiConsole.Prompt(new TextPrompt($"{label}:") + .Validate(i => i > 0 ? ValidationResult.Success() : ValidationResult.Error(errorMessage))); + } + + /// + /// Prompts for a required positive decimal. + /// + public static decimal PromptPositiveDecimal(string label, string errorMessage = "Must be > 0") + { + return AnsiConsole.Prompt(new TextPrompt($"{label}:") + .Validate(d => d > 0 ? ValidationResult.Success() : ValidationResult.Error(errorMessage))); + } + + /// + /// Prompts for a required non-negative integer (â‰Ĩ 0). + /// + public static int PromptNonNegativeInt(string label, int defaultValue = 0, string errorMessage = "Must be >= 0") + { + return AnsiConsole.Prompt(new TextPrompt($"{label}:") + .DefaultValue(defaultValue) + .Validate(i => i >= 0 ? ValidationResult.Success() : ValidationResult.Error(errorMessage))); + } + + /// + /// Prompts for a paginated result with standard page and page size inputs. + /// + public static (int Page, int PageSize) PromptPagination() + { + int page = AnsiConsole.Prompt(new TextPrompt("Page") + .DefaultValue(1) + .Validate(i => i > 0 ? ValidationResult.Success() : ValidationResult.Error("Page must be > 0"))); + + int pageSize = AnsiConsole.Prompt(new TextPrompt("Page Size (1-100)") + .DefaultValue(10) + .Validate(i => i is >= 1 and <= 100 + ? ValidationResult.Success() + : ValidationResult.Error("1-100"))); + + return (page, pageSize); + } + + /// + /// Prompts for a required string input. + /// + public static string PromptRequired(string label, string errorMessage = "Cannot be empty") + { + return AnsiConsole.Prompt(new TextPrompt($"{label}:") + .Validate(s => !string.IsNullOrWhiteSpace(s) + ? ValidationResult.Success() + : ValidationResult.Error(errorMessage))); + } + + /// + /// Displays an error message in red. + /// + public static void DisplayError(string message) + { + AnsiConsole.MarkupLine($"[red]{Markup.Escape(message)}[/]"); + } + + /// + /// Displays a success message in green. + /// + public static void DisplaySuccess(string message) + { + AnsiConsole.MarkupLine($"[green]{Markup.Escape(message)}[/]"); + } + + /// + /// Truncates text to maximum length. + /// + public static string Truncate(string s, int max) + { + return s.Length > max ? s[..max] + "..." : s; + } + + /// + /// Checks if content type indicates JSON. + /// + public static bool IsJson(string contentType, string body) + { + if (!string.IsNullOrWhiteSpace(contentType) && + contentType.Contains("json", StringComparison.OrdinalIgnoreCase)) + return true; + + if (string.IsNullOrWhiteSpace(body)) + return false; + + var t = body.TrimStart(); + return t.StartsWith("{") || t.StartsWith("["); + } +} diff --git a/ConsoleClient/Interfaces/IConsoleMenuHandler.cs b/ConsoleClient/Interfaces/IConsoleMenuHandler.cs new file mode 100644 index 00000000..62b12e76 --- /dev/null +++ b/ConsoleClient/Interfaces/IConsoleMenuHandler.cs @@ -0,0 +1,20 @@ +namespace ECommerceApp.ConsoleClient.Interfaces; + +/// +/// Defines the contract for handling console menu operations. +/// Follows Single Responsibility Principle - each handler manages one domain. +/// +public interface IConsoleMenuHandler +{ + /// + /// Gets the display name for the menu option. + /// + string MenuName { get; } + + /// + /// Executes the menu handler asynchronously. + /// + /// The HTTP client for API calls + /// A task representing the asynchronous operation + Task ExecuteAsync(HttpClient http); +} diff --git a/ConsoleClient/Models/CategoryListQuery.cs b/ConsoleClient/Models/CategoryListQuery.cs new file mode 100644 index 00000000..eaf09cd2 --- /dev/null +++ b/ConsoleClient/Models/CategoryListQuery.cs @@ -0,0 +1,13 @@ +namespace ECommerceApp.ConsoleClient.Models; + +/// +/// Represents query parameters for category list filtering and sorting. +/// +public record CategoryListQuery +{ + public int Page { get; set; } = 1; + public int PageSize { get; set; } = 10; + public string? Search { get; set; } + public string? SortBy { get; set; } + public string? SortDirection { get; set; } +} diff --git a/ConsoleClient/Models/PaginationState.cs b/ConsoleClient/Models/PaginationState.cs new file mode 100644 index 00000000..0dcb67dc --- /dev/null +++ b/ConsoleClient/Models/PaginationState.cs @@ -0,0 +1,34 @@ +namespace ECommerceApp.ConsoleClient.Models; + +/// +/// Represents the state of a paginated view for navigation purposes. +/// +public record PaginationState +{ + public int CurrentPage { get; set; } = 1; + public int PageSize { get; set; } = 10; + public int TotalCount { get; set; } + public bool HasNextPage => CurrentPage * PageSize < TotalCount; + public bool HasPreviousPage => CurrentPage > 1; + public int TotalPages => (TotalCount + PageSize - 1) / PageSize; + public int IndexOffset => (CurrentPage - 1) * PageSize; + + /// + /// Gets the navigation choices available for the current pagination state. + /// + public List GetNavigationChoices() + { + var choices = new List(); + + if (HasPreviousPage) + choices.Add("Previous Page"); + + if (HasNextPage) + choices.Add("Next Page"); + + choices.Add("Jump to Page"); + choices.Add("Back to Menu"); + + return choices; + } +} diff --git a/ConsoleClient/Models/SaleListQuery.cs b/ConsoleClient/Models/SaleListQuery.cs new file mode 100644 index 00000000..1617e0ed --- /dev/null +++ b/ConsoleClient/Models/SaleListQuery.cs @@ -0,0 +1,16 @@ +namespace ECommerceApp.ConsoleClient.Models; + +/// +/// Represents query parameters for sale list filtering and sorting. +/// +public record SaleListQuery +{ + public int Page { get; set; } = 1; + public int PageSize { get; set; } = 10; + public DateTime? StartDate { get; set; } + public DateTime? EndDate { get; set; } + public string? CustomerName { get; set; } + public string? CustomerEmail { get; set; } + public string? SortBy { get; set; } + public string? SortDirection { get; set; } +} diff --git a/ConsoleClient/Program.cs b/ConsoleClient/Program.cs new file mode 100644 index 00000000..15900a53 --- /dev/null +++ b/ConsoleClient/Program.cs @@ -0,0 +1,175 @@ +using System.Net; +using System.Text; +using System.Text.Json; +using ECommerceApp.ConsoleClient; +using ECommerceApp.ConsoleClient.Handlers; +using ECommerceApp.ConsoleClient.Helpers; +using ECommerceApp.ConsoleClient.Interfaces; +using ECommerceApp.ConsoleClient.Utilities; +using Spectre.Console; + +namespace ECommerceApp.ConsoleClient; + +/// +/// Main console client entry point. +/// Orchestrates menu navigation and delegates domain-specific logic to handlers. +/// Follows Dependency Inversion Principle using IConsoleMenuHandler abstraction. +/// Refactored to reduce cyclomatic complexity and eliminate code duplication. +/// +public static class Program +{ + private const string DefaultBaseUrl = "http://localhost:51680"; + + public static async Task Main(string[] args) + { + AnsiConsole.Write(new FigletText("ECommerce API").Color(Color.Green)); + + var settings = new ClientSettings { BaseUrl = ResolveBaseUrl(args) }; + var http = CreateHttpClient(settings.BaseUrl); + + try + { + // Initialize handlers following Dependency Injection pattern + var handlers = new Dictionary + { + { "Products", new ProductMenuHandler() }, + { "Categories", new CategoryMenuHandler() }, + { "Sales", new SalesMenuHandler() } + }; + + while (true) + { + var choice = AnsiConsole.Prompt(new SelectionPrompt() + .Title($"[yellow]Base URL:[/] [blue]{settings.BaseUrl}[/]\nSelect an option:") + .PageSize(10) + .AddChoices(handlers.Keys.Concat(new[] { "Custom Request", "Settings", "Exit" }).ToList())); + + if (handlers.TryGetValue(choice, out var handler)) + { + await handler.ExecuteAsync(http); + } + else + { + switch (choice) + { + case "Custom Request": + await CustomRequestAsync(http); + break; + case "Settings": + var newUrl = PromptBaseUrl(settings.BaseUrl); + if (newUrl != settings.BaseUrl) + { + settings.BaseUrl = newUrl; + http.Dispose(); + http = CreateHttpClient(settings.BaseUrl); + } + break; + case "Exit": + return 0; + } + } + } + } + finally + { + http?.Dispose(); + } + } + + private static string ResolveBaseUrl(string[] args) + { + // Priority: CLI arg --base-url, env ECOMMERCE_BASE_URL, default + var fromArg = GetArgValue(args, "--base-url"); + if (!string.IsNullOrWhiteSpace(fromArg)) + return fromArg!; + + var env = Environment.GetEnvironmentVariable("ECOMMERCE_BASE_URL"); + if (!string.IsNullOrWhiteSpace(env)) + return env!; + + return DefaultBaseUrl; + } + + private static string? GetArgValue(string[] args, string key) + { + for (int i = 0; i < args.Length; i++) + { + if (string.Equals(args[i], key, StringComparison.OrdinalIgnoreCase)) + { + if (i + 1 < args.Length) + return args[i + 1]; + } + else if (args[i].StartsWith(key + "=", StringComparison.OrdinalIgnoreCase)) + { + return args[i][(key.Length + 1)..]; + } + } + return null; + } + + private static HttpClient CreateHttpClient(string baseUrl) + { + // For development: ignore SSL certificate validation errors for self-signed certificates + var handler = new HttpClientHandler(); + + // Always bypass certificate validation for localhost and 127.0.0.1 in development + if (baseUrl.Contains("localhost", StringComparison.OrdinalIgnoreCase) || + baseUrl.Contains("127.0.0.1", StringComparison.OrdinalIgnoreCase)) + { + handler.ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true; + } + + var http = new HttpClient(handler, disposeHandler: true) + { + BaseAddress = new Uri(baseUrl), + Timeout = TimeSpan.FromSeconds(30) + }; + http.DefaultRequestHeaders.Accept.Clear(); + http.DefaultRequestHeaders.Accept.ParseAdd("application/json"); + return http; + } + + private static string PromptBaseUrl(string current) + { + var input = AnsiConsole.Prompt(new TextPrompt("Enter API base URL:") + .DefaultValue(current) + .Validate(url => Uri.TryCreate(url, UriKind.Absolute, out _) + ? ValidationResult.Success() + : ValidationResult.Error("Invalid URL"))); + return input; + } + + private static async Task CustomRequestAsync(HttpClient http) + { + var method = AnsiConsole.Prompt(new SelectionPrompt() + .Title("HTTP Method") + .AddChoices("GET", "POST", "PUT", "DELETE")); + + var path = ConsoleInputHelper.PromptRequired("Path (e.g., /api/product)"); + + string? body = null; + if (method is "POST" or "PUT") + { + body = AnsiConsole.Ask("JSON body (leave empty for none):", ""); + } + + try + { + using var req = new HttpRequestMessage(new HttpMethod(method), path); + if (!string.IsNullOrWhiteSpace(body)) + { + req.Content = new StringContent(body!, Encoding.UTF8, "application/json"); + } + await ApiClient.SendAndRenderAsync(http, req); + } + catch (Exception ex) + { + ConsoleInputHelper.DisplayError(ex.Message); + } + } + + private sealed record ClientSettings + { + public string BaseUrl { get; set; } = DefaultBaseUrl; + } +} diff --git a/ConsoleClient/Utilities/ApiClient.cs b/ConsoleClient/Utilities/ApiClient.cs new file mode 100644 index 00000000..bb8f3241 --- /dev/null +++ b/ConsoleClient/Utilities/ApiClient.cs @@ -0,0 +1,183 @@ +using System; +using System.Net; +using System.Text; +using System.Text.Json; +using ECommerceApp.ConsoleClient.Helpers; +using Spectre.Console; + +namespace ECommerceApp.ConsoleClient.Utilities; + +/// +/// Provides API communication utilities. +/// Centralizes HTTP requests, response parsing, and error handling. +/// Follows Single Responsibility Principle. +/// +public static class ApiClient +{ + /// + /// Sends a GET request to the specified path and renders the response. + /// + public static async Task GetAndRenderAsync(HttpClient http, string path) + { + using var req = new HttpRequestMessage(HttpMethod.Get, path); + await SendAndRenderAsync(http, req); + } + + /// + /// Sends an HTTP request and displays the response. + /// + public static async Task SendAndRenderAsync(HttpClient http, HttpRequestMessage req) + { + await AnsiConsole + .Status() + .Spinner(Spinner.Known.Dots) + .StartAsync( + "Calling API...", + async _ => + { + using var res = await http.SendAsync(req); + var body = await res.Content.ReadAsStringAsync(); + var header = $"{(int)res.StatusCode} {res.ReasonPhrase}"; + var ct = res.Content.Headers.ContentType?.MediaType ?? ""; + + AnsiConsole.Write(new Rule($"[bold]HTTP {req.Method}[/] {req.RequestUri}")); + AnsiConsole.MarkupLine( + $"Status: [bold]{header}[/] Content-Type: [italic]{ct}[/]" + ); + + var truncatedBody = ConsoleInputHelper.Truncate(body, 8000); + var escapedBody = Markup.Escape(truncatedBody); + + if (ConsoleInputHelper.IsJson(ct, body)) + { + try + { + AnsiConsole.Write(new Panel(escapedBody).Header("Response").Expand()); + } + catch + { + AnsiConsole.Write(new Panel(escapedBody).Header("Response").Expand()); + } + } + else + { + AnsiConsole.Write(new Panel(escapedBody).Header("Response").Expand()); + } + } + ); + } + + // ...existing code... + /// + /// Fetches a single entity by ID and deserializes it. + /// + public static async Task?> FetchEntityAsync(HttpClient http, string path) + { + using var getReq = new HttpRequestMessage(HttpMethod.Get, path); + using var getRes = await http.SendAsync(getReq); + if (!getRes.IsSuccessStatusCode) + { + ConsoleInputHelper.DisplayError($"Failed to fetch entity: {getRes.StatusCode}"); + return null; + } + + var json = await getRes.Content.ReadAsStringAsync(); + var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + return JsonSerializer.Deserialize>(json, options); + } + + /// + /// Fetches a paginated list and deserializes it. + /// + public static async Task?> FetchPaginatedAsync( + HttpClient http, + string path + ) + { + using var req = new HttpRequestMessage(HttpMethod.Get, path); + using var res = await http.SendAsync(req); + if (!res.IsSuccessStatusCode) + { + ConsoleInputHelper.DisplayError($"Failed to fetch data: {res.StatusCode}"); + return null; + } + + var json = await res.Content.ReadAsStringAsync(); + var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; + return JsonSerializer.Deserialize>(json, options); + } + + /// + /// Sends a GET request with an ID parameter. + /// + public static async Task GetByIdAsync(HttpClient http, string template, string label) + { + int id = ConsoleInputHelper.PromptPositiveInt($"{label} Id"); + var path = template.Replace("{id}", id.ToString()); + await SendAndRenderAsync(http, new HttpRequestMessage(HttpMethod.Get, path)); + } + + /// + /// Sends a POST request with JSON body. + /// + public static async Task PostAsync(HttpClient http, string path, object payload) + { + var json = JsonSerializer.Serialize(payload); + using var req = new HttpRequestMessage(HttpMethod.Post, path) + { + Content = new StringContent(json, Encoding.UTF8, "application/json"), + }; + await SendAndRenderAsync(http, req); + } + + /// + /// Sends a PUT request with JSON body. + /// + public static async Task PutAsync(HttpClient http, string path, object payload) + { + var json = JsonSerializer.Serialize(payload); + using var req = new HttpRequestMessage(HttpMethod.Put, path) + { + Content = new StringContent(json, Encoding.UTF8, "application/json"), + }; + await SendAndRenderAsync(http, req); + } + + /// + /// Sends a DELETE request. + /// + public static async Task DeleteAsync(HttpClient http, string path) + { + using var req = new HttpRequestMessage(HttpMethod.Delete, path); + await SendAndRenderAsync(http, req); + } +} + +/// +/// Paginated response wrapper for deserialization in ConsoleClient. +/// +public sealed record PaginatedResponse +{ + public bool RequestFailed { get; init; } + public HttpStatusCode ResponseCode { get; init; } + public string ErrorMessage { get; init; } = string.Empty; + public List? Data { get; init; } + public int CurrentPage { get; init; } + public int PageSize { get; init; } + public int TotalCount { get; init; } + public int TotalPages => (int)Math.Ceiling((double)TotalCount / PageSize); + public bool HasNextPage => CurrentPage < TotalPages; + public bool HasPreviousPage => CurrentPage > 1; + public int IndexOffset => (CurrentPage - 1) * PageSize; +} + +/// +/// API response wrapper for deserialization. +/// +public sealed record ApiResponse +{ + public bool RequestFailed { get; init; } + public HttpStatusCode ResponseCode { get; init; } + public string ErrorMessage { get; init; } = string.Empty; + public T? Data { get; init; } +} diff --git a/ConsoleClient/Utilities/MenuActionHandler.cs b/ConsoleClient/Utilities/MenuActionHandler.cs new file mode 100644 index 00000000..6b53a9f4 --- /dev/null +++ b/ConsoleClient/Utilities/MenuActionHandler.cs @@ -0,0 +1,30 @@ +namespace ECommerceApp.ConsoleClient.Utilities; + +/// +/// Encapsulates menu action handling to reduce cyclomatic complexity. +/// Follows Strategy Pattern for extensible menu operations. +/// +public class MenuActionHandler +{ + private readonly Dictionary> _actions; + + public MenuActionHandler() + { + _actions = new(); + } + + public void Register(string action, Func handler) + { + _actions[action] = handler; + } + + public async Task ExecuteAsync(string action, HttpClient http) + { + if (_actions.TryGetValue(action, out var handler)) + { + await handler(http); + } + } + + public IEnumerable GetActions() => _actions.Keys; +} diff --git a/ConsoleClient/Utilities/QueryStringBuilder.cs b/ConsoleClient/Utilities/QueryStringBuilder.cs new file mode 100644 index 00000000..59d6a4b3 --- /dev/null +++ b/ConsoleClient/Utilities/QueryStringBuilder.cs @@ -0,0 +1,38 @@ +namespace ECommerceApp.ConsoleClient.Utilities; + +/// +/// Builder for constructing query strings with null-safe parameter handling. +/// +public class QueryStringBuilder +{ + private readonly Dictionary _parameters = new(); + + /// + /// Adds a parameter to the query string. + /// Null values are ignored to avoid building unnecessary parameters. + /// + /// The parameter key + /// The parameter value (null values are ignored) + /// The builder instance for method chaining + public QueryStringBuilder Add(string key, string? value) + { + if (!string.IsNullOrEmpty(value)) + { + _parameters[key] = Uri.EscapeDataString(value); + } + return this; + } + + /// + /// Builds the query string. + /// + /// The formatted query string (e.g., "?key1=value1&key2=value2"), or empty string if no parameters + public string Build() + { + if (_parameters.Count == 0) + return string.Empty; + + var query = string.Join("&", _parameters.Select(p => $"{p.Key}={p.Value}")); + return $"?{query}"; + } +} diff --git a/ConsoleClient/Utilities/TableRenderer.cs b/ConsoleClient/Utilities/TableRenderer.cs new file mode 100644 index 00000000..545ec200 --- /dev/null +++ b/ConsoleClient/Utilities/TableRenderer.cs @@ -0,0 +1,479 @@ +using System.Reflection; +using Spectre.Console; + +namespace ECommerceApp.ConsoleClient.Utilities; + +/// +/// Utility for rendering data collections as formatted tables in the console. +/// Abstracts away object IDs by using 1-based index numbers. +/// +public static class TableRenderer +{ + /// + /// Renders a collection of items as a table and returns the selected item. + /// Uses 1-based index numbers instead of IDs for user-friendly selection. + /// + /// The type of items to display + /// The items to display in the table + /// The table title + /// Column names to exclude from display + /// The selected item, or null if cancelled + public static T? SelectFromTable( + IList items, + string title, + int indexOffset = 0, + params string[] excludeColumns + ) + where T : class + { + if (items.Count == 0) + { + AnsiConsole.MarkupLine("[yellow]No items to display[/]"); + return null; + } + + var table = new Table { Title = new TableTitle(title) }; + table.AddColumn("[bold]#[/]"); + + var properties = GetDisplayProperties(excludeColumns); + foreach (var prop in properties) + { + table.AddColumn($"[bold]{prop.Name}[/]"); + } + + for (int i = 0; i < items.Count; i++) + { + var cells = new List { (indexOffset + i + 1).ToString() }; + foreach (var prop in properties) + { + var value = prop.GetValue(items[i]); + cells.Add(FormatValue(value)); + } + table.AddRow(cells.ToArray()); + } + + AnsiConsole.Write(table); + + // Prompt for selection + var choices = Enumerable + .Range(indexOffset + 1, items.Count) + .Select(i => i.ToString()) + .Concat(new[] { "Cancel" }) + .ToList(); + + var choice = AnsiConsole.Prompt( + new SelectionPrompt().Title("Select an item:").AddChoices(choices) + ); + + if (choice == "Cancel" || !int.TryParse(choice, out int selectedIndex)) + return null; + + return items[selectedIndex - indexOffset - 1]; + } + + /// + /// Creates a selection prompt that shows index and item name together. + /// More user-friendly than selecting by number alone. + /// Supports pagination for lists with more than 32 items. + /// + public static T? SelectFromPrompt( + IList items, + string title, + int indexOffset = 0, + string nameProperty = "Name" + ) + where T : class + { + var formatter = BuildNameFormatter(nameProperty); + return SelectWithPaging(items, title, indexOffset, formatter); + } + + /// + /// Creates a selection prompt with a custom display function for formatting items. + /// Allows flexible display of item information (e.g., date + customer name for sales). + /// + public static T? SelectFromPrompt( + IList items, + string title, + int indexOffset, + Func displayFormatter + ) + where T : class + { + return SelectWithPaging(items, title, indexOffset, displayFormatter); + } + + /// + /// Displays a collection of items as a formatted table without selection. + /// + public static void DisplayTable( + IList items, + string title, + int indexOffset = 0, + params string[] excludeColumns + ) + where T : class + { + if (items.Count == 0) + { + AnsiConsole.MarkupLine("[yellow]No items to display[/]"); + return; + } + + var table = new Table { Title = new TableTitle(title) }; + table.AddColumn("[bold]#[/]"); + + var properties = GetDisplayProperties(excludeColumns); + foreach (var prop in properties) + { + table.AddColumn($"[bold]{prop.Name}[/]"); + } + + for (int i = 0; i < items.Count; i++) + { + var cells = new List { (indexOffset + i + 1).ToString() }; + foreach (var prop in properties) + { + var value = prop.GetValue(items[i]); + cells.Add(FormatValue(value)); + } + table.AddRow(cells.ToArray()); + } + + AnsiConsole.Write(table); + } + + /// + /// Gets properties suitable for display, excluding specified columns and internal fields. + /// Automatically excludes ID columns (anything ending with "Id"). + /// Returns properties in the desired display order: Name, Description, Price, Stock, IsActive. + /// + private static List GetDisplayProperties(string[] excludeColumns) + where T : class + { + var desiredOrder = new[] + { + "Name", + "Description", + "Price", + "Stock", + "IsActive", + "CustomerName", + "SaleDate", + "TotalAmount", + "CustomerEmail", + "CustomerAddress", + }; + + var allProps = typeof(T) + .GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(p => + p.CanRead + && !excludeColumns.Contains(p.Name, StringComparer.OrdinalIgnoreCase) + && !p.Name.EndsWith("Id", StringComparison.OrdinalIgnoreCase) + ) + .Where(p => + p.PropertyType.IsPrimitive + || p.PropertyType == typeof(string) + || p.PropertyType == typeof(decimal) + || p.PropertyType == typeof(DateTime) + || p.PropertyType == typeof(DateTime?) + ) + .ToList(); + + // Sort by desired order, then add any remaining properties alphabetically + var props = new List(); + foreach (var propName in desiredOrder) + { + var prop = allProps.FirstOrDefault(p => + p.Name.Equals(propName, StringComparison.OrdinalIgnoreCase) + ); + if (prop != null) + { + props.Add(prop); + allProps.Remove(prop); + } + } + + // Add any remaining properties in alphabetical order + props.AddRange(allProps.OrderBy(p => p.Name)); + + return props; + } + + /// + /// Async selection prompt with dynamic pagination support. + /// Fetches pages from the API on demand as the user navigates. + /// + public static async Task SelectFromPromptAsync( + Func>> fetchPageAsync, + int totalCount, + int pageSize, + string title, + Func displayFormatter + ) + where T : class + { + if (totalCount == 0) + { + AnsiConsole.MarkupLine("[yellow]No items to display[/]"); + return null; + } + + return await SelectWithDynamicPaging( + fetchPageAsync, + totalCount, + pageSize, + title, + displayFormatter + ); + } + + private static Func BuildNameFormatter(string nameProperty) + where T : class + { + var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); + var nameProp = properties.FirstOrDefault(p => + p.Name.Equals(nameProperty, StringComparison.OrdinalIgnoreCase) + ); + + return item => nameProp?.GetValue(item)?.ToString() ?? "Unknown"; + } + + private static T? SelectWithPaging( + IList items, + string title, + int indexOffset, + Func displayFormatter + ) + where T : class + { + if (items.Count == 0) + { + AnsiConsole.MarkupLine("[yellow]No items to display[/]"); + return null; + } + + const int pageSize = 32; + int currentPage = 0; + int totalPages = (items.Count + pageSize - 1) / pageSize; + + while (true) + { + var (startIndex, endIndex) = GetPageBounds(currentPage, pageSize, items.Count); + var choices = BuildChoices( + items, + displayFormatter, + indexOffset, + startIndex, + endIndex, + currentPage, + totalPages + ); + var choice = PromptSelection(title, choices, currentPage + 1, totalPages); + + var action = ParseNavigation(choice); + if (action == PagingAction.Cancel) + return null; + + if (HandleNavigation(ref currentPage, totalPages, action)) + continue; + + var selectedIndex = TryExtractIndex(choice, indexOffset); + if ( + selectedIndex.HasValue + && selectedIndex.Value >= 0 + && selectedIndex.Value < items.Count + ) + return items[selectedIndex.Value]; + + return null; + } + } + + private static async Task SelectWithDynamicPaging( + Func>> fetchPageAsync, + int totalCount, + int pageSize, + string title, + Func displayFormatter + ) + where T : class + { + // Use 0-based page index internally to align with BuildChoices/HandleNavigation logic. + int currentPageIndex = 0; + int totalPages = (totalCount + pageSize - 1) / pageSize; + var currentPageItems = new List(); + + while (true) + { + if (currentPageItems.Count == 0) + { + currentPageItems = await fetchPageAsync(currentPageIndex + 1); + if (currentPageItems.Count == 0) + { + AnsiConsole.MarkupLine("[yellow]No items on this page[/]"); + return null; + } + } + + int indexOffset = currentPageIndex * pageSize; + var choices = BuildChoices( + currentPageItems, + displayFormatter, + indexOffset, + 0, + currentPageItems.Count, + currentPageIndex, + totalPages + ); + var choice = PromptSelection(title, choices, currentPageIndex + 1, totalPages); + + var action = ParseNavigation(choice); + if (action == PagingAction.Cancel) + return null; + + if (HandleNavigation(ref currentPageIndex, totalPages, action)) + { + currentPageItems.Clear(); + continue; + } + + var localIndex = TryExtractIndex(choice, indexOffset); + if (localIndex.HasValue) + { + if (localIndex.Value >= 0 && localIndex.Value < currentPageItems.Count) + return currentPageItems[localIndex.Value]; + } + + return null; + } + } + + private static (int Start, int End) GetPageBounds(int currentPage, int pageSize, int totalItems) + { + int startIndex = currentPage * pageSize; + int endIndex = Math.Min(startIndex + pageSize, totalItems); + return (startIndex, endIndex); + } + + private static List BuildChoices( + IList items, + Func displayFormatter, + int indexOffset, + int startIndex, + int endIndex, + int currentPage, + int totalPages + ) + where T : class + { + var choices = new List(); + + for (int i = startIndex; i < endIndex; i++) + { + var index = indexOffset + i + 1; + choices.Add($"{index} - {displayFormatter(items[i])}"); + } + + if (totalPages > 1) + { + choices.Add("---"); + if (currentPage > 0) + choices.Add("< Previous Page"); + if (currentPage < totalPages - 1) + choices.Add("Next Page >"); + } + + choices.Add("Cancel"); + return choices; + } + + private static string PromptSelection( + string title, + List choices, + int currentPage, + int totalPages + ) + { + string pageIndicator = totalPages > 1 ? $" (Page {currentPage}/{totalPages})" : ""; + // Spectre.Console requires page size >= 3; guard small choice lists (e.g., 1 item + Cancel). + var promptPageSize = Math.Max(3, Math.Min(40, choices.Count)); + return AnsiConsole.Prompt( + new SelectionPrompt() + .Title($"[green]{title}{pageIndicator}[/]") + .PageSize(promptPageSize) + .MoreChoicesText("[grey](Use arrow keys to navigate)[/]") + .AddChoices(choices) + ); + } + + private static PagingAction ParseNavigation(string choice) + { + return choice switch + { + "Cancel" => PagingAction.Cancel, + "---" => PagingAction.Cancel, + "< Previous Page" => PagingAction.Previous, + "Next Page >" => PagingAction.Next, + _ => PagingAction.Select, + }; + } + + private static bool HandleNavigation(ref int currentPage, int totalPages, PagingAction action) + { + if (action == PagingAction.Previous && currentPage > 0) + { + currentPage--; + return true; + } + + if (action == PagingAction.Next && currentPage < totalPages - 1) + { + currentPage++; + return true; + } + + return false; + } + + private static int? TryExtractIndex(string choice, int indexOffset) + { + var firstPart = choice.Split('-')[0].Trim(); + if (int.TryParse(firstPart, out int selectedDisplayIndex)) + { + return selectedDisplayIndex - indexOffset - 1; + } + + return null; + } + + private enum PagingAction + { + Select, + Previous, + Next, + Cancel, + } + + /// + /// Formats a value for table display. + /// + private static string FormatValue(object? value) + { + if (value == null) + return "[dim]—[/]"; + + if (value is bool boolVal) + return boolVal ? "[green]Yes[/]" : "[red]No[/]"; + + if (value is DateTime dateVal) + return dateVal.ToString("yyyy-MM-dd HH:mm"); + + if (value is decimal decimalVal) + return decimalVal.ToString("N2"); + + var str = value.ToString() ?? ""; + return str.Length > 50 ? str[..47] + "..." : str; + } +} diff --git a/ConsoleClient/packages.lock.json b/ConsoleClient/packages.lock.json new file mode 100644 index 00000000..0f48a778 --- /dev/null +++ b/ConsoleClient/packages.lock.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "dependencies": { + "net10.0": { + "Spectre.Console": { + "type": "Direct", + "requested": "[0.49.1, )", + "resolved": "0.49.1", + "contentHash": "USV+pdu49OJ3nCjxNuw1K9Zw/c1HCBbwbjXZp0EOn6wM99tFdAtN34KEBZUMyRuJuXlUMDqhd8Yq9obW2MslYA==" + } + } + } +} \ No newline at end of file diff --git a/Controllers/CategoriesController.cs b/Controllers/CategoriesController.cs new file mode 100644 index 00000000..9e221d6b --- /dev/null +++ b/Controllers/CategoriesController.cs @@ -0,0 +1,169 @@ +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace ECommerceApp.RyanW84.Controllers; + +[ApiController] +[Route("api/[controller]")] +[Route("api/v1/categories")] +/// +/// Category CRUD API endpoints. +/// Provides create, read (by id/name), update, delete, and soft-delete restore operations for . +/// Responses are wrapped in the project's standard API response DTOs and follow the same error mapping conventions. +/// +public class CategoriesController : ControllerBase +{ + private readonly ICategoryService _categoryService; + + public CategoriesController(ICategoryService categoryService) => + _categoryService = categoryService; + + // POST /api/categories + /// + /// Creates a new category. + /// + [HttpPost] + [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] + public async Task Create( + [FromBody] ApiRequestDto request, + CancellationToken cancellationToken + ) + { + ApiResponseDto result = await _categoryService.CreateCategoryAsync(request, cancellationToken); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + + return CreatedAtAction(nameof(GetById), new { id = result.Data!.CategoryId }, result); + } + + // GET /api/categories + /// + /// Returns a paginated list of categories. + /// Supports filtering/sorting via . + /// + [HttpGet] + [ResponseCache( + Duration = 120, + Location = ResponseCacheLocation.Any, + VaryByQueryKeys = new[] { "*" } + )] + public async Task GetAll( + [FromQuery] CategoryQueryParameters queryParameters, + CancellationToken cancellationToken + ) + { + PaginatedResponseDto> result = await _categoryService.GetAllCategoriesAsync( + queryParameters, + cancellationToken + ); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return Ok(result); + } + + // GET /api/categories/{id} + /// + /// Retrieves a single category by numeric identifier. + /// + [HttpGet("{id:int}")] + [ResponseCache(Duration = 120, Location = ResponseCacheLocation.Any)] + public async Task GetById(int id, CancellationToken cancellationToken) + { + ApiResponseDto result = await _categoryService.GetCategoryAsync(id, cancellationToken); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + + Response.Headers.Append( + "Link", + $"; rel=\"self\", ; rel=\"collection\"" + ); + return Ok(result); + } + + // GET /api/categories/name/{name} + /// + /// Retrieves a category by its name. + /// + [HttpGet("name/{name}")] + [ResponseCache( + Duration = 60, + Location = ResponseCacheLocation.Any, + VaryByQueryKeys = new[] { "name" } + )] + public async Task GetByName(string name, CancellationToken cancellationToken) + { + ApiResponseDto result = await _categoryService.GetCategoryByNameAsync(name, cancellationToken); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return Ok(result); + } + + // PUT /api/categories/{id} + /// + /// Updates an existing category by id. + /// + [HttpPut("{id:int}")] + [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] + public async Task Update( + int id, + [FromBody] ApiRequestDto request, + CancellationToken cancellationToken + ) + { + ApiResponseDto result = await _categoryService.UpdateCategoryAsync(id, request, cancellationToken); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return Ok(result); + } + + // DELETE /api/categories/{id} + /// + /// Soft-deletes a category by id. + /// + [HttpDelete("{id:int}")] + [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] + public async Task Delete(int id, CancellationToken cancellationToken) + { + ApiResponseDto result = await _categoryService.DeleteCategoryAsync(id, cancellationToken); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return NoContent(); + } + + // GET /api/categories/deleted + /// + /// Lists categories that have been soft-deleted. + /// + [HttpGet("deleted")] + [ResponseCache(Duration = 30, Location = ResponseCacheLocation.Any)] + public async Task GetDeletedCategories(CancellationToken cancellationToken) + { + ApiResponseDto> result = await _categoryService.GetDeletedCategoriesAsync(cancellationToken); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return Ok(result); + } + + // POST /api/categories/{id}/restore + /// + /// Restores a previously soft-deleted category. + /// + [HttpPost("{id:int}/restore")] + [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] + public async Task Restore(int id, CancellationToken cancellationToken) + { + ApiResponseDto result = await _categoryService.RestoreCategoryAsync(id, cancellationToken); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return Ok(result); + } + + // POST /api/v1/categories/{id}/restorations + // Noun-based alternative to "restore". + [HttpPost("{id:int}/restorations")] + [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] + public Task CreateRestoration(int id, CancellationToken cancellationToken) => + Restore(id, cancellationToken); +} diff --git a/Controllers/ControllerResponseExtensions.cs b/Controllers/ControllerResponseExtensions.cs new file mode 100644 index 00000000..cedfc207 --- /dev/null +++ b/Controllers/ControllerResponseExtensions.cs @@ -0,0 +1,27 @@ +using System.Net; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.WebUtilities; + +namespace ECommerceApp.RyanW84.Controllers; + +public static class ControllerResponseExtensions +{ + public static IActionResult FromFailure(this ControllerBase controller, HttpStatusCode statusCode, string errorMessage) + { + if (statusCode == HttpStatusCode.NoContent) + { + return controller.NoContent(); + } + + var detail = string.IsNullOrWhiteSpace(errorMessage) + ? "Request could not be completed." + : errorMessage; + + var status = (int)statusCode; + return controller.Problem( + title: ReasonPhrases.GetReasonPhrase(status), + detail: detail, + statusCode: status + ); + } +} diff --git a/Controllers/ProductController.cs b/Controllers/ProductController.cs new file mode 100644 index 00000000..e34268cb --- /dev/null +++ b/Controllers/ProductController.cs @@ -0,0 +1,178 @@ +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Interfaces; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.OutputCaching; + +namespace ECommerceApp.RyanW84.Controllers; + +/// +/// API controller for managing products. +/// Provides CRUD operations and product queries with pagination support. +/// +[ApiController] +[Route("api/[controller]")] +[Route("api/products")] +[Route("api/v1/products")] +public class ProductController(IProductService productService) : ControllerBase +{ + private readonly IProductService _productService = productService; + + /// + /// Retrieves all products with optional filtering and pagination. + /// + /// Query parameters for filtering and pagination + /// Cancellation token + /// Paginated list of products + [HttpGet] + [OutputCache(PolicyName = "Products")] + public async Task GetProductsAsync( + [FromQuery] ProductQueryParameters queryParameters, + CancellationToken cancellationToken = default + ) + { + PaginatedResponseDto> result = await _productService.GetProductsAsync( + queryParameters, + cancellationToken + ); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return Ok(result); + } + + /// + /// Retrieves a specific product by its ID. + /// + /// The product ID + /// Cancellation token + /// The requested product or 404 Not Found + [HttpGet("{id:int}")] + [OutputCache(PolicyName = "Products")] + public async Task GetProductById( + int id, + CancellationToken cancellationToken = default + ) + { + ApiResponseDto result = await _productService.GetProductByIdAsync( + id, + cancellationToken + ); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + + Response.Headers.Append( + "Link", + $"; rel=\"self\", ; rel=\"collection\"" + ); + return Ok(result); + } + + // GET /api/products/category/{categoryId} + [HttpGet("category/{categoryId:int}")] + [OutputCache(PolicyName = "Products")] + public async Task GetProductsByCategory( + int categoryId, + CancellationToken cancellationToken = default + ) + { + ApiResponseDto> result = await _productService.GetProductsByCategoryIdAsync( + categoryId, + cancellationToken + ); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return Ok(result); + } + + // POST /api/products + [HttpPost] + public async Task CreateProduct( + [FromBody] ApiRequestDto request, + CancellationToken cancellationToken = default + ) + { + ApiResponseDto result = await _productService.CreateProductAsync( + request, + cancellationToken + ); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + + return CreatedAtAction(nameof(GetProductById), new { id = result.Data!.ProductId }, result); + } + + // PUT /api/products/{id} + [HttpPut("{id:int}")] + public async Task UpdateProduct( + int id, + [FromBody] ApiRequestDto request, + CancellationToken cancellationToken = default + ) + { + ApiResponseDto result = await _productService.UpdateProductAsync( + id, + request, + cancellationToken + ); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return Ok(result); + } + + // DELETE /api/products/{id} + [HttpDelete("{id:int}")] + public async Task DeleteProduct( + int id, + CancellationToken cancellationToken = default + ) + { + ApiResponseDto result = await _productService.DeleteProductAsync( + id, + cancellationToken + ); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return NoContent(); + } + + // GET /api/products/deleted + [HttpGet("deleted")] + [ResponseCache(Duration = 30, Location = ResponseCacheLocation.Any)] + public async Task GetDeletedProducts( + CancellationToken cancellationToken = default + ) + { + ApiResponseDto> result = await _productService.GetDeletedProductsAsync( + cancellationToken + ); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return Ok(result); + } + + // POST /api/products/{id}/restore + [HttpPost("{id:int}/restore")] + [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] + public async Task RestoreProduct( + int id, + CancellationToken cancellationToken = default + ) + { + ApiResponseDto result = await _productService.RestoreProductAsync( + id, + cancellationToken + ); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return Ok(result); + } + + // POST /api/v1/products/{id}/restorations + // Noun-based alternative to "restore". + [HttpPost("{id:int}/restorations")] + [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] + public Task CreateRestoration( + int id, + CancellationToken cancellationToken = default + ) => RestoreProduct(id, cancellationToken); +} diff --git a/Controllers/SalesController.cs b/Controllers/SalesController.cs new file mode 100644 index 00000000..b400aa52 --- /dev/null +++ b/Controllers/SalesController.cs @@ -0,0 +1,155 @@ +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Interfaces; +using Microsoft.AspNetCore.Mvc; + +namespace ECommerceApp.RyanW84.Controllers; + +[ApiController] +[Route("api/[controller]")] +[Route("api/v1/sales")] +/// +/// Sales API endpoints. +/// Supports creating and managing sales, including read operations that can optionally include historical (soft-deleted) products. +/// +public class SalesController : ControllerBase +{ + private readonly ISaleService _saleService; + + public SalesController(ISaleService saleService) => _saleService = saleService; + + // POST /api/sales + /// + /// Creates a sale with line items. + /// + [HttpPost] + [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] + public async Task Create( + [FromBody] ApiRequestDto request, + CancellationToken cancellationToken + ) + { + ApiResponseDto result = await _saleService.CreateSaleAsync(request, cancellationToken); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + + return CreatedAtAction(nameof(GetById), new { id = result.Data!.SaleId }, result); + } + + /// + /// Retrieves a single sale by id. + /// + [HttpGet("{id:int}")] + [ResponseCache(Duration = 60, Location = ResponseCacheLocation.Any)] + public async Task GetById(int id, CancellationToken cancellationToken) + { + ApiResponseDto result = await _saleService.GetSaleByIdAsync(id, cancellationToken); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + + Response.Headers.Append( + "Link", + $"; rel=\"self\", ; rel=\"collection\"" + ); + return Ok(result); + } + + /// + /// Returns a paginated list of sales. + /// Supports filtering/sorting via . + /// + [HttpGet] + [ResponseCache( + Duration = 30, + Location = ResponseCacheLocation.Any, + VaryByQueryKeys = new[] { "*" } + )] + public async Task GetAll( + [FromQuery] SaleQueryParameters queryParameters, + CancellationToken cancellationToken = default + ) + { + PaginatedResponseDto> result = await _saleService.GetSalesAsync(queryParameters, cancellationToken); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return Ok(result); + } + + /// + /// Lists sales including those whose items reference soft-deleted products (historical view). + /// + [HttpGet("with-deleted-products")] + [ResponseCache(Duration = 30, Location = ResponseCacheLocation.Any)] + public async Task GetAllWithDeletedProducts(CancellationToken cancellationToken) + { + ApiResponseDto> result = await _saleService.GetHistoricalSalesAsync(cancellationToken); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return Ok(result); + } + + // GET /api/v1/sales/history + // Noun-based alternative to "with-deleted-products". + [HttpGet("history")] + [ResponseCache(Duration = 30, Location = ResponseCacheLocation.Any)] + public Task GetHistory(CancellationToken cancellationToken) => + GetAllWithDeletedProducts(cancellationToken); + + /// + /// Retrieves a single sale by id, including historical (soft-deleted) products. + /// + [HttpGet("{id:int}/with-deleted-products")] + [ResponseCache(Duration = 60, Location = ResponseCacheLocation.Any)] + public async Task GetByIdWithDeletedProducts( + int id, + CancellationToken cancellationToken + ) + { + ApiResponseDto result = await _saleService.GetSaleByIdWithHistoricalProductsAsync( + id, + cancellationToken + ); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return Ok(result); + } + + // GET /api/v1/sales/{id}/history + // Noun-based alternative to "with-deleted-products". + [HttpGet("{id:int}/history")] + [ResponseCache(Duration = 60, Location = ResponseCacheLocation.Any)] + public Task GetByIdHistory(int id, CancellationToken cancellationToken) => + GetByIdWithDeletedProducts(id, cancellationToken); + + // PUT /api/sales/{id} + /// + /// Updates an existing sale by id. + /// + [HttpPut("{id:int}")] + [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] + public async Task Update( + int id, + [FromBody] ApiRequestDto request, + CancellationToken cancellationToken + ) + { + ApiResponseDto result = await _saleService.UpdateSaleAsync(id, request, cancellationToken); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return Ok(result); + } + + // DELETE /api/sales/{id} + /// + /// Soft-deletes a sale by id. + /// + [HttpDelete("{id:int}")] + [ResponseCache(NoStore = true, Location = ResponseCacheLocation.None)] + public async Task Delete(int id, CancellationToken cancellationToken) + { + ApiResponseDto result = await _saleService.DeleteSaleAsync(id, cancellationToken); + if (result.RequestFailed) + return this.FromFailure(result.ResponseCode, result.ErrorMessage); + return NoContent(); + } +} diff --git a/Data/Configuration/IndexConfiguration.cs b/Data/Configuration/IndexConfiguration.cs new file mode 100644 index 00000000..478f76ff --- /dev/null +++ b/Data/Configuration/IndexConfiguration.cs @@ -0,0 +1,78 @@ +using ECommerceApp.RyanW84.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace ECommerceApp.RyanW84.Data.Configuration; + +/// +/// Configures database indexes for optimized query performance. +/// Indexes are crucial for fast lookups and filtered queries. +/// +public static class IndexConfiguration +{ + public static void ConfigureIndexes(ModelBuilder modelBuilder) + { + // Product indexes for common queries + modelBuilder + .Entity() + .HasIndex(p => p.CategoryId) + .HasDatabaseName("IX_Products_CategoryId"); + + modelBuilder.Entity().HasIndex(p => p.Price).HasDatabaseName("IX_Products_Price"); + + modelBuilder + .Entity() + .HasIndex(p => p.IsActive) + .HasDatabaseName("IX_Products_IsActive"); + + modelBuilder + .Entity() + .HasIndex(p => p.CreatedAt) + .HasDatabaseName("IX_Products_CreatedAt"); + + modelBuilder + .Entity() + .HasIndex(p => new { p.IsDeleted, p.IsActive }) + .HasDatabaseName("IX_Products_IsDeleted_IsActive"); + + // Category indexes + modelBuilder + .Entity() + .HasIndex(c => c.Name) + .IsUnique() + .HasDatabaseName("IX_Categories_Name"); + + modelBuilder + .Entity() + .HasIndex(c => c.IsDeleted) + .HasDatabaseName("IX_Categories_IsDeleted"); + + // Sale indexes for date range queries + modelBuilder.Entity().HasIndex(s => s.SaleDate).HasDatabaseName("IX_Sales_SaleDate"); + + modelBuilder + .Entity() + .HasIndex(s => s.CustomerEmail) + .HasDatabaseName("IX_Sales_CustomerEmail"); + + modelBuilder + .Entity() + .HasIndex(s => s.CustomerName) + .HasDatabaseName("IX_Sales_CustomerName"); + + modelBuilder + .Entity() + .HasIndex(s => s.TotalAmount) + .HasDatabaseName("IX_Sales_TotalAmount"); + + // SaleItem indexes + modelBuilder + .Entity() + .HasIndex(si => si.SaleId) + .HasDatabaseName("IX_SaleItems_SaleId"); + + modelBuilder + .Entity() + .HasIndex(si => si.ProductId) + .HasDatabaseName("IX_SaleItems_ProductId"); + } +} diff --git a/Data/Configuration/ModelConfiguration.cs b/Data/Configuration/ModelConfiguration.cs new file mode 100644 index 00000000..a7b23d85 --- /dev/null +++ b/Data/Configuration/ModelConfiguration.cs @@ -0,0 +1,120 @@ +using ECommerceApp.RyanW84.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace ECommerceApp.RyanW84.Data.Configuration; + +/// +/// Handles Entity Framework model configuration following SRP. +/// Centralizes all FluentAPI configurations for entities. +/// +public static class ModelConfiguration +{ + public static void ConfigureModels(ModelBuilder modelBuilder) + { + ConfigureProduct(modelBuilder); + ConfigureCategory(modelBuilder); + ConfigureSale(modelBuilder); + ConfigureSaleItem(modelBuilder); + ConfigureCategorySealeRelationship(modelBuilder); + } + + private static void ConfigureProduct(ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.HasKey(p => p.ProductId); + entity.Property(p => p.Name).IsRequired().HasMaxLength(100); + entity.Property(p => p.Description).IsRequired().HasMaxLength(500); + entity.Property(p => p.Price).IsRequired().HasPrecision(18, 2); + entity.Property(p => p.Stock).IsRequired().HasDefaultValue(0); + entity.Property(p => p.IsActive).IsRequired().HasDefaultValue(true); + entity.Property(p => p.CategoryId).IsRequired(); + entity.HasIndex(p => p.Name).HasDatabaseName("IX_Products_Name"); + + entity + .HasOne(p => p.Category) + .WithMany(c => c.Products) + .HasForeignKey(p => p.CategoryId) + .OnDelete(DeleteBehavior.Cascade); + + entity + .HasMany(p => p.SaleItems) + .WithOne(si => si.Product) + .HasForeignKey(si => si.ProductId) + .OnDelete(DeleteBehavior.NoAction); + }); + } + + private static void ConfigureCategory(ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.HasKey(c => c.CategoryId); + entity.Property(c => c.Name).IsRequired().HasMaxLength(100); + entity.Property(c => c.Description).IsRequired().HasMaxLength(500); + entity.HasIndex(c => c.Name).IsUnique().HasDatabaseName("IX_Categories_Name"); + }); + + modelBuilder + .Entity() + .HasMany(c => c.Products) + .WithOne(p => p.Category) + .HasForeignKey(p => p.CategoryId) + .OnDelete(DeleteBehavior.Cascade); + } + + private static void ConfigureSale(ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.HasKey(s => s.SaleId); + entity.Property(s => s.SaleDate).IsRequired(); + entity.Property(s => s.TotalAmount).IsRequired().HasPrecision(18, 2); + entity.Property(s => s.CustomerName).IsRequired().HasMaxLength(100); + entity.Property(s => s.CustomerEmail).IsRequired().HasMaxLength(100); + entity.Property(s => s.CustomerAddress).IsRequired().HasMaxLength(200); + + entity + .HasMany(s => s.SaleItems) + .WithOne(si => si.Sale) + .HasForeignKey(si => si.SaleId) + .OnDelete(DeleteBehavior.Cascade); + }); + } + + private static void ConfigureSaleItem(ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.HasKey(si => new { si.SaleId, si.ProductId }); + entity.Property(si => si.Quantity).IsRequired(); + entity.Property(si => si.UnitPrice).IsRequired().HasPrecision(18, 2); + }); + } + + private static void ConfigureCategorySealeRelationship(ModelBuilder modelBuilder) + { + modelBuilder + .Entity() + .HasMany(c => c.Sales) + .WithMany(s => s.Categories) + .UsingEntity>( + "CategorySale", + j => + j.HasOne() + .WithMany() + .HasForeignKey("SaleId") + .OnDelete(DeleteBehavior.Cascade), + j => + j.HasOne() + .WithMany() + .HasForeignKey("CategoryId") + .OnDelete(DeleteBehavior.Cascade), + j => + { + j.HasKey("CategoryId", "SaleId"); + j.ToTable("CategorySales"); + } + ); + } +} diff --git a/Data/Configurations/CategoryConfiguration.cs b/Data/Configurations/CategoryConfiguration.cs new file mode 100644 index 00000000..69f2c1a7 --- /dev/null +++ b/Data/Configurations/CategoryConfiguration.cs @@ -0,0 +1,34 @@ +using ECommerceApp.RyanW84.Data.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ECommerceApp.RyanW84.Data.Configurations; + +public class CategoryConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(c => c.CategoryId); + builder.Property(c => c.Name).IsRequired().HasMaxLength(100); + builder.Property(c => c.Description).IsRequired().HasMaxLength(500); + + builder + .HasMany(c => c.Products) + .WithOne(p => p.Category) + .HasForeignKey(p => p.CategoryId) + .OnDelete(DeleteBehavior.Cascade); + + builder + .HasMany(c => c.Sales) + .WithMany(s => s.Categories) + .UsingEntity>( + "CategorySale", + j => j.HasOne().WithMany().HasForeignKey("SaleId").OnDelete(DeleteBehavior.Cascade), + j => j.HasOne().WithMany().HasForeignKey("CategoryId").OnDelete(DeleteBehavior.Cascade), + j => + { + j.HasKey("CategoryId", "SaleId"); + j.ToTable("CategorySales"); + }); + } +} \ No newline at end of file diff --git a/Data/Configurations/ProductConfiguration.cs b/Data/Configurations/ProductConfiguration.cs new file mode 100644 index 00000000..4ff93255 --- /dev/null +++ b/Data/Configurations/ProductConfiguration.cs @@ -0,0 +1,32 @@ +using ECommerceApp.RyanW84.Data.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ECommerceApp.RyanW84.Data.Configurations; + +public class ProductConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(p => p.ProductId); + builder.Property(p => p.Name).IsRequired().HasMaxLength(100); + builder.Property(p => p.Description).IsRequired().HasMaxLength(500); + builder.Property(p => p.Price).IsRequired().HasPrecision(18, 2); + builder.Property(p => p.Stock).IsRequired().HasDefaultValue(0); + builder.Property(p => p.IsActive).IsRequired().HasDefaultValue(true); + builder.Property(p => p.CategoryId).IsRequired(); + builder.HasIndex(p => p.Name).HasDatabaseName("IX_Products_Name"); + + builder + .HasOne(p => p.Category) + .WithMany(c => c.Products) + .HasForeignKey(p => p.CategoryId) + .OnDelete(DeleteBehavior.Cascade); + + builder + .HasMany(p => p.SaleItems) + .WithOne(si => si.Product) + .HasForeignKey(si => si.ProductId) + .OnDelete(DeleteBehavior.NoAction); + } +} \ No newline at end of file diff --git a/Data/Configurations/SaleConfiguration.cs b/Data/Configurations/SaleConfiguration.cs new file mode 100644 index 00000000..b454e16a --- /dev/null +++ b/Data/Configurations/SaleConfiguration.cs @@ -0,0 +1,24 @@ +using ECommerceApp.RyanW84.Data.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ECommerceApp.RyanW84.Data.Configurations; + +public class SaleConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(s => s.SaleId); + builder.Property(s => s.SaleDate).IsRequired(); + builder.Property(s => s.TotalAmount).IsRequired().HasPrecision(18, 2); + builder.Property(s => s.CustomerName).IsRequired().HasMaxLength(100); + builder.Property(s => s.CustomerEmail).IsRequired().HasMaxLength(100); + builder.Property(s => s.CustomerAddress).IsRequired().HasMaxLength(200); + + builder + .HasMany(s => s.SaleItems) + .WithOne(si => si.Sale) + .HasForeignKey(si => si.SaleId) + .OnDelete(DeleteBehavior.Cascade); + } +} \ No newline at end of file diff --git a/Data/Configurations/SaleItemConfiguration.cs b/Data/Configurations/SaleItemConfiguration.cs new file mode 100644 index 00000000..50103f50 --- /dev/null +++ b/Data/Configurations/SaleItemConfiguration.cs @@ -0,0 +1,16 @@ +using ECommerceApp.RyanW84.Data.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace ECommerceApp.RyanW84.Data.Configurations; + +public class SaleItemConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.HasKey(si => new { si.SaleId, si.ProductId }); + + builder.Property(si => si.Quantity).IsRequired(); + builder.Property(si => si.UnitPrice).IsRequired().HasPrecision(18, 2); + } +} \ No newline at end of file diff --git a/Data/DTO/ApiRequestDto.cs b/Data/DTO/ApiRequestDto.cs new file mode 100644 index 00000000..e61ce801 --- /dev/null +++ b/Data/DTO/ApiRequestDto.cs @@ -0,0 +1,8 @@ +namespace ECommerceApp.RyanW84.Data.DTO; + +/// +/// Standard request wrapper for API endpoints. +/// The payload is modeled as a nullable value to support validation and consistent error responses. +/// +/// The request payload type. +public record ApiRequestDto(T? Payload); diff --git a/Data/DTO/ApiResponseDto.cs b/Data/DTO/ApiResponseDto.cs new file mode 100644 index 00000000..d3c3ef63 --- /dev/null +++ b/Data/DTO/ApiResponseDto.cs @@ -0,0 +1,53 @@ +using System.Net; + +namespace ECommerceApp.RyanW84.Data.DTO; + +public class ApiResponseDto +{ + public bool RequestFailed { get; set; } + public HttpStatusCode ResponseCode { get; set; } + public string ErrorMessage { get; set; } = string.Empty; + public T? Data { get; set; } + + /// + /// Creates a success response with the provided data. + /// + public static ApiResponseDto Success(T? data, HttpStatusCode statusCode = HttpStatusCode.OK) + { + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = statusCode, + ErrorMessage = string.Empty, + Data = data, + }; + } + + /// + /// Creates an error response with the provided status code and message. + /// + public static ApiResponseDto Failure(HttpStatusCode statusCode, string errorMessage) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = statusCode, + ErrorMessage = errorMessage, + Data = default, + }; + } + + /// + /// Passthrough for existing responses or conversion from other responses. + /// + public static ApiResponseDto FromResponse(ApiResponseDto source, T? data) + { + return new ApiResponseDto + { + RequestFailed = source.RequestFailed, + ResponseCode = source.ResponseCode, + ErrorMessage = source.ErrorMessage, + Data = data, + }; + } +} diff --git a/Data/DTO/CategoryQueryParameters.cs b/Data/DTO/CategoryQueryParameters.cs new file mode 100644 index 00000000..e352d15d --- /dev/null +++ b/Data/DTO/CategoryQueryParameters.cs @@ -0,0 +1,30 @@ +namespace ECommerceApp.RyanW84.Data.DTO; + +public class CategoryQueryParameters +{ + private const int MaxPageSize = 32; + + private int _page = 1; + private int _pageSize = 10; + public int Page + { + get => _page; + set => _page = value < 1 ? 1 : value; + } + public int PageSize + { + get => _pageSize; + set + { + if (value < 1) + _pageSize = 10; + else + _pageSize = value > MaxPageSize ? MaxPageSize : value; + } + } + public string? Search { get; set; } + public string? SortBy { get; set; } + public string? SortDirection { get; set; } + + public bool IncludeDeleted { get; set; } +} diff --git a/Data/DTO/PaginatedResponseDto.cs b/Data/DTO/PaginatedResponseDto.cs new file mode 100644 index 00000000..02663b46 --- /dev/null +++ b/Data/DTO/PaginatedResponseDto.cs @@ -0,0 +1,61 @@ +using System.Net; + +namespace ECommerceApp.RyanW84.Data.DTO; + +public class PaginatedResponseDto +{ + public bool RequestFailed { get; set; } + public HttpStatusCode ResponseCode { get; set; } + public string ErrorMessage { get; set; } = string.Empty; + public T? Data { get; set; } + public int CurrentPage { get; set; } + public int PageSize { get; set; } + public int TotalCount { get; set; } + public int TotalPages => (int)Math.Ceiling((double)TotalCount / PageSize); + public bool HasNextPage => CurrentPage < TotalPages; + public bool HasPreviousPage => CurrentPage > 1; + + /// + /// Creates a success paginated response. + /// + public static PaginatedResponseDto Success( + T? data, + int currentPage, + int pageSize, + int totalCount + ) + { + return new PaginatedResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = data, + CurrentPage = currentPage, + PageSize = pageSize, + TotalCount = totalCount, + }; + } + + /// + /// Creates a failure paginated response. + /// + public static PaginatedResponseDto Failure( + HttpStatusCode statusCode, + string errorMessage, + int currentPage = 1, + int pageSize = 10 + ) + { + return new PaginatedResponseDto + { + RequestFailed = true, + ResponseCode = statusCode, + ErrorMessage = errorMessage, + Data = default, + CurrentPage = currentPage, + PageSize = pageSize, + TotalCount = 0, + }; + } +} diff --git a/Data/DTO/ProductQueryParameters.cs b/Data/DTO/ProductQueryParameters.cs new file mode 100644 index 00000000..b2fe83a1 --- /dev/null +++ b/Data/DTO/ProductQueryParameters.cs @@ -0,0 +1,30 @@ +namespace ECommerceApp.RyanW84.Data.DTO; + +public class ProductQueryParameters +{ + private const int MaxPageSize = 32; + + private int _page = 1; + private int _pageSize = 10; + public int Page + { + get => _page; + set => _page = value < 1 ? 1 : value; + } + public int PageSize + { + get => _pageSize; + set + { + if (value < 1) _pageSize = 10; + else _pageSize = value > MaxPageSize ? MaxPageSize : value; + } + } + public string? Search { get; set; } + public decimal? MinPrice { get; set; } + public decimal? MaxPrice { get; set; } + + public int? CategoryId { get; set; } + public string? SortBy { get; set; } + public string? SortDirection { get; set; } +} diff --git a/Data/DTO/SaleQueryParameters.cs b/Data/DTO/SaleQueryParameters.cs new file mode 100644 index 00000000..7b44c3e3 --- /dev/null +++ b/Data/DTO/SaleQueryParameters.cs @@ -0,0 +1,32 @@ +namespace ECommerceApp.RyanW84.Data.DTO; + +public class SaleQueryParameters +{ + private const int MaxPageSize = 32; + + private int _page = 1; + private int _pageSize = 10; + + public int Page + { + get => _page; + set => _page = value < 1 ? 1 : value; + } + public int PageSize + { + get => _pageSize; + set + { + if (value < 1) _pageSize = 10; + else _pageSize = value > MaxPageSize ? MaxPageSize : value; + } + } + + public DateTime? StartDate { get; set; } + + public DateTime? EndDate { get; set; } + public string? CustomerName { get; set; } + public string? CustomerEmail { get; set; } + public string? SortBy { get; set; } + public string? SortDirection { get; set; } +} diff --git a/Data/DTO/SalesSummaryDto.cs b/Data/DTO/SalesSummaryDto.cs new file mode 100644 index 00000000..8cfd3743 --- /dev/null +++ b/Data/DTO/SalesSummaryDto.cs @@ -0,0 +1,10 @@ +namespace ECommerceApp.RyanW84.Data.DTO; + +public sealed class SalesSummaryDto +{ + public string ProductName { get; init; } = null!; + public string CategoryName { get; init; } = null!; + public int TotalQuantitySold { get; init; } + public decimal TotalRevenue { get; init; } + public DateTime LastSaleDate { get; init; } +} diff --git a/Data/ECommerceDbContext.cs b/Data/ECommerceDbContext.cs new file mode 100644 index 00000000..24727b74 --- /dev/null +++ b/Data/ECommerceDbContext.cs @@ -0,0 +1,35 @@ +using ECommerceApp.RyanW84.Data.Configuration; +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Data.Seeding; +using Microsoft.EntityFrameworkCore; + +namespace ECommerceApp.RyanW84.Data; + +/// +/// EF Core DbContext for eCommerce domain. +/// Delegates model configuration and seeding to separate handler classes per SRP. +/// +public class ECommerceDbContext(DbContextOptions options) : DbContext(options) +{ + public DbSet Products { get; set; } = null!; + public DbSet Categories { get; set; } = null!; + public DbSet Sales { get; set; } = null!; + public DbSet SaleItems { get; set; } = null!; + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + ModelConfiguration.ConfigureModels(modelBuilder); + IndexConfiguration.ConfigureIndexes(modelBuilder); + ConfigureGlobalQueryFilters(modelBuilder); + } + + private static void ConfigureGlobalQueryFilters(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasQueryFilter(c => !c.IsDeleted); + modelBuilder.Entity().HasQueryFilter(p => !p.IsDeleted); + modelBuilder.Entity().HasQueryFilter(si => !si.Product!.IsDeleted); + } + + public void SeedData() => DatabaseSeeder.SeedDatabase(this); +} diff --git a/Data/ECommerceDbContext.cs.backup b/Data/ECommerceDbContext.cs.backup new file mode 100644 index 00000000..c701424f --- /dev/null +++ b/Data/ECommerceDbContext.cs.backup @@ -0,0 +1,309 @@ +using ECommerceApp.RyanW84.Data.Configurations; +using ECommerceApp.RyanW84.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace ECommerceApp.RyanW84.Data; + +public class ECommerceDbContext(DbContextOptions options) : DbContext(options) +{ + // DbSets for the entities + public DbSet Products { get; set; } = null!; + public DbSet Categories { get; set; } = null!; + public DbSet Sales { get; set; } = null!; + public DbSet SaleItems { get; set; } = null!; + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity(entity => + { + entity.HasKey(p => p.ProductId); + entity.Property(p => p.Name).IsRequired().HasMaxLength(100); + entity.Property(p => p.Description).IsRequired().HasMaxLength(500); + entity.Property(p => p.Price).IsRequired().HasPrecision(18, 2); + entity.Property(p => p.Stock).IsRequired().HasDefaultValue(0); + entity.Property(p => p.IsActive).IsRequired().HasDefaultValue(true); + entity.Property(p => p.CategoryId).IsRequired(); + entity.HasIndex(p => p.Name).HasDatabaseName("IX_Products_Name"); + + entity + .HasOne(p => p.Category) + .WithMany(c => c.Products) + .HasForeignKey(p => p.CategoryId) + .OnDelete(DeleteBehavior.Cascade); + + entity + .HasMany(p => p.SaleItems) + .WithOne(si => si.Product) + .HasForeignKey(si => si.ProductId) + .OnDelete(DeleteBehavior.NoAction); // choose behavior you want + }); + + modelBuilder.Entity() + .HasMany(c => c.Products) + .WithOne(p => p.Category) + .HasForeignKey(p => p.CategoryId) + .OnDelete(DeleteBehavior.Cascade); + + // configure many-to-many Category <-> Sale + modelBuilder.Entity() + .HasMany(c => c.Sales) + .WithMany(s => s.Categories) + .UsingEntity>( + "CategorySale", + j => j.HasOne().WithMany().HasForeignKey("SaleId").OnDelete(DeleteBehavior.Cascade), + j => j.HasOne().WithMany().HasForeignKey("CategoryId").OnDelete(DeleteBehavior.Cascade), + j => + { + j.HasKey("CategoryId", "SaleId"); + j.ToTable("CategorySales"); + }); + + // Sale mapping + modelBuilder.Entity(entity => + { + entity.HasKey(s => s.SaleId); + entity.Property(s => s.SaleDate).IsRequired(); + entity.Property(s => s.TotalAmount).IsRequired().HasPrecision(18, 2); + entity.Property(s => s.CustomerName).IsRequired().HasMaxLength(100); + entity.Property(s => s.CustomerEmail).IsRequired().HasMaxLength(100); + entity.Property(s => s.CustomerAddress).IsRequired().HasMaxLength(200); + + entity + .HasMany(s => s.SaleItems) + .WithOne(si => si.Sale) + .HasForeignKey(si => si.SaleId) + .OnDelete(DeleteBehavior.Cascade); + }); + + // SaleItem mapping (composite key) + modelBuilder.Entity(entity => + { + entity.HasKey(si => new { si.SaleId, si.ProductId }); + + entity.Property(si => si.Quantity).IsRequired(); + entity.Property(si => si.UnitPrice).IsRequired().HasPrecision(18, 2); + }); + + // Global query filters for soft delete + modelBuilder.Entity().HasQueryFilter(p => !p.IsDeleted); + modelBuilder.Entity().HasQueryFilter(c => !c.IsDeleted); + // Filter out SaleItems that reference soft-deleted products + modelBuilder.Entity().HasQueryFilter(si => !si.Product.IsDeleted); + } + + public void SeedData() + { + // Seed Categories - Let Entity Framework auto-generate IDs + var electronics = new Category { Name = "Electronics", Description = "Electronic devices and gadgets" }; + var clothing = new Category { Name = "Clothing", Description = "Apparel and fashion items" }; + var books = new Category { Name = "Books", Description = "Books and publications" }; + var homeAndGarden = new Category { Name = "Home & Garden", Description = "Home improvement and gardening supplies" }; + var sports = new Category { Name = "Sports & Outdoors", Description = "Sports equipment and outdoor gear" }; + var beauty = new Category { Name = "Beauty & Personal Care", Description = "Cosmetics and personal care products" }; + var toys = new Category { Name = "Toys & Games", Description = "Toys, games, and entertainment" }; + var automotive = new Category { Name = "Automotive", Description = "Car parts and automotive accessories" }; + var health = new Category { Name = "Health & Household", Description = "Health products and household essentials" }; + var office = new Category { Name = "Office Products", Description = "Office supplies and equipment" }; + + Categories.AddRange(electronics, clothing, books, homeAndGarden, sports, beauty, toys, automotive, health, office); + + // Save categories first to get their generated IDs + SaveChanges(); + + // Seed Products - Use the generated CategoryIds (55 products total) + var products = new List + { + // Electronics (10 products) + new Product { Name = "Laptop", Description = "High-performance laptop", Price = 999.99m, Stock = 10, IsActive = true, CategoryId = electronics.CategoryId }, + new Product { Name = "Smartphone", Description = "Latest smartphone model", Price = 699.99m, Stock = 15, IsActive = true, CategoryId = electronics.CategoryId }, + new Product { Name = "Wireless Headphones", Description = "Noise-cancelling wireless headphones", Price = 199.99m, Stock = 25, IsActive = true, CategoryId = electronics.CategoryId }, + new Product { Name = "Smart TV 55\"", Description = "4K Ultra HD Smart Television", Price = 799.99m, Stock = 8, IsActive = true, CategoryId = electronics.CategoryId }, + new Product { Name = "Gaming Console", Description = "Next-gen gaming console", Price = 499.99m, Stock = 12, IsActive = true, CategoryId = electronics.CategoryId }, + new Product { Name = "Tablet", Description = "10-inch Android tablet", Price = 299.99m, Stock = 20, IsActive = true, CategoryId = electronics.CategoryId }, + new Product { Name = "Bluetooth Speaker", Description = "Portable wireless speaker", Price = 79.99m, Stock = 35, IsActive = true, CategoryId = electronics.CategoryId }, + new Product { Name = "Smart Watch", Description = "Fitness tracking smartwatch", Price = 249.99m, Stock = 18, IsActive = true, CategoryId = electronics.CategoryId }, + new Product { Name = "External Hard Drive", Description = "2TB portable hard drive", Price = 89.99m, Stock = 22, IsActive = true, CategoryId = electronics.CategoryId }, + new Product { Name = "Wireless Router", Description = "High-speed WiFi router", Price = 129.99m, Stock = 14, IsActive = true, CategoryId = electronics.CategoryId }, + + // Clothing (8 products) + new Product { Name = "T-Shirt", Description = "Cotton t-shirt", Price = 19.99m, Stock = 50, IsActive = true, CategoryId = clothing.CategoryId }, + new Product { Name = "Jeans", Description = "Denim jeans", Price = 49.99m, Stock = 30, IsActive = true, CategoryId = clothing.CategoryId }, + new Product { Name = "Running Shoes", Description = "Comfortable athletic shoes", Price = 89.99m, Stock = 25, IsActive = true, CategoryId = clothing.CategoryId }, + new Product { Name = "Winter Jacket", Description = "Warm winter coat", Price = 129.99m, Stock = 15, IsActive = true, CategoryId = clothing.CategoryId }, + new Product { Name = "Dress Shirt", Description = "Formal dress shirt", Price = 39.99m, Stock = 40, IsActive = true, CategoryId = clothing.CategoryId }, + new Product { Name = "Yoga Pants", Description = "Comfortable yoga pants", Price = 34.99m, Stock = 28, IsActive = true, CategoryId = clothing.CategoryId }, + new Product { Name = "Baseball Cap", Description = "Adjustable baseball cap", Price = 16.99m, Stock = 60, IsActive = true, CategoryId = clothing.CategoryId }, + new Product { Name = "Leather Belt", Description = "Genuine leather belt", Price = 29.99m, Stock = 35, IsActive = true, CategoryId = clothing.CategoryId }, + + // Books (6 products) + new Product { Name = "Programming Book", Description = "Learn C# programming", Price = 39.99m, Stock = 20, IsActive = true, CategoryId = books.CategoryId }, + new Product { Name = "Science Fiction Novel", Description = "Bestselling sci-fi adventure", Price = 14.99m, Stock = 45, IsActive = true, CategoryId = books.CategoryId }, + new Product { Name = "Cookbook", Description = "Italian cuisine recipes", Price = 24.99m, Stock = 30, IsActive = true, CategoryId = books.CategoryId }, + new Product { Name = "Biography", Description = "Life story of a famous inventor", Price = 19.99m, Stock = 25, IsActive = true, CategoryId = books.CategoryId }, + new Product { Name = "Children's Book", Description = "Educational picture book", Price = 12.99m, Stock = 50, IsActive = true, CategoryId = books.CategoryId }, + new Product { Name = "Textbook", Description = "Advanced mathematics textbook", Price = 89.99m, Stock = 10, IsActive = true, CategoryId = books.CategoryId }, + + // Home & Garden (6 products) + new Product { Name = "Garden Hose", Description = "50ft expandable garden hose", Price = 34.99m, Stock = 20, IsActive = true, CategoryId = homeAndGarden.CategoryId }, + new Product { Name = "Lawn Mower", Description = "Electric lawn mower", Price = 249.99m, Stock = 8, IsActive = true, CategoryId = homeAndGarden.CategoryId }, + new Product { Name = "Paint Set", Description = "Interior paint set with brushes", Price = 79.99m, Stock = 15, IsActive = true, CategoryId = homeAndGarden.CategoryId }, + new Product { Name = "Tool Set", Description = "150-piece tool set", Price = 89.99m, Stock = 12, IsActive = true, CategoryId = homeAndGarden.CategoryId }, + new Product { Name = "Coffee Maker", Description = "12-cup coffee maker", Price = 59.99m, Stock = 18, IsActive = true, CategoryId = homeAndGarden.CategoryId }, + new Product { Name = "Blender", Description = "High-speed kitchen blender", Price = 69.99m, Stock = 22, IsActive = true, CategoryId = homeAndGarden.CategoryId }, + + // Sports & Outdoors (6 products) + new Product { Name = "Tennis Racket", Description = "Professional tennis racket", Price = 149.99m, Stock = 10, IsActive = true, CategoryId = sports.CategoryId }, + new Product { Name = "Yoga Mat", Description = "Non-slip yoga mat", Price = 29.99m, Stock = 40, IsActive = true, CategoryId = sports.CategoryId }, + new Product { Name = "Dumbbells Set", Description = "Adjustable dumbbells 5-50lbs", Price = 199.99m, Stock = 6, IsActive = true, CategoryId = sports.CategoryId }, + new Product { Name = "Camping Tent", Description = "4-person camping tent", Price = 119.99m, Stock = 12, IsActive = true, CategoryId = sports.CategoryId }, + new Product { Name = "Bicycle Helmet", Description = "Safety bicycle helmet", Price = 39.99m, Stock = 25, IsActive = true, CategoryId = sports.CategoryId }, + new Product { Name = "Swimming Goggles", Description = "Anti-fog swimming goggles", Price = 14.99m, Stock = 35, IsActive = true, CategoryId = sports.CategoryId }, + + // Beauty & Personal Care (5 products) + new Product { Name = "Shampoo", Description = "Moisturizing shampoo 16oz", Price = 8.99m, Stock = 60, IsActive = true, CategoryId = beauty.CategoryId }, + new Product { Name = "Face Cream", Description = "Anti-aging face cream", Price = 24.99m, Stock = 30, IsActive = true, CategoryId = beauty.CategoryId }, + new Product { Name = "Hair Dryer", Description = "Professional hair dryer", Price = 49.99m, Stock = 15, IsActive = true, CategoryId = beauty.CategoryId }, + new Product { Name = "Makeup Set", Description = "Complete makeup kit", Price = 79.99m, Stock = 20, IsActive = true, CategoryId = beauty.CategoryId }, + new Product { Name = "Perfume", Description = "Designer fragrance 3.4oz", Price = 89.99m, Stock = 12, IsActive = true, CategoryId = beauty.CategoryId }, + + // Toys & Games (5 products) + new Product { Name = "Board Game", Description = "Strategy board game for family", Price = 34.99m, Stock = 25, IsActive = true, CategoryId = toys.CategoryId }, + new Product { Name = "Building Blocks", Description = "Creative building block set", Price = 49.99m, Stock = 18, IsActive = true, CategoryId = toys.CategoryId }, + new Product { Name = "Puzzle 1000pc", Description = "1000-piece jigsaw puzzle", Price = 19.99m, Stock = 30, IsActive = true, CategoryId = toys.CategoryId }, + new Product { Name = "Stuffed Animal", Description = "Soft plush teddy bear", Price = 14.99m, Stock = 40, IsActive = true, CategoryId = toys.CategoryId }, + new Product { Name = "Remote Control Car", Description = "RC stunt car", Price = 39.99m, Stock = 15, IsActive = true, CategoryId = toys.CategoryId }, + + // Automotive (4 products) + new Product { Name = "Car Wash Kit", Description = "Complete car cleaning kit", Price = 29.99m, Stock = 20, IsActive = true, CategoryId = automotive.CategoryId }, + new Product { Name = "Tire Pressure Gauge", Description = "Digital tire pressure gauge", Price = 12.99m, Stock = 35, IsActive = true, CategoryId = automotive.CategoryId }, + new Product { Name = "Car Air Freshener", Description = "Long-lasting car air freshener", Price = 6.99m, Stock = 50, IsActive = true, CategoryId = automotive.CategoryId }, + new Product { Name = "Phone Mount", Description = "Dashboard phone holder", Price = 16.99m, Stock = 28, IsActive = true, CategoryId = automotive.CategoryId }, + + // Health & Household (3 products) + new Product { Name = "First Aid Kit", Description = "Complete first aid kit", Price = 24.99m, Stock = 15, IsActive = true, CategoryId = health.CategoryId }, + new Product { Name = "Vitamins", Description = "Multivitamin supplement", Price = 19.99m, Stock = 40, IsActive = true, CategoryId = health.CategoryId }, + new Product { Name = "Laundry Detergent", Description = "Concentrated laundry detergent", Price = 11.99m, Stock = 45, IsActive = true, CategoryId = health.CategoryId }, + + // Office Products (2 products) + new Product { Name = "Office Chair", Description = "Ergonomic office chair", Price = 199.99m, Stock = 8, IsActive = true, CategoryId = office.CategoryId }, + new Product { Name = "Printer Paper", Description = "500 sheets printer paper", Price = 7.99m, Stock = 100, IsActive = true, CategoryId = office.CategoryId } + }; + + Products.AddRange(products); + SaveChanges(); + + // Get all products for sales generation + var allProducts = Products.ToList(); + + // Create some sales data - mix of current and historical sales + var sales = new List(); + var random = new Random(42); // Fixed seed for reproducible data + + // Generate 50 sales with various dates + for (int i = 0; i < 50; i++) + { + // Create sales from the last 2 years to test temporal filtering + var saleDate = DateTime.UtcNow.AddDays(-random.Next(0, 730)); // 0-730 days ago + + // Select 1-5 random products for each sale + var saleProducts = allProducts.OrderBy(x => random.Next()).Take(random.Next(1, 6)).ToList(); + + var saleItems = new List(); + decimal totalAmount = 0; + + foreach (var product in saleProducts) + { + var quantity = random.Next(1, 4); // 1-3 items + var unitPrice = product.Price; + totalAmount += unitPrice * quantity; + + saleItems.Add(new SaleItem + { + ProductId = product.ProductId, + Quantity = quantity, + UnitPrice = unitPrice + }); + } + + // Generate fake customer data + var customerNames = new[] { "John Doe", "Jane Smith", "Bob Johnson", "Alice Brown", "Charlie Wilson", "Diana Davis", "Edward Miller", "Fiona Garcia", "George Rodriguez", "Helen Martinez" }; + var customerName = customerNames[random.Next(customerNames.Length)]; + var customerEmail = $"{customerName.ToLower().Replace(" ", ".")}@example.com"; + var addresses = new[] { "123 Main St", "456 Oak Ave", "789 Pine Rd", "321 Elm St", "654 Maple Dr", "987 Cedar Ln", "147 Birch Blvd", "258 Spruce Way", "369 Willow Ct", "741 Poplar Pl" }; + var customerAddress = $"{addresses[random.Next(addresses.Length)]}, Anytown, ST 12345"; + + sales.Add(new Sale + { + SaleDate = saleDate, + TotalAmount = totalAmount, + CustomerName = customerName, + CustomerEmail = customerEmail, + CustomerAddress = customerAddress, + SaleItems = saleItems + }); + } + + // Add some products that will be soft-deleted to test temporal filtering + // These products will be deleted after some sales but before others + var productsToDelete = allProducts.Where(p => p.CategoryId == electronics.CategoryId).Take(3).ToList(); + var deletionDate = DateTimeOffset.UtcNow.AddDays(-30); // Deleted 30 days ago + + foreach (var product in productsToDelete) + { + product.SoftDelete(); + product.DeletedAt = deletionDate; + } + + // Create some sales after the deletion date that shouldn't include deleted products + for (int i = 0; i < 10; i++) + { + var saleDate = DateTime.UtcNow.AddDays(-random.Next(1, 29)); // 1-29 days ago (after deletion) + + // Only select products that are not deleted + var availableProducts = allProducts.Where(p => !p.IsDeleted).ToList(); + var saleProducts = availableProducts.OrderBy(x => random.Next()).Take(random.Next(1, 4)).ToList(); + + if (saleProducts.Count > 0) + { + var saleItems = new List(); + decimal totalAmount = 0; + + foreach (var product in saleProducts) + { + var quantity = random.Next(1, 3); + var unitPrice = product.Price; + totalAmount += unitPrice * quantity; + + saleItems.Add(new SaleItem + { + ProductId = product.ProductId, + Quantity = quantity, + UnitPrice = unitPrice + }); + } + + // Generate fake customer data + var customerNames = new[] { "John Doe", "Jane Smith", "Bob Johnson", "Alice Brown", "Charlie Wilson", "Diana Davis", "Edward Miller", "Fiona Garcia", "George Rodriguez", "Helen Martinez" }; + var customerName = customerNames[random.Next(customerNames.Length)]; + var customerEmail = $"{customerName.ToLower().Replace(" ", ".")}@example.com"; + var addresses = new[] { "123 Main St", "456 Oak Ave", "789 Pine Rd", "321 Elm St", "654 Maple Dr", "987 Cedar Ln", "147 Birch Blvd", "258 Spruce Way", "369 Willow Ct", "741 Poplar Pl" }; + var customerAddress = $"{addresses[random.Next(addresses.Length)]}, Anytown, ST 12345"; + + sales.Add(new Sale + { + SaleDate = saleDate, + TotalAmount = totalAmount, + CustomerName = customerName, + CustomerEmail = customerEmail, + CustomerAddress = customerAddress, + SaleItems = saleItems + }); + } + } + + Sales.AddRange(sales); + SaveChanges(); + } +} diff --git a/Data/Migrations/20250924200106_RemoveQuantityFromSales.cs b/Data/Migrations/20250924200106_RemoveQuantityFromSales.cs new file mode 100644 index 00000000..927ae962 --- /dev/null +++ b/Data/Migrations/20250924200106_RemoveQuantityFromSales.cs @@ -0,0 +1,92 @@ +īģŋusing Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ECommerceApp.RyanW84.Data.Migrations +{ + /// + public partial class RemoveQuantityFromSales : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_Sales_Categories_CategoryId", + table: "Sales"); + + migrationBuilder.DropForeignKey( + name: "FK_Sales_Products_ProductId", + table: "Sales"); + + migrationBuilder.DropIndex( + name: "IX_Sales_CategoryId", + table: "Sales"); + + migrationBuilder.DropIndex( + name: "IX_Sales_ProductId", + table: "Sales"); + + migrationBuilder.DropColumn( + name: "Quantity", + table: "Sales"); + + migrationBuilder.DropColumn( + name: "ProductId", + table: "Sales"); + + migrationBuilder.DropColumn( + name: "CategoryId", + table: "Sales"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "CategoryId", + table: "Sales", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "ProductId", + table: "Sales", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "Quantity", + table: "Sales", + type: "int", + nullable: false, + defaultValue: 0); + + migrationBuilder.CreateIndex( + name: "IX_Sales_CategoryId", + table: "Sales", + column: "CategoryId"); + + migrationBuilder.CreateIndex( + name: "IX_Sales_ProductId", + table: "Sales", + column: "ProductId"); + + migrationBuilder.AddForeignKey( + name: "FK_Sales_Categories_CategoryId", + table: "Sales", + column: "CategoryId", + principalTable: "Categories", + principalColumn: "CategoryId"); + + migrationBuilder.AddForeignKey( + name: "FK_Sales_Products_ProductId", + table: "Sales", + column: "ProductId", + principalTable: "Products", + principalColumn: "ProductId", + onDelete: ReferentialAction.Cascade); + } + } +} diff --git a/Data/Migrations/20250924200514_CreateSaleItemsTable.cs b/Data/Migrations/20250924200514_CreateSaleItemsTable.cs new file mode 100644 index 00000000..39519eff --- /dev/null +++ b/Data/Migrations/20250924200514_CreateSaleItemsTable.cs @@ -0,0 +1,52 @@ +īģŋusing Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ECommerceApp.RyanW84.Data.Migrations +{ + /// + public partial class CreateSaleItemsTable : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "SaleItems", + columns: table => new + { + SaleId = table.Column(type: "int", nullable: false), + ProductId = table.Column(type: "int", nullable: false), + Quantity = table.Column(type: "int", nullable: false), + UnitPrice = table.Column(type: "decimal(18,2)", precision: 18, scale: 2, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_SaleItems", x => new { x.SaleId, x.ProductId }); + table.ForeignKey( + name: "FK_SaleItems_Products_ProductId", + column: x => x.ProductId, + principalTable: "Products", + principalColumn: "ProductId", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_SaleItems_Sales_SaleId", + column: x => x.SaleId, + principalTable: "Sales", + principalColumn: "SaleId", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_SaleItems_ProductId", + table: "SaleItems", + column: "ProductId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "SaleItems"); + } + } +} diff --git a/Data/Models/BaseEntity.cs b/Data/Models/BaseEntity.cs new file mode 100644 index 00000000..e64dd622 --- /dev/null +++ b/Data/Models/BaseEntity.cs @@ -0,0 +1,27 @@ +namespace ECommerceApp.RyanW84.Data.Models; + +public abstract class BaseEntity +{ + public bool IsDeleted { get; set; } + public DateTimeOffset? DeletedAt { get; set; } + public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow; + public string? CreatedBy { get; set; } + public DateTimeOffset? UpdatedAt { get; set; } + public string? UpdatedBy { get; set; } + + // Soft delete method + public void SoftDelete() + { + IsDeleted = true; + DeletedAt = DateTimeOffset.UtcNow; + UpdatedAt = DateTimeOffset.UtcNow; + } + + // Restore from soft delete + public void Restore() + { + IsDeleted = false; + DeletedAt = null; + UpdatedAt = DateTimeOffset.UtcNow; + } +} diff --git a/Data/Models/Category.cs b/Data/Models/Category.cs new file mode 100644 index 00000000..9e96f347 --- /dev/null +++ b/Data/Models/Category.cs @@ -0,0 +1,40 @@ +using System.Text.Json.Serialization; + +namespace ECommerceApp.RyanW84.Data.Models; + +/// +/// Category domain entity. +/// Represents a logical grouping of products (e.g., Electronics) and participates in sales reporting. +/// +public class Category : BaseEntity +{ + // EF Core needs settable properties for materialization + /// + /// Primary key for the category. + /// + public int CategoryId { get; set; } + + /// + /// Display name of the category. + /// + public required string Name { get; set; } = null!; + + /// + /// Human-readable description shown to clients. + /// + public required string Description { get; set; } = null!; + + // Navigation property to Products - don't serialize to avoid circular references + /// + /// Products currently assigned to this category. + /// + [JsonIgnore] + public ICollection Products { get; set; } = new List(); + + // Navigation property to Sales - don't serialize to avoid circular references + /// + /// Sales associated with this category (via many-to-many relationship). + /// + [JsonIgnore] + public ICollection Sales { get; set; } = new List(); +} diff --git a/Data/Models/Product.cs b/Data/Models/Product.cs new file mode 100644 index 00000000..68a540e2 --- /dev/null +++ b/Data/Models/Product.cs @@ -0,0 +1,54 @@ +using System.Text.Json.Serialization; + +namespace ECommerceApp.RyanW84.Data.Models; + +/// +/// Product domain entity. +/// Represents a sellable catalog item with pricing, stock, an owning category, and optional sale history. +/// +public class Product : BaseEntity +{ + /// + /// Primary key for the product. + /// + public int ProductId { get; set; } + + /// + /// Display name shown in lists and detail views. + /// + public required string Name { get; set; } = null!; + + /// + /// Human-readable product description. + /// + public required string Description { get; set; } = null!; + + /// + /// Current unit price. + /// + public required decimal Price { get; set; } + + /// + /// Current stock level. + /// + public required int Stock { get; set; } + + /// + /// Indicates whether the product is active and should be offered for sale. + /// + public required bool IsActive { get; set; } + + /// + /// Foreign key to the owning . + /// + public int CategoryId { get; set; } + + /// + /// Navigation property to the owning category. + /// + public Category? Category { get; set; } + + // Don't serialize SaleItems to avoid circular references + [JsonIgnore] + public ICollection SaleItems { get; set; } = new List(); +} diff --git a/Data/Models/Sale.cs b/Data/Models/Sale.cs new file mode 100644 index 00000000..94a9981a --- /dev/null +++ b/Data/Models/Sale.cs @@ -0,0 +1,55 @@ +using System.Text.Json.Serialization; + +namespace ECommerceApp.RyanW84.Data.Models; + +/// +/// Sale domain entity. +/// Represents a customer purchase transaction with customer details and one or more line items. +/// +public class Sale +{ + // EF Core needs settable properties for materialization + /// + /// Primary key for the sale. + /// + public int SaleId { get; set; } + + /// + /// Timestamp for when the sale occurred. + /// + public DateTime SaleDate { get; set; } + + /// + /// Total sale amount, typically derived from item totals. + /// + public decimal TotalAmount { get; set; } + + /// + /// Customer display name associated with the sale. + /// + public required string CustomerName { get; set; } = null!; + + /// + /// Customer email associated with the sale. + /// + public required string CustomerEmail { get; set; } = null!; + + /// + /// Customer address associated with the sale. + /// + public required string CustomerAddress { get; set; } = null!; + + // Sale has many items (each item references a product) + /// + /// Line items belonging to this sale. + /// + public ICollection SaleItems { get; set; } = new List(); + + // Don't serialize Categories to avoid circular references + /// + /// Categories associated with this sale (via product/category relationships). + /// Not serialized to avoid cycles. + /// + [JsonIgnore] + public ICollection Categories { get; set; } = new List(); +} diff --git a/Data/Models/SaleItem.cs b/Data/Models/SaleItem.cs new file mode 100644 index 00000000..c298ec10 --- /dev/null +++ b/Data/Models/SaleItem.cs @@ -0,0 +1,48 @@ +using System.Text.Json.Serialization; + +namespace ECommerceApp.RyanW84.Data.Models; + +/// +/// Sale line item entity. +/// Captures the product, quantity, and unit price at the time of purchase. +/// +public class SaleItem +{ + /// + /// Foreign key to the owning . + /// + public int SaleId { get; set; } + + // Don't serialize Sale to avoid circular references + /// + /// Navigation property to the owning sale. + /// Not serialized to avoid cycles. + /// + [JsonIgnore] + public Sale? Sale { get; set; } + + /// + /// Foreign key to the purchased . + /// + public int ProductId { get; set; } + + /// + /// Navigation property to the purchased product. + /// + public Product? Product { get; set; } + + /// + /// Quantity purchased. + /// + public int Quantity { get; set; } + + /// + /// Unit price captured at purchase time. + /// + public decimal UnitPrice { get; set; } + + /// + /// Computed total for this line item. + /// + public decimal LineTotal => UnitPrice * Quantity; +} diff --git a/Data/Models/SalesSummary.cs b/Data/Models/SalesSummary.cs new file mode 100644 index 00000000..65431b94 --- /dev/null +++ b/Data/Models/SalesSummary.cs @@ -0,0 +1,33 @@ +namespace ECommerceApp.RyanW84.Data.Models; + +/// +/// Lightweight sales reporting projection. +/// Summarizes sales performance for a product/category combination across a time range. +/// +public class SalesSummary +{ + /// + /// Name of the product being summarized. + /// + public string ProductName { get; set; } = null!; + + /// + /// Name of the category the product belongs to. + /// + public string CategoryName { get; set; } = null!; + + /// + /// Total units sold over the aggregation period. + /// + public int TotalQuantitySold { get; set; } + + /// + /// Total revenue over the aggregation period. + /// + public decimal TotalRevenue { get; set; } + + /// + /// Timestamp of the most recent sale included in the aggregation. + /// + public DateTime LastSaleDate { get; set; } +} diff --git a/Data/Seeding/DatabaseSeeder.cs b/Data/Seeding/DatabaseSeeder.cs new file mode 100644 index 00000000..9aa40916 --- /dev/null +++ b/Data/Seeding/DatabaseSeeder.cs @@ -0,0 +1,279 @@ +using ECommerceApp.RyanW84.Data.Models; + +namespace ECommerceApp.RyanW84.Data.Seeding; + +/// +/// Handles database seeding with initial test data for products, categories, and sales. +/// Follows SRP by isolating seed data logic from DbContext. +/// +public static class DatabaseSeeder +{ + public static void SeedDatabase(ECommerceDbContext context) + { + if (HasExistingData(context)) + { + ClearExistingData(context); + } + + var categories = SeedCategories(context); + var products = SeedProducts(context, categories); + SeedSales(context, products); + + context.SaveChanges(); + } + + private static bool HasExistingData(ECommerceDbContext context) + { + return context.Categories.Any() || context.Products.Any() || context.Sales.Any(); + } + + private static void ClearExistingData(ECommerceDbContext context) + { + context.Sales.RemoveRange(context.Sales); + context.SaleItems.RemoveRange(context.SaleItems); + context.Products.RemoveRange(context.Products); + context.Categories.RemoveRange(context.Categories); + context.SaveChanges(); + } + + private static List SeedCategories(ECommerceDbContext context) + { + var categories = new[] + { + new Category { Name = "Electronics", Description = "Electronic devices and gadgets" }, + new Category { Name = "Clothing", Description = "Apparel and fashion items" }, + new Category { Name = "Books", Description = "Books and publications" }, + new Category + { + Name = "Home & Garden", + Description = "Home improvement and gardening supplies", + }, + new Category { Name = "Sports", Description = "Sports and outdoor equipment" }, + new Category { Name = "Furniture", Description = "Home furniture and decor" }, + new Category { Name = "Toys", Description = "Toys and games for all ages" }, + new Category { Name = "Beauty", Description = "Beauty and personal care products" }, + new Category { Name = "Food & Beverage", Description = "Food and drink items" }, + new Category { Name = "Automotive", Description = "Car accessories and parts" }, + }; + + context.Categories.AddRange(categories); + context.SaveChanges(); + return categories.ToList(); + } + + private static List SeedProducts(ECommerceDbContext context, List categories) + { + var products = GetProductSeedData(categories); + context.Products.AddRange(products); + context.SaveChanges(); + return products.ToList(); + } + + private static Product[] GetProductSeedData(List categories) + { + var products = new List(); + var random = new Random(42); + var productNames = BuildProductNames(); + var descriptions = BuildProductDescriptions(); + + int productIndex = 0; + DistributeProductsAcrossCategories( + categories, + productNames, + descriptions, + random, + products, + ref productIndex + ); + FillRemainingProducts( + categories, + productNames, + descriptions, + random, + products, + ref productIndex + ); + + return products.ToArray(); + } + + private static string[] BuildProductNames() + { + return new[] + { + "Laptop", + "Mouse", + "Keyboard", + "Monitor", + "Headphones", + "Webcam", + "USB Hub", + "External SSD", + "Graphics Card", + "Power Supply", + "RAM Module", + "Motherboard", + "CPU Cooler", + "Case Fan", + "HDMI Cable", + "T-Shirt", + "Jeans", + "Jacket", + "Sweater", + "Shorts", + "Socks", + "Hat", + "Scarf", + "Gloves", + "Shoes", + "C# Programming", + "Design Patterns", + "Clean Code", + "The Pragmatic Programmer", + "Code Complete", + "Garden Hose", + "Lawn Mower", + "Paint Set", + "Tool Set", + "Light Fixture", + "Basketball", + "Yoga Mat", + "Running Shoes", + "Tennis Racket", + "Dumbbell Set", + "Office Chair", + "Desk", + "Bookshelf", + "Board Game", "Action Figure", + }; + } + + private static string[] BuildProductDescriptions() + { + return new[] + { + "High-quality item", + "Premium product", + "Professional grade", + "Best seller", + "Durable and reliable", + "Great value", + "Excellent quality", + "Top rated", + "Customer favorite", + "Long lasting", + }; + } + + private static void DistributeProductsAcrossCategories( + List categories, + string[] productNames, + string[] descriptions, + Random random, + List products, + ref int productIndex + ) + { + foreach (var category in categories) + { + int productsPerCategory = productIndex < 45 ? 5 : (productIndex < 50 ? 10 : 0); + for (int i = 0; i < productsPerCategory && productIndex < 50; i++) + { + products.Add( + CreateProduct( + productNames, + descriptions, + random, + category.CategoryId, + productIndex + ) + ); + productIndex++; + } + } + } + + private static void FillRemainingProducts( + List categories, + string[] productNames, + string[] descriptions, + Random random, + List products, + ref int productIndex + ) + { + while (productIndex < 50) + { + var randomCategoryId = categories[random.Next(categories.Count)].CategoryId; + products.Add( + CreateProduct(productNames, descriptions, random, randomCategoryId, productIndex) + ); + productIndex++; + } + } + + private static Product CreateProduct( + string[] productNames, + string[] descriptions, + Random random, + int categoryId, + int productIndex + ) + { + return new Product + { + Name = + $"{productNames[productIndex % productNames.Length]} {(productIndex / productNames.Length) + 1}", + Description = descriptions[random.Next(descriptions.Length)], + Price = Math.Round((decimal)(10 + random.Next(0, 500) + random.NextDouble()), 2), + Stock = random.Next(5, 200), + IsActive = true, + CategoryId = categoryId, + }; + } + + private static void SeedSales(ECommerceDbContext context, List products) + { + var random = new Random(42); + const int saleCount = 100; + + for (int i = 0; i < saleCount; i++) + { + var saleDate = DateTime.UtcNow.AddDays(-random.Next(0, 730)); + var saleProducts = products + .OrderBy(_ => random.Next()) + .Take(random.Next(1, 6)) + .ToList(); + var saleItems = new List(); + decimal total = 0; + + foreach (var product in saleProducts) + { + var quantity = random.Next(1, 4); + var lineTotal = product.Price * quantity; + total += lineTotal; + + saleItems.Add( + new SaleItem + { + ProductId = product.ProductId, + Quantity = quantity, + UnitPrice = product.Price, + } + ); + } + + var sale = new Sale + { + SaleDate = saleDate, + CustomerName = $"Customer {i + 1}", + CustomerEmail = $"customer{i + 1}@example.com", + CustomerAddress = $"{i + 1} Main St", + TotalAmount = total, + SaleItems = saleItems, + }; + + context.Sales.Add(sale); + } + } +} diff --git a/ECommerceApp.RyanW84.sln b/ECommerceApp.RyanW84.sln new file mode 100644 index 00000000..18487fb1 --- /dev/null +++ b/ECommerceApp.RyanW84.sln @@ -0,0 +1,73 @@ +īģŋ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 18 +VisualStudioVersion = 18.0.11010.61 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ECommerceApp.RyanW84", "ECommerceApp.RyanW84.csproj", "{CE08365A-9671-42FF-BF24-F8A7B9C1A80F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ConsoleClient", "ConsoleClient", "{4271BA7F-337D-145F-AF0F-46C0E07C0FC9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ECommerceApp.ConsoleClient", "ConsoleClient\ECommerceApp.ConsoleClient.csproj", "{4FC36BE2-9316-4BBE-93FE-531DA237E0EC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{B3B0BC5E-0A4E-48BB-9729-2E31ABC6936F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ECommerceApp.ArchitectureTests", "tests\ArchitectureTests\ECommerceApp.ArchitectureTests.csproj", "{1E20FB2A-BA6D-423A-85EE-9A7192E5C76E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {CE08365A-9671-42FF-BF24-F8A7B9C1A80F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE08365A-9671-42FF-BF24-F8A7B9C1A80F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE08365A-9671-42FF-BF24-F8A7B9C1A80F}.Debug|x64.ActiveCfg = Debug|Any CPU + {CE08365A-9671-42FF-BF24-F8A7B9C1A80F}.Debug|x64.Build.0 = Debug|Any CPU + {CE08365A-9671-42FF-BF24-F8A7B9C1A80F}.Debug|x86.ActiveCfg = Debug|Any CPU + {CE08365A-9671-42FF-BF24-F8A7B9C1A80F}.Debug|x86.Build.0 = Debug|Any CPU + {CE08365A-9671-42FF-BF24-F8A7B9C1A80F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE08365A-9671-42FF-BF24-F8A7B9C1A80F}.Release|Any CPU.Build.0 = Release|Any CPU + {CE08365A-9671-42FF-BF24-F8A7B9C1A80F}.Release|x64.ActiveCfg = Release|Any CPU + {CE08365A-9671-42FF-BF24-F8A7B9C1A80F}.Release|x64.Build.0 = Release|Any CPU + {CE08365A-9671-42FF-BF24-F8A7B9C1A80F}.Release|x86.ActiveCfg = Release|Any CPU + {CE08365A-9671-42FF-BF24-F8A7B9C1A80F}.Release|x86.Build.0 = Release|Any CPU + {4FC36BE2-9316-4BBE-93FE-531DA237E0EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4FC36BE2-9316-4BBE-93FE-531DA237E0EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4FC36BE2-9316-4BBE-93FE-531DA237E0EC}.Debug|x64.ActiveCfg = Debug|Any CPU + {4FC36BE2-9316-4BBE-93FE-531DA237E0EC}.Debug|x64.Build.0 = Debug|Any CPU + {4FC36BE2-9316-4BBE-93FE-531DA237E0EC}.Debug|x86.ActiveCfg = Debug|Any CPU + {4FC36BE2-9316-4BBE-93FE-531DA237E0EC}.Debug|x86.Build.0 = Debug|Any CPU + {4FC36BE2-9316-4BBE-93FE-531DA237E0EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4FC36BE2-9316-4BBE-93FE-531DA237E0EC}.Release|Any CPU.Build.0 = Release|Any CPU + {4FC36BE2-9316-4BBE-93FE-531DA237E0EC}.Release|x64.ActiveCfg = Release|Any CPU + {4FC36BE2-9316-4BBE-93FE-531DA237E0EC}.Release|x64.Build.0 = Release|Any CPU + {4FC36BE2-9316-4BBE-93FE-531DA237E0EC}.Release|x86.ActiveCfg = Release|Any CPU + {4FC36BE2-9316-4BBE-93FE-531DA237E0EC}.Release|x86.Build.0 = Release|Any CPU + {1E20FB2A-BA6D-423A-85EE-9A7192E5C76E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1E20FB2A-BA6D-423A-85EE-9A7192E5C76E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E20FB2A-BA6D-423A-85EE-9A7192E5C76E}.Debug|x64.ActiveCfg = Debug|Any CPU + {1E20FB2A-BA6D-423A-85EE-9A7192E5C76E}.Debug|x64.Build.0 = Debug|Any CPU + {1E20FB2A-BA6D-423A-85EE-9A7192E5C76E}.Debug|x86.ActiveCfg = Debug|Any CPU + {1E20FB2A-BA6D-423A-85EE-9A7192E5C76E}.Debug|x86.Build.0 = Debug|Any CPU + {1E20FB2A-BA6D-423A-85EE-9A7192E5C76E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1E20FB2A-BA6D-423A-85EE-9A7192E5C76E}.Release|Any CPU.Build.0 = Release|Any CPU + {1E20FB2A-BA6D-423A-85EE-9A7192E5C76E}.Release|x64.ActiveCfg = Release|Any CPU + {1E20FB2A-BA6D-423A-85EE-9A7192E5C76E}.Release|x64.Build.0 = Release|Any CPU + {1E20FB2A-BA6D-423A-85EE-9A7192E5C76E}.Release|x86.ActiveCfg = Release|Any CPU + {1E20FB2A-BA6D-423A-85EE-9A7192E5C76E}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {4FC36BE2-9316-4BBE-93FE-531DA237E0EC} = {4271BA7F-337D-145F-AF0F-46C0E07C0FC9} + {1E20FB2A-BA6D-423A-85EE-9A7192E5C76E} = {B3B0BC5E-0A4E-48BB-9729-2E31ABC6936F} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {E254F0F7-1A99-4D0B-A4A8-777C5F749256} + EndGlobalSection +EndGlobal diff --git a/Hosting/DatabaseInitializerHostedService.cs b/Hosting/DatabaseInitializerHostedService.cs new file mode 100644 index 00000000..a73a3405 --- /dev/null +++ b/Hosting/DatabaseInitializerHostedService.cs @@ -0,0 +1,59 @@ +using ECommerceApp.RyanW84.Data; +using Microsoft.EntityFrameworkCore; + +namespace ECommerceApp.RyanW84.Hosting; + +/// +/// Applies migrations at startup. In Development, optionally resets and seeds the database. +/// Keeps Program.cs lean and satisfies SRP. +/// +public class DatabaseInitializerHostedService(IServiceProvider services, + IWebHostEnvironment env, + ILogger logger) : IHostedService +{ + public async Task StartAsync(CancellationToken cancellationToken) + { + using var scope = services.CreateScope(); + var db = scope.ServiceProvider.GetRequiredService(); + + try + { + // For development, use a reset flag instead of always deleting the database + // This avoids the expensive EnsureDeletedAsync call on every startup + // Only reset if the environment variable is explicitly set + if (env.IsDevelopment() && IsResetRequested()) + { + logger.LogInformation("Development mode: resetting database as requested..."); + await db.Database.EnsureDeletedAsync(cancellationToken); + } + + await db.Database.MigrateAsync(cancellationToken); + logger.LogInformation("Database migrations applied."); + + if (env.IsDevelopment() && IsResetRequested()) + { + db.SeedData(); + await db.SaveChangesAsync(cancellationToken); + logger.LogInformation("Database seeded with initial data."); + } + } + catch (Exception ex) + { + logger.LogError(ex, "An error occurred while migrating or seeding the database."); + throw; + } + } + + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + + /// + /// Checks if database reset is requested via environment variable. + /// Set ECOMMERCE_RESET_DB=true to reset the database on startup. + /// + private static bool IsResetRequested() + { + var resetEnv = Environment.GetEnvironmentVariable("ECOMMERCE_RESET_DB"); + return resetEnv?.Equals("true", StringComparison.OrdinalIgnoreCase) ?? false; + } +} + diff --git a/Interfaces/Helpers/ICategoryProcessingHelper.cs b/Interfaces/Helpers/ICategoryProcessingHelper.cs new file mode 100644 index 00000000..41e4f415 --- /dev/null +++ b/Interfaces/Helpers/ICategoryProcessingHelper.cs @@ -0,0 +1,13 @@ +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; + +namespace ECommerceApp.RyanW84.Interfaces.Helpers; + +public interface ICategoryProcessingHelper +{ + ApiResponseDto? ValidateCreateRequest(ApiRequestDto request); + + Category PrepareForCreate(Category incoming); + + Category PrepareForUpdate(Category existing, Category incoming, int id); +} diff --git a/Interfaces/Helpers/IProductProcessingHelper.cs b/Interfaces/Helpers/IProductProcessingHelper.cs new file mode 100644 index 00000000..700973e7 --- /dev/null +++ b/Interfaces/Helpers/IProductProcessingHelper.cs @@ -0,0 +1,11 @@ +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; + +namespace ECommerceApp.RyanW84.Interfaces.Helpers; + +public interface IProductProcessingHelper +{ + ApiResponseDto? ValidateCreateRequest(ApiRequestDto request); + + Product PrepareForUpdate(Product existing, Product incoming, int id); +} diff --git a/Interfaces/Helpers/IProductQueryHelper.cs b/Interfaces/Helpers/IProductQueryHelper.cs new file mode 100644 index 00000000..222c653e --- /dev/null +++ b/Interfaces/Helpers/IProductQueryHelper.cs @@ -0,0 +1,8 @@ +using ECommerceApp.RyanW84.Data.DTO; + +namespace ECommerceApp.RyanW84.Interfaces.Helpers; + +public interface IProductQueryHelper +{ + void NormalizePriceRange(ProductQueryParameters parameters); +} diff --git a/Interfaces/Helpers/ISaleProcessingHelper.cs b/Interfaces/Helpers/ISaleProcessingHelper.cs new file mode 100644 index 00000000..1053dc63 --- /dev/null +++ b/Interfaces/Helpers/ISaleProcessingHelper.cs @@ -0,0 +1,21 @@ +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; + +namespace ECommerceApp.RyanW84.Interfaces.Helpers; + +public interface ISaleProcessingHelper +{ + ApiResponseDto? ValidateCreateSaleRequest(ApiRequestDto request); + + Task<( + bool IsError, + List Data, + ApiResponseDto Error + )> FetchAndValidateProductsAsync(Sale payload, CancellationToken cancellationToken); + + Task> ExecuteSaleTransactionAsync( + Sale payload, + List products, + CancellationToken cancellationToken + ); +} diff --git a/Interfaces/Helpers/ISaleQueryHelper.cs b/Interfaces/Helpers/ISaleQueryHelper.cs new file mode 100644 index 00000000..1acfc526 --- /dev/null +++ b/Interfaces/Helpers/ISaleQueryHelper.cs @@ -0,0 +1,11 @@ +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; + +namespace ECommerceApp.RyanW84.Interfaces.Helpers; + +public interface ISaleQueryHelper +{ + void NormalizeDateRange(SaleQueryParameters parameters); + void FilterHistoricalItems(Sale sale); + void FilterHistoricalItems(IEnumerable sales); +} diff --git a/Interfaces/ICategoryRepository.cs b/Interfaces/ICategoryRepository.cs new file mode 100644 index 00000000..aaf748f9 --- /dev/null +++ b/Interfaces/ICategoryRepository.cs @@ -0,0 +1,41 @@ +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; + +namespace ECommerceApp.RyanW84.Interfaces; + +public interface ICategoryRepository +{ + Task CategoryExistsAsync( + string categoryName, + CancellationToken cancellationToken = default + ); + Task> GetByIdAsync( + int id, + CancellationToken cancellationToken = default + ); + Task> GetByNameAsync( + string name, + CancellationToken cancellationToken = default + ); + Task>> GetAllCategoriesAsync( + CategoryQueryParameters parameters, + CancellationToken cancellationToken = default + ); + Task> AddAsync( + Category entity, + CancellationToken cancellationToken = default + ); + Task> UpdateAsync( + Category entity, + CancellationToken cancellationToken = default + ); + Task> DeleteAsync(int id, CancellationToken cancellationToken = default); + Task> RestoreAsync(int id, CancellationToken cancellationToken = default); + Task>> GetDeletedCategoriesAsync( + CancellationToken cancellationToken = default + ); + Task> HardDeleteAsync( + int id, + CancellationToken cancellationToken = default + ); +} diff --git a/Interfaces/ICategoryService.cs b/Interfaces/ICategoryService.cs new file mode 100644 index 00000000..909fa09e --- /dev/null +++ b/Interfaces/ICategoryService.cs @@ -0,0 +1,40 @@ +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; + +namespace ECommerceApp.RyanW84.Interfaces; + +public interface ICategoryService +{ + Task> CreateCategoryAsync( + ApiRequestDto request, + CancellationToken cancellationToken = default + ); + Task> GetCategoryAsync( + int id, + CancellationToken cancellationToken = default + ); + Task>> GetAllCategoriesAsync( + CategoryQueryParameters parameters, + CancellationToken cancellationToken = default + ); + Task> UpdateCategoryAsync( + int id, + ApiRequestDto request, + CancellationToken cancellationToken = default + ); + Task> DeleteCategoryAsync( + int id, + CancellationToken cancellationToken = default + ); + Task> GetCategoryByNameAsync( + string name, + CancellationToken cancellationToken = default + ); + Task>> GetDeletedCategoriesAsync( + CancellationToken cancellationToken = default + ); + Task> RestoreCategoryAsync( + int id, + CancellationToken cancellationToken = default + ); +} diff --git a/Interfaces/IProductRepository.cs b/Interfaces/IProductRepository.cs new file mode 100644 index 00000000..ddcca2ba --- /dev/null +++ b/Interfaces/IProductRepository.cs @@ -0,0 +1,33 @@ +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; + +namespace ECommerceApp.RyanW84.Interfaces; + +public interface IProductRepository +{ + + Task> GetByIdAsync( + int id, + CancellationToken cancellationToken = default + ); + Task>> GetAllProductsAsync(ProductQueryParameters parameters, CancellationToken cancellationToken = default); + Task> AddAsync( + Product entity, + CancellationToken cancellationToken = default + ); + Task>> GetProductsByCategoryIdAsync(int categoryId, CancellationToken cancellationToken = default); + Task> UpdateAsync( + Product entity, + CancellationToken cancellationToken = default + ); + Task> DeleteAsync(int id, + CancellationToken cancellationToken = default + ); + Task> RestoreAsync(int id, + CancellationToken cancellationToken = default + ); + Task>> GetDeletedProductsAsync(CancellationToken cancellationToken = default); + Task> HardDeleteAsync(int id, + CancellationToken cancellationToken = default + ); +} diff --git a/Interfaces/IProductService.cs b/Interfaces/IProductService.cs new file mode 100644 index 00000000..96d92757 --- /dev/null +++ b/Interfaces/IProductService.cs @@ -0,0 +1,16 @@ +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; + +namespace ECommerceApp.RyanW84.Interfaces; + +public interface IProductService +{ + Task> CreateProductAsync(ApiRequestDto request, CancellationToken cancellationToken = default); + Task>> GetProductsAsync(ProductQueryParameters parameters, CancellationToken cancellationToken = default); + Task> GetProductByIdAsync(int id, CancellationToken cancellationToken = default); + Task>> GetProductsByCategoryIdAsync(int categoryId, CancellationToken cancellationToken = default); + Task> UpdateProductAsync(int id, ApiRequestDto request, CancellationToken cancellationToken = default); + Task> DeleteProductAsync(int id, CancellationToken cancellationToken = default); + Task>> GetDeletedProductsAsync(CancellationToken cancellationToken = default); + Task> RestoreProductAsync(int id, CancellationToken cancellationToken = default); +} diff --git a/Interfaces/IRepository.cs b/Interfaces/IRepository.cs new file mode 100644 index 00000000..16adbf1c --- /dev/null +++ b/Interfaces/IRepository.cs @@ -0,0 +1,12 @@ +namespace ECommerceApp.RyanW84.Interfaces +{ + public interface IRepository + where T : class + { + Task GetByIdAsync(int id, CancellationToken cancellationToken = default); + Task> ListAsync(CancellationToken cancellationToken = default); + Task AddAsync(T entity, CancellationToken cancellationToken = default); + Task UpdateAsync(T entity, CancellationToken cancellationToken = default); + Task DeleteAsync(T entity, CancellationToken cancellationToken = default); + } +} diff --git a/Interfaces/ISaleRepository.cs b/Interfaces/ISaleRepository.cs new file mode 100644 index 00000000..a71e7cbc --- /dev/null +++ b/Interfaces/ISaleRepository.cs @@ -0,0 +1,32 @@ +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; + +namespace ECommerceApp.RyanW84.Interfaces; + +public interface ISaleRepository +{ + + Task> GetByIdAsync( + int id, + CancellationToken cancellationToken = default + ); + Task>> GetAllSalesAsync(SaleQueryParameters parameters, CancellationToken cancellationToken = default); + Task> AddAsync( + Sale entity, + CancellationToken cancellationToken = default + ); + Task> UpdateAsync( + Sale entity, + CancellationToken cancellationToken = default + ); + Task> DeleteAsync(int id, + CancellationToken cancellationToken = default + ); + Task> GetByIdWithHistoricalProductsAsync( + int id, + CancellationToken cancellationToken = default + ); + Task>> GetHistoricalSalesAsync( + CancellationToken cancellationToken = default + ); +} diff --git a/Interfaces/ISaleService.cs b/Interfaces/ISaleService.cs new file mode 100644 index 00000000..4eae629a --- /dev/null +++ b/Interfaces/ISaleService.cs @@ -0,0 +1,24 @@ +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; + +namespace ECommerceApp.RyanW84.Interfaces; + +public interface ISaleService +{ + Task> CreateSaleAsync(ApiRequestDto request, CancellationToken cancellationToken = default); + Task> GetSaleByIdAsync(int id, CancellationToken cancellationToken = default); + Task>> GetSalesAsync(SaleQueryParameters parameters, CancellationToken cancellationToken = default); + Task> GetSaleByIdWithHistoricalProductsAsync(int id, CancellationToken cancellationToken = default); + Task>> GetHistoricalSalesAsync(CancellationToken cancellationToken = default); + + Task> UpdateSaleAsync( + int id, + ApiRequestDto request, + CancellationToken cancellationToken = default + ); + + Task> DeleteSaleAsync( + int id, + CancellationToken cancellationToken = default + ); +} diff --git a/Interfaces/ISalesSummaryService.cs b/Interfaces/ISalesSummaryService.cs new file mode 100644 index 00000000..d4e01617 --- /dev/null +++ b/Interfaces/ISalesSummaryService.cs @@ -0,0 +1,8 @@ +using ECommerceApp.RyanW84.Data.DTO; + +namespace ECommerceApp.RyanW84.Interfaces; + +public interface ISalesSummaryService +{ + Task> GetSalesSummaryAsync(CancellationToken cancellationToken = default); +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..8d7f1d34 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Ryan W + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Middleware/GlobalExceptionHandlerMiddleware.cs b/Middleware/GlobalExceptionHandlerMiddleware.cs new file mode 100644 index 00000000..4b9773d0 --- /dev/null +++ b/Middleware/GlobalExceptionHandlerMiddleware.cs @@ -0,0 +1,113 @@ +using System.Net; +using System.Text.Json; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.WebUtilities; + +namespace ECommerceApp.RyanW84.Middleware; + +public class GlobalExceptionHandlerMiddleware +{ + private readonly RequestDelegate _next; + private readonly ILogger _logger; + private static readonly JsonSerializerOptions JsonOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = true, + }; + + public GlobalExceptionHandlerMiddleware( + RequestDelegate next, + ILogger logger + ) + { + _next = next; + _logger = logger; + } + + public async Task InvokeAsync(HttpContext context) + { + try + { + await _next(context); + } + catch (Exception ex) + { + _logger.LogError(ex, "An unhandled exception occurred."); + await HandleExceptionAsync(context, ex); + } + } + + private static async Task HandleExceptionAsync(HttpContext context, Exception exception) + { + if (context.Response.HasStarted) + { + return; + } + + var (statusCode, message) = MapExceptionToResponse(exception); + context.Response.StatusCode = statusCode; + context.Response.ContentType = "application/problem+json"; + + var problem = new ProblemDetails + { + Status = statusCode, + Title = ReasonPhrases.GetReasonPhrase(statusCode), + Detail = message, + Type = $"https://httpstatuses.com/{statusCode}", + Instance = context.Request.Path + }; + problem.Extensions["traceId"] = context.TraceIdentifier; + problem.Extensions["errorType"] = exception.GetType().Name; + + await context.Response.WriteAsync(JsonSerializer.Serialize(problem, JsonOptions)); + } + + private static (int StatusCode, string Message) MapExceptionToResponse(Exception exception) => + exception switch + { + ArgumentNullException => ( + (int)HttpStatusCode.BadRequest, + "Required parameter is missing." + ), + ArgumentOutOfRangeException => ( + (int)HttpStatusCode.BadRequest, + "Parameter value is out of acceptable range." + ), + ArgumentException => ((int)HttpStatusCode.BadRequest, "Invalid argument provided."), + KeyNotFoundException => ( + (int)HttpStatusCode.NotFound, + "The requested resource was not found." + ), + UnauthorizedAccessException => ((int)HttpStatusCode.Unauthorized, "Access is denied."), + InvalidOperationException => ( + (int)HttpStatusCode.BadRequest, + "The operation is not valid in the current state." + ), + NotSupportedException => ( + (int)HttpStatusCode.BadRequest, + "The requested operation is not supported." + ), + TimeoutException => ((int)HttpStatusCode.RequestTimeout, "The operation timed out."), + OperationCanceledException => ( + (int)HttpStatusCode.RequestTimeout, + "The operation was cancelled." + ), + FormatException => ((int)HttpStatusCode.BadRequest, "Invalid data format."), + OverflowException => ( + (int)HttpStatusCode.BadRequest, + "Numeric value is too large or too small." + ), + _ => ( + (int)HttpStatusCode.InternalServerError, + "An unexpected error occurred. Please try again later." + ), + }; +} + +public static class GlobalExceptionHandlerMiddlewareExtensions +{ + public static IApplicationBuilder UseGlobalExceptionHandler(this IApplicationBuilder builder) + { + return builder.UseMiddleware(); + } +} diff --git a/Options/ScalarUiOptions.cs b/Options/ScalarUiOptions.cs new file mode 100644 index 00000000..3c7179bc --- /dev/null +++ b/Options/ScalarUiOptions.cs @@ -0,0 +1,14 @@ +namespace ECommerceApp.RyanW84.Options; + +public class ScalarUiOptions +{ + public string? Title { get; set; } + public string? Theme { get; set; } = "blueplanet"; // blueplanet | purple | default + public string? Layout { get; set; } = "modern"; // modern | classic + public bool DarkMode { get; set; } = true; + public bool HideModels { get; set; } + public bool ShowSidebar { get; set; } = true; + public bool DefaultOpenAllTags { get; set; } = true; + public string? SearchHotKey { get; set; } = "k"; + public string? CustomCss { get; set; } +} diff --git a/Program.cs b/Program.cs new file mode 100644 index 00000000..3a8dcf71 --- /dev/null +++ b/Program.cs @@ -0,0 +1,314 @@ +using ECommerceApp.RyanW84.Interfaces; +using ECommerceApp.RyanW84.Middleware; +using ECommerceApp.RyanW84.Services; +using ECommerceApp.RyanW84.Validators; +using ECommerceApp.RyanW84.Interfaces.Helpers; +using ECommerceApp.RyanW84.Services.Helpers; +using FluentValidation; +using FluentValidation.AspNetCore; +using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Scalar.AspNetCore; +using Serilog; +using ECommerceApp.RyanW84.Options; + +namespace ECommerceApp.RyanW84; + +public class Program +{ + // Entry point of the application + public static void Main(string[] args) + { + // Configure Serilog before building the app + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Information() + .WriteTo.Console() + .WriteTo.File( + Path.Combine(AppContext.BaseDirectory, "logs", "ecommerce-.log"), + rollingInterval: RollingInterval.Day, + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}" + ) + .Enrich.WithProperty("Application", "ECommerceAPI") + .CreateLogger(); + + try + { + Log.Information("Starting ECommerceAPI application"); + var builder = WebApplication.CreateBuilder(args); + builder.Host.UseSerilog(); + + ConfigureServices(builder); + var app = builder.Build(); + ConfigureMiddlewareAndRoutes(app); + app.Run(); + } + catch (Exception ex) + { + Log.Fatal(ex, "Application terminated unexpectedly"); + } + finally + { + Log.CloseAndFlush(); + } + } + + private static void ConfigureServices(WebApplicationBuilder builder) + { + // Load user secrets only in Development to source connection strings safely + if (builder.Environment.IsDevelopment()) + { + builder.Configuration.AddUserSecrets(); + } + + builder + .Services.AddControllers() + .AddJsonOptions(options => + { + options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; + options.JsonSerializerOptions.WriteIndented = false; // Reduce payload size in production + options.JsonSerializerOptions.DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull; + options.JsonSerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase; + // Performance: Handle circular references efficiently + options.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles; + }); + + builder + .Services.AddFluentValidationAutoValidation() + .AddFluentValidationClientsideAdapters(); + + builder.Services.AddValidatorsFromAssemblyContaining(); + + // Performance: Response caching and output caching + builder.Services.AddResponseCaching(); + builder.Services.AddOutputCache(options => + { + options.AddBasePolicy(builder => builder.Cache()); + options.AddPolicy("Products", builder => builder.Expire(TimeSpan.FromMinutes(2)).Tag("products")); + options.AddPolicy("Categories", builder => builder.Expire(TimeSpan.FromMinutes(5)).Tag("categories")); + options.AddPolicy("Sales", builder => builder.Expire(TimeSpan.FromMinutes(1)).Tag("sales")); + }); + + // Performance: Memory cache for frequently accessed data + builder.Services.AddMemoryCache(options => + { + options.SizeLimit = 1024; // Limit cache size to prevent memory bloat + options.CompactionPercentage = 0.25; // Remove 25% when size limit reached + }); + + // Performance: HTTP Response compression + builder.Services.AddResponseCompression(options => + { + options.EnableForHttps = true; + options.Providers.Add(); + options.Providers.Add(); + }); + + builder.Services.Configure(options => + { + options.Level = System.IO.Compression.CompressionLevel.Fastest; + }); + + builder.Services.Configure(options => + { + options.Level = System.IO.Compression.CompressionLevel.Fastest; + }); + + builder.Services.Configure(options => + { + options.ForwardedHeaders = + ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; + options.KnownIPNetworks.Clear(); + options.KnownProxies.Clear(); + }); + + builder.Services.AddOpenApi(); + + string connectionString = GetConnectionString(builder.Configuration); + builder.Services.AddDbContextPool(options => + { + ConfigureDbContext(options, connectionString); + options.EnableSensitiveDataLogging(builder.Environment.IsDevelopment()); + options.EnableDetailedErrors(builder.Environment.IsDevelopment()); + options.ConfigureWarnings(w => w.Throw(Microsoft.EntityFrameworkCore.Diagnostics.RelationalEventId.MultipleCollectionIncludeWarning)); + + // Performance: Enable query caching and compiled model + options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking); // Default to no-tracking for read operations + }, poolSize: 128); + + // Performance: Configure Kestrel server limits + builder.WebHost.ConfigureKestrel(options => + { + options.Limits.MaxConcurrentConnections = 1000; + options.Limits.MaxConcurrentUpgradedConnections = 1000; + options.Limits.MaxRequestBodySize = 10 * 1024 * 1024; // 10 MB + options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2); + options.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(30); + }); + + // Options + builder.Services.AddOptions(); + builder.Services.Configure( + builder.Configuration.GetSection("ScalarUi") + ); + + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + + // Hosted services + builder.Services.AddHostedService(); + } + + private static void ConfigureMiddlewareAndRoutes(WebApplication app) + { + app.UseGlobalExceptionHandler(); + + if (app.Environment.IsDevelopment()) + { + ConfigureDevelopmentPipeline(app); + } + else + { + app.UseExceptionHandler("/Error"); + app.UseHsts(); + } + + app.UseForwardedHeaders(); + app.UseHttpsRedirection(); + + // Performance: Response compression before routing + app.UseResponseCompression(); + + app.UseRouting(); + app.UseResponseCaching(); + app.UseOutputCache(); // Output cache after response cache + app.UseAuthorization(); + app.MapControllers(); + app.MapGet("/", () => Results.Redirect("/scalar/v1")); + } + + private static void ConfigureDevelopmentPipeline(WebApplication app) + { + app.MapOpenApi(); + var scalarOpts = app.Services.GetService>()?.Value + ?? new ScalarUiOptions(); + app.MapScalarApiReference(options => + { + options.Title = string.IsNullOrWhiteSpace(scalarOpts.Title) + ? "ECommerceApp.RyanW84 API Documentation" + : scalarOpts.Title; + + options.Theme = scalarOpts.Theme?.ToLowerInvariant() switch + { + "blueplanet" => ScalarTheme.BluePlanet, + "purple" => ScalarTheme.Purple, + "default" => ScalarTheme.Default, + _ => ScalarTheme.BluePlanet + }; + + options.Layout = scalarOpts.Layout?.ToLowerInvariant() switch + { + "modern" => ScalarLayout.Modern, + "classic" => ScalarLayout.Classic, + _ => ScalarLayout.Modern + }; + + options.DarkMode = scalarOpts.DarkMode; + options.WithDefaultHttpClient(ScalarTarget.CSharp, ScalarClient.HttpClient); + options.HideModels = scalarOpts.HideModels; + options.ShowSidebar = scalarOpts.ShowSidebar; + options.DefaultOpenAllTags = scalarOpts.DefaultOpenAllTags; + if (!string.IsNullOrWhiteSpace(scalarOpts.SearchHotKey)) + { + options.SearchHotKey = scalarOpts.SearchHotKey!; + } + if (!string.IsNullOrWhiteSpace(scalarOpts.CustomCss)) + { + options.CustomCss = scalarOpts.CustomCss; + } + }); + + TryOpenBrowser(app); + } + + private static void TryOpenBrowser(WebApplication app) + { + var urls = app.Urls; + if (urls.Count == 0) + return; + + var url = urls.First().Replace("http://", "https://") + "/scalar/v1"; + try + { + System.Diagnostics.Process.Start( + new System.Diagnostics.ProcessStartInfo { FileName = url, UseShellExecute = true } + ); + if (app.Logger.IsEnabled(LogLevel.Information)) + app.Logger.LogInformation("Browser opened to: {Url}", url); + } + catch (Exception ex) + { + app.Logger.LogWarning(ex, "Failed to open browser automatically"); + } + } + + private static string GetConnectionString(IConfiguration configuration) + { + // Try common locations for the connection string + // 1) appsettings/UserSecrets ConnectionStrings section + var cs = configuration.GetConnectionString("DatabaseConnection") + ?? configuration.GetConnectionString("DefaultConnection") + // 2) flat environment variables (common in containers / CI) + ?? configuration["DATABASE_CONNECTION_STRING"] + ?? configuration["DB_CONNECTION_STRING"] + // 3) Azure-style env vars + ?? Environment.GetEnvironmentVariable("SQLCONNSTR_DatabaseConnection") + ?? Environment.GetEnvironmentVariable("SQLCONNSTR_DefaultConnection"); + + if (!string.IsNullOrWhiteSpace(cs)) + return cs; + + // As a development fallback, allow SQLite so the app can boot without secrets + // This does NOT ship any credentials and only creates a local file DB. + var devSqlitePath = Path.Combine(AppContext.BaseDirectory, "Data", "ecommerce.dev.db"); + Directory.CreateDirectory(Path.GetDirectoryName(devSqlitePath)!); + var sqliteCs = $"Data Source={devSqlitePath}"; + return sqliteCs; + } + + private static void ConfigureDbContext(DbContextOptionsBuilder options, string connectionString) + { + // Choose a provider by inspecting the connection string + // If it looks like SQLite (Data Source=...), use SQLite; otherwise use SQL Server + if (connectionString.Contains("Data Source=", StringComparison.OrdinalIgnoreCase) + && !connectionString.Contains("Server=", StringComparison.OrdinalIgnoreCase) + && !connectionString.Contains("Data Source=(localdb)", StringComparison.OrdinalIgnoreCase)) + { + options.UseSqlite(connectionString, sqliteOptions => + { + sqliteOptions.CommandTimeout(30); + }); + } + else + { + options.UseSqlServer(connectionString, sqlServerOptions => + { + sqlServerOptions.CommandTimeout(30); + sqlServerOptions.EnableRetryOnFailure( + maxRetryCount: 3, + maxRetryDelay: TimeSpan.FromSeconds(5), + errorNumbersToAdd: null); + }); + } + } +} diff --git a/Properties/launchSettings.json b/Properties/launchSettings.json new file mode 100644 index 00000000..d0846ecf --- /dev/null +++ b/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "ECommerceApp.RyanW84": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "scalar/v1", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:51679;http://localhost:51680" + } + } +} \ No newline at end of file diff --git a/Properties/serviceDependencies.json b/Properties/serviceDependencies.json new file mode 100644 index 00000000..e3c99f4b --- /dev/null +++ b/Properties/serviceDependencies.json @@ -0,0 +1,12 @@ +{ + "dependencies": { + "secrets1": { + "type": "secrets" + }, + "mssql1": { + "type": "mssql", + "connectionId": "ConnectionStrings:DatabaseConnection", + "dynamicId": null + } + } +} \ No newline at end of file diff --git a/Properties/serviceDependencies.local.json b/Properties/serviceDependencies.local.json new file mode 100644 index 00000000..1ae8f05e --- /dev/null +++ b/Properties/serviceDependencies.local.json @@ -0,0 +1,13 @@ +{ + "dependencies": { + "secrets1": { + "type": "secrets.user" + }, + "mssql1": { + "secretStore": "LocalSecretsFile", + "type": "mssql.onprem", + "connectionId": "ConnectionStrings:DatabaseConnection", + "dynamicId": null + } + } +} \ No newline at end of file diff --git a/Repositories/CategoryRepository.cs b/Repositories/CategoryRepository.cs new file mode 100644 index 00000000..1650c7b2 --- /dev/null +++ b/Repositories/CategoryRepository.cs @@ -0,0 +1,502 @@ +using System.Net; +using ECommerceApp.RyanW84.Data; +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Interfaces; +using Microsoft.EntityFrameworkCore; + +namespace ECommerceApp.RyanW84.Repositories; + +public class CategoryRepository(ECommerceDbContext db) : ICategoryRepository +{ + private readonly ECommerceDbContext _db = db; + + public async Task CategoryExistsAsync( + string categoryName, + CancellationToken cancellationToken = default + ) + { + if (string.IsNullOrWhiteSpace(categoryName)) + return false; + return await CompiledQueries.CategoryExistsByName(_db, categoryName, cancellationToken); + } + + public async Task> GetByIdAsync( + int id, + CancellationToken cancellationToken = default + ) + { + try + { + Category? category = await CompiledQueries.GetCategoryByIdWithRelations( + _db, + id, + cancellationToken + ); + + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = category, + }; + } + catch (Exception ex) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = HttpStatusCode.InternalServerError, + ErrorMessage = ex.Message, + Data = null, + }; + } + } + + public async Task> GetByNameAsync( + string name, + CancellationToken cancellationToken = default + ) + { + try + { + Category? category = await _db + .Categories.AsNoTracking() + .AsSplitQuery() + .Include(c => c.Products) + .Include(c => c.Sales) + .FirstOrDefaultAsync(c => c.Name == name, cancellationToken); + + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = category, + }; + } + catch (Exception ex) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = HttpStatusCode.InternalServerError, + ErrorMessage = ex.Message, + Data = null, + }; + } + } + + public async Task>> GetAllCategoriesAsync( + CategoryQueryParameters parameters, + CancellationToken cancellationToken = default + ) + { + var (page, pageSize) = NormalizePageParameters(parameters); + + try + { + var query = BuildCategoryQuery(parameters); + var (totalCount, categories) = await FetchCategoriesPageAsync( + query, + page, + pageSize, + cancellationToken + ); + return BuildSuccessResponse(categories, page, pageSize, totalCount); + } + catch (Exception ex) + { + return BuildFailureResponse(ex.Message, page, pageSize); + } + } + + private static (int Page, int PageSize) NormalizePageParameters( + CategoryQueryParameters parameters + ) + { + var page = Math.Max(parameters.Page, 1); + var pageSize = Math.Clamp(parameters.PageSize, 1, 32); + return (page, pageSize); + } + + private static async Task<(int TotalCount, List Categories)> FetchCategoriesPageAsync( + IQueryable query, + int page, + int pageSize, + CancellationToken cancellationToken + ) + { + var totalCount = await query.CountAsync(cancellationToken); + var categories = await query + .Skip((page - 1) * pageSize) + .Take(pageSize) + .ToListAsync(cancellationToken); + return (totalCount, categories); + } + + private static PaginatedResponseDto> BuildSuccessResponse( + List categories, + int page, + int pageSize, + int totalCount + ) => + new() + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = categories, + CurrentPage = page, + PageSize = pageSize, + TotalCount = totalCount, + }; + + private static PaginatedResponseDto> BuildFailureResponse( + string errorMessage, + int page, + int pageSize + ) => + new() + { + RequestFailed = true, + ResponseCode = HttpStatusCode.InternalServerError, + ErrorMessage = errorMessage, + Data = [], + CurrentPage = page, + PageSize = pageSize, + TotalCount = 0, + }; + + private IQueryable BuildCategoryQuery(CategoryQueryParameters parameters) + { + var query = GetBaseQuery(); + if (parameters.IncludeDeleted) + query = query.IgnoreQueryFilters(); + query = ApplySearchFilter(query, parameters.Search); + var descending = string.Equals( + parameters.SortDirection, + "desc", + StringComparison.OrdinalIgnoreCase + ); + query = ApplyCategorySorting( + query, + parameters.SortBy?.Trim().ToLowerInvariant(), + descending + ); + return query; + } + + private static IQueryable ApplyCategorySorting( + IQueryable query, + string? sortBy, + bool descending + ) + { + return sortBy switch + { + "name" => descending + ? query.OrderByDescending(c => c.Name) + : query.OrderBy(c => c.Name), + "createdat" => descending + ? query.OrderByDescending(c => c.CreatedAt) + : query.OrderBy(c => c.CreatedAt), + _ => descending + ? query.OrderByDescending(c => c.CategoryId) + : query.OrderBy(c => c.CategoryId), + }; + } + + private static ApiResponseDto CreateErrorResponse(HttpStatusCode code, string message) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = code, + ErrorMessage = message, + Data = default, + }; + } + + private IQueryable GetBaseQuery() + { + return _db.Categories + .TagWith("CategoryRepository.GetBaseQuery") + .AsNoTracking() + .Include(c => c.Products); + } + + private IQueryable ApplySearchFilter(IQueryable query, string? search) + { + if (string.IsNullOrEmpty(search?.Trim())) + return query; + var pattern = $"%{search.Trim()}%"; + return query.Where(c => + EF.Functions.Like(c.Name, pattern) || EF.Functions.Like(c.Description, pattern) + ); + } + + public async Task> AddAsync( + Category entity, + CancellationToken cancellationToken = default + ) + { + try + { + await _db.Categories.AddAsync(entity, cancellationToken); + await _db.SaveChangesAsync(cancellationToken); + + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.Created, + ErrorMessage = string.Empty, + Data = entity, + }; + } + catch (DbUpdateConcurrencyException) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = HttpStatusCode.Conflict, + ErrorMessage = "Concurrency conflict occurred while adding the category.", + Data = entity, + }; + } + catch (DbUpdateException) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = HttpStatusCode.BadRequest, + ErrorMessage = "Failed to add category. Please check the data and try again.", + Data = entity, + }; + } + catch (Exception) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = HttpStatusCode.InternalServerError, + ErrorMessage = "An unexpected error occurred while adding the category.", + Data = entity, + }; + } + } + + public async Task> UpdateAsync( + Category entity, + CancellationToken cancellationToken = default + ) + { + try + { + _db.Categories.Update(entity); + await _db.SaveChangesAsync(cancellationToken); + + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = entity, + }; + } + catch (DbUpdateConcurrencyException) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = HttpStatusCode.Conflict, + ErrorMessage = "Concurrency conflict occurred while updating the category.", + Data = entity, + }; + } + catch (DbUpdateException) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = HttpStatusCode.BadRequest, + ErrorMessage = "Failed to update category. Please check the data and try again.", + Data = entity, + }; + } + catch (Exception) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = HttpStatusCode.InternalServerError, + ErrorMessage = "An unexpected error occurred while updating the category.", + Data = entity, + }; + } + } + + public async Task> DeleteAsync( + int id, + CancellationToken cancellationToken = default + ) + { + try + { + Category? category = await _db.Categories.FindAsync( + new object[] { id }, + cancellationToken + ); + if (category == null) + return CreateErrorResponse(HttpStatusCode.NotFound, "Category not found"); + + category.IsDeleted = true; + category.DeletedAt = DateTime.UtcNow; + await _db.SaveChangesAsync(cancellationToken); + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.NoContent, + ErrorMessage = string.Empty, + Data = true, + }; + } + catch (DbUpdateConcurrencyException) + { + return CreateErrorResponse( + HttpStatusCode.Conflict, + "Concurrency conflict occurred while deleting the category." + ); + } + catch (DbUpdateException) + { + return CreateErrorResponse( + HttpStatusCode.BadRequest, + "Failed to delete category. Please try again." + ); + } + catch (Exception) + { + return CreateErrorResponse( + HttpStatusCode.InternalServerError, + "An unexpected error occurred while deleting the category." + ); + } + } + + public async Task> RestoreAsync( + int id, + CancellationToken cancellationToken = default + ) + { + try + { + Category? category = await _db + .Categories.IgnoreQueryFilters() + .FirstOrDefaultAsync(c => c.CategoryId == id, cancellationToken); + if (category == null) + return CreateErrorResponse(HttpStatusCode.NotFound, "Category not found"); + if (!category.IsDeleted) + return CreateErrorResponse( + HttpStatusCode.BadRequest, + "Category is not deleted" + ); + category.IsDeleted = false; + category.DeletedAt = null; + await _db.SaveChangesAsync(cancellationToken); + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = true, + }; + } + catch (Exception ex) + { + return CreateErrorResponse( + HttpStatusCode.InternalServerError, + $"Failed to restore category: {ex.Message}" + ); + } + } + + public async Task>> GetDeletedCategoriesAsync( + CancellationToken cancellationToken = default + ) + { + try + { + List deletedCategories = await _db + .Categories.IgnoreQueryFilters() // Include soft-deleted items + .Where(c => c.IsDeleted) + .Include(c => c.Products) + .Include(c => c.Sales) + .AsNoTracking() + .ToListAsync(cancellationToken); + + return new ApiResponseDto> + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = deletedCategories, + }; + } + catch (Exception ex) + { + return new ApiResponseDto> + { + RequestFailed = true, + ResponseCode = HttpStatusCode.InternalServerError, + ErrorMessage = ex.Message, + Data = null, + }; + } + } + + public async Task> HardDeleteAsync( + int id, + CancellationToken cancellationToken = default + ) + { + try + { + Category? category = await _db + .Categories.IgnoreQueryFilters() // Include soft-deleted items + .FirstOrDefaultAsync(c => c.CategoryId == id, cancellationToken); + + if (category == null) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = HttpStatusCode.NotFound, + ErrorMessage = "Category not found", + Data = false, + }; + } + + _db.Categories.Remove(category); + await _db.SaveChangesAsync(cancellationToken); + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.NoContent, + ErrorMessage = string.Empty, + Data = true, + }; + } + catch (Exception ex) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = HttpStatusCode.InternalServerError, + ErrorMessage = ex.Message, + Data = false, + }; + } + } +} diff --git a/Repositories/CompiledQueries.cs b/Repositories/CompiledQueries.cs new file mode 100644 index 00000000..2b0948b7 --- /dev/null +++ b/Repositories/CompiledQueries.cs @@ -0,0 +1,68 @@ +using ECommerceApp.RyanW84.Data; +using ECommerceApp.RyanW84.Data.Models; +using Microsoft.EntityFrameworkCore; + +namespace ECommerceApp.RyanW84.Repositories; + +/// +/// Compiled queries for high-performance database operations. +/// Compiled queries are cached by EF Core and eliminate query compilation overhead on repeated executions. +/// +public static class CompiledQueries +{ + /// + /// Compiled query to get a Product by ID with its Category included. + /// + public static readonly Func> GetProductByIdWithCategory = + EF.CompileAsyncQuery((ECommerceDbContext db, int productId, CancellationToken ct) => + db.Products + .AsNoTracking() + .Include(p => p.Category) + .FirstOrDefault(p => p.ProductId == productId) + ); + + /// + /// Compiled query to get a Category by ID with Products and Sales included using split query. + /// + public static readonly Func> GetCategoryByIdWithRelations = + EF.CompileAsyncQuery((ECommerceDbContext db, int categoryId, CancellationToken ct) => + db.Categories + .AsNoTracking() + .AsSplitQuery() + .Include(c => c.Products) + .Include(c => c.Sales) + .FirstOrDefault(c => c.CategoryId == categoryId) + ); + + /// + /// Compiled query to get a Sale by ID with SaleItems, Products, and Categories using split query. + /// + public static readonly Func> GetSaleByIdWithRelations = + EF.CompileAsyncQuery((ECommerceDbContext db, int saleId, CancellationToken ct) => + db.Sales + .AsNoTracking() + .AsSplitQuery() + .Include(s => s.SaleItems) + .ThenInclude(si => si.Product) + .Include(s => s.Categories) + .FirstOrDefault(s => s.SaleId == saleId) + ); + + /// + /// Compiled query to check if a category exists by name. + /// + public static readonly Func> CategoryExistsByName = + EF.CompileAsyncQuery((ECommerceDbContext db, string categoryName, CancellationToken ct) => + db.Categories.Any(c => c.Name == categoryName) + ); + + /// + /// Compiled query to get a Product by ID (simple, no includes). + /// + public static readonly Func> GetProductById = + EF.CompileAsyncQuery((ECommerceDbContext db, int productId, CancellationToken ct) => + db.Products + .AsNoTracking() + .FirstOrDefault(p => p.ProductId == productId) + ); +} diff --git a/Repositories/ProductRepository.cs b/Repositories/ProductRepository.cs new file mode 100644 index 00000000..ad51cc62 --- /dev/null +++ b/Repositories/ProductRepository.cs @@ -0,0 +1,513 @@ +using System.Net; +using ECommerceApp.RyanW84.Data; +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Interfaces; +using Microsoft.EntityFrameworkCore; + +namespace ECommerceApp.RyanW84.Repositories +{ + public class ProductRepository(ECommerceDbContext db) : IProductRepository + { + private readonly ECommerceDbContext _db = db; + + public async Task> AddAsync( + Product entity, + CancellationToken cancellationToken = default + ) + { + try + { + await _db.Products.AddAsync(entity, cancellationToken); + await _db.SaveChangesAsync(cancellationToken); + + Product? createdProduct = await GetProductWithCategory( + entity.ProductId, + cancellationToken + ); + + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.Created, + ErrorMessage = string.Empty, + Data = createdProduct, + }; + } + catch (DbUpdateException ex) + { + return HandleDbUpdateException(ex); + } + catch (Exception ex) + { + return CreateErrorResponse( + HttpStatusCode.InternalServerError, + $"Failed to create product: {ex.Message}" + ); + } + } + + public async Task> GetByIdAsync( + int id, + CancellationToken cancellationToken = default + ) + { + try + { + Product? product = await CompiledQueries.GetProductByIdWithCategory( + _db, + id, + cancellationToken + ); + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = product, + }; + } + catch (Exception ex) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = HttpStatusCode.InternalServerError, + ErrorMessage = ex.Message, + Data = null, + }; + } + } + + public async Task>> GetAllProductsAsync( + ProductQueryParameters? parameters, + CancellationToken cancellationToken = default + ) + { + parameters ??= new ProductQueryParameters(); + + var page = Math.Max(parameters.Page, 1); + var pageSize = Math.Clamp(parameters.PageSize, 1, 100); + + try + { + IQueryable query = GetBaseProductQuery(); + query = ApplyProductFilters(query, parameters); + + var descending = string.Equals( + parameters.SortDirection, + "desc", + StringComparison.OrdinalIgnoreCase + ); + var sortBy = parameters.SortBy?.Trim().ToLowerInvariant(); + IQueryable orderedQuery = ApplyProductSorting(query, sortBy, descending); + + var totalCount = await orderedQuery.CountAsync(cancellationToken); + List products = await orderedQuery + .Skip((page - 1) * pageSize) + .Take(pageSize) + .ToListAsync(cancellationToken); + + return new PaginatedResponseDto> + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = products, + CurrentPage = page, + PageSize = pageSize, + TotalCount = totalCount, + }; + } + catch (Exception ex) + { + return new PaginatedResponseDto> + { + RequestFailed = true, + ResponseCode = HttpStatusCode.InternalServerError, + ErrorMessage = ex.Message, + Data = null, + CurrentPage = page, + PageSize = pageSize, + TotalCount = 0, + }; + } + } + + private static IQueryable ApplyProductSorting( + IQueryable query, + string? sortBy, + bool descending + ) + { + return sortBy switch + { + "name" => descending + ? query.OrderByDescending(p => p.Name) + : query.OrderBy(p => p.Name), + "price" => descending + ? query.OrderByDescending(p => p.Price) + : query.OrderBy(p => p.Price), + "createdat" => descending + ? query.OrderByDescending(p => p.CreatedAt) + : query.OrderBy(p => p.CreatedAt), + "stock" => descending + ? query.OrderByDescending(p => p.Stock) + : query.OrderBy(p => p.Stock), + "category" => descending + ? query.OrderByDescending(p => p.Category!.Name) + : query.OrderBy(p => p.Category!.Name), + _ => descending + ? query.OrderByDescending(p => p.ProductId) + : query.OrderBy(p => p.ProductId), + }; + } + + private static ApiResponseDto CreateErrorResponse( + HttpStatusCode statusCode, + string errorMessage + ) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = statusCode, + ErrorMessage = errorMessage, + Data = default, + }; + } + + private static ApiResponseDto HandleDbUpdateException(DbUpdateException ex) + { + if ( + ex.InnerException?.Message.Contains("FOREIGN KEY") == true + || ex.InnerException?.Message.Contains("constraint") == true + ) + { + return CreateErrorResponse( + HttpStatusCode.BadRequest, + "Invalid data: Foreign key constraint violation or invalid reference." + ); + } + + return CreateErrorResponse( + HttpStatusCode.Conflict, + "Failed to save product due to data conflict." + ); + } + + private IQueryable GetBaseProductQuery() + { + return _db + .Products.TagWith("ProductRepository.GetBaseProductQuery") + .Include(p => p.Category) + .Where(p => !p.IsDeleted) + .AsNoTracking() + .AsQueryable(); + } + + private static IQueryable ApplyProductFilters( + IQueryable query, + ProductQueryParameters parameters + ) + { + var search = parameters.Search?.Trim(); + if (!string.IsNullOrEmpty(search)) + { + var likePattern = $"%{search}%"; + query = query.Where(p => + EF.Functions.Like(p.Name, likePattern) + || EF.Functions.Like(p.Description, likePattern) + ); + } + + if (parameters.MinPrice is { } minPrice) + { + query = query.Where(p => p.Price >= minPrice); + } + + if (parameters.MaxPrice is { } maxPrice) + { + query = query.Where(p => p.Price <= maxPrice); + } + + if (parameters.CategoryId is { } categoryId) + { + query = query.Where(p => p.CategoryId == categoryId); + } + + return query; + } + + private static void UpdateProductProperties(Product existing, Product entity) + { + existing.Name = entity.Name; + existing.Description = entity.Description; + existing.Price = entity.Price; + existing.Stock = entity.Stock; + existing.IsActive = entity.IsActive; + existing.CategoryId = entity.CategoryId; + } + + private Task GetProductWithCategory( + int productId, + CancellationToken cancellationToken + ) + { + return CompiledQueries.GetProductByIdWithCategory(_db, productId, cancellationToken); + } + + private static ApiResponseDto CheckConstraintViolation(DbUpdateException ex) + { + return + ex.InnerException?.Message.Contains("FOREIGN KEY") == true + || ex.InnerException?.Message.Contains("constraint") == true + ? CreateErrorResponse( + HttpStatusCode.Conflict, + "Cannot delete product as it is referenced by existing sales." + ) + : CreateErrorResponse( + HttpStatusCode.InternalServerError, + "Failed to delete product due to database error." + ); + } + + public async Task>> GetProductsByCategoryIdAsync( + int categoryId, + CancellationToken cancellationToken = default + ) + { + try + { + List products = await _db + .Products.AsNoTracking() + .Where(p => p.CategoryId == categoryId) + .Include(p => p.Category) + .ToListAsync(cancellationToken); + return new ApiResponseDto> + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = products, + }; + } + catch (Exception ex) + { + return new ApiResponseDto> + { + RequestFailed = true, + ResponseCode = HttpStatusCode.InternalServerError, + ErrorMessage = ex.Message, + Data = null, + }; + } + } + + public async Task> UpdateAsync( + Product entity, + CancellationToken cancellationToken = default + ) + { + try + { + Product? existing = await _db + .Products.Include(p => p.Category) + .FirstOrDefaultAsync(p => p.ProductId == entity.ProductId, cancellationToken); + + if (existing == null) + return CreateErrorResponse( + HttpStatusCode.NotFound, + "Product not found" + ); + + UpdateProductProperties(existing, entity); + await _db.SaveChangesAsync(cancellationToken); + + Product? updatedProduct = await GetProductWithCategory( + existing.ProductId, + cancellationToken + ); + + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = updatedProduct, + }; + } + catch (DbUpdateConcurrencyException) + { + return CreateErrorResponse( + HttpStatusCode.Conflict, + "Product was modified by another user. Please refresh and try again." + ); + } + catch (DbUpdateException ex) + { + return HandleDbUpdateException(ex); + } + catch (Exception ex) + { + return CreateErrorResponse( + HttpStatusCode.InternalServerError, + $"Failed to update product: {ex.Message}" + ); + } + } + + public async Task> DeleteAsync( + int id, + CancellationToken cancellationToken = default + ) + { + try + { + Product? product = await _db.Products.FindAsync( + new object[] { id }, + cancellationToken + ); + if (product == null) + return CreateErrorResponse(HttpStatusCode.NotFound, "Product not found"); + + product.IsDeleted = true; + product.DeletedAt = DateTime.UtcNow; + await _db.SaveChangesAsync(cancellationToken); + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.NoContent, + ErrorMessage = string.Empty, + Data = true, + }; + } + catch (DbUpdateException ex) + { + return CheckConstraintViolation(ex); + } + catch (Exception ex) + { + return CreateErrorResponse( + HttpStatusCode.InternalServerError, + $"Failed to delete product: {ex.Message}" + ); + } + } + + public async Task> RestoreAsync( + int id, + CancellationToken cancellationToken = default + ) + { + try + { + Product? product = await _db + .Products.IgnoreQueryFilters() + .FirstOrDefaultAsync(p => p.ProductId == id, cancellationToken); + if (product == null) + return CreateErrorResponse(HttpStatusCode.NotFound, "Product not found"); + if (!product.IsDeleted) + return CreateErrorResponse( + HttpStatusCode.BadRequest, + "Product is not deleted" + ); + + product.IsDeleted = false; + product.DeletedAt = null; + await _db.SaveChangesAsync(cancellationToken); + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = true, + }; + } + catch (Exception ex) + { + return CreateErrorResponse( + HttpStatusCode.InternalServerError, + $"Failed to restore product: {ex.Message}" + ); + } + } + + public async Task>> GetDeletedProductsAsync( + CancellationToken cancellationToken = default + ) + { + try + { + List deletedProducts = await _db + .Products.IgnoreQueryFilters() // Include soft-deleted items + .Where(p => p.IsDeleted) + .Include(p => p.Category) + .AsNoTracking() + .ToListAsync(cancellationToken); + + return new ApiResponseDto> + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = deletedProducts, + }; + } + catch (Exception ex) + { + return new ApiResponseDto> + { + RequestFailed = true, + ResponseCode = HttpStatusCode.InternalServerError, + ErrorMessage = ex.Message, + Data = null, + }; + } + } + + public async Task> HardDeleteAsync( + int id, + CancellationToken cancellationToken = default + ) + { + try + { + Product? product = await _db + .Products.IgnoreQueryFilters() // Include soft-deleted items + .FirstOrDefaultAsync(p => p.ProductId == id, cancellationToken); + + if (product == null) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = HttpStatusCode.NotFound, + ErrorMessage = "Product not found", + Data = false, + }; + } + + _db.Products.Remove(product); + await _db.SaveChangesAsync(cancellationToken); + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.NoContent, + ErrorMessage = string.Empty, + Data = true, + }; + } + catch (Exception ex) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = HttpStatusCode.InternalServerError, + ErrorMessage = ex.Message, + Data = false, + }; + } + } + } +} diff --git a/Repositories/SaleRepository.cs b/Repositories/SaleRepository.cs new file mode 100644 index 00000000..fa213bcd --- /dev/null +++ b/Repositories/SaleRepository.cs @@ -0,0 +1,423 @@ +using System.Net; +using ECommerceApp.RyanW84.Data; +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Interfaces; +using Microsoft.EntityFrameworkCore; + +namespace ECommerceApp.RyanW84.Repositories; + +public class SaleRepository(ECommerceDbContext db) : ISaleRepository +{ + private readonly ECommerceDbContext _db = db; + + public async Task> GetByIdAsync( + int id, + CancellationToken cancellationToken = default + ) + { + try + { + Sale? sale = await CompiledQueries.GetSaleByIdWithRelations( + _db, + id, + cancellationToken + ); + + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = sale, + }; + } + catch (Exception ex) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = HttpStatusCode.InternalServerError, + ErrorMessage = ex.Message, + Data = null, + }; + } + } + + private static IQueryable ApplySaleSorting( + IQueryable query, + string? sortBy, + bool descending + ) + { + return sortBy switch + { + "customername" => descending + ? query.OrderByDescending(s => s.CustomerName) + : query.OrderBy(s => s.CustomerName), + "totalamount" => descending + ? query.OrderByDescending(s => s.TotalAmount) + : query.OrderBy(s => s.TotalAmount), + "saledate" => descending + ? query.OrderByDescending(s => s.SaleDate) + : query.OrderBy(s => s.SaleDate), + _ => descending + ? query.OrderByDescending(s => s.SaleDate) + : query.OrderBy(s => s.SaleDate), + }; + } + + private static ApiResponseDto CreateErrorResponse( + HttpStatusCode statusCode, + string errorMessage + ) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = statusCode, + ErrorMessage = errorMessage, + Data = default, + }; + } + + public async Task>> GetAllSalesAsync( + SaleQueryParameters parameters, + CancellationToken cancellationToken = default + ) + { + var page = Math.Max(parameters.Page, 1); + var pageSize = Math.Clamp(parameters.PageSize, 1, 100); + + try + { + IQueryable query = GetBaseSalesQuery(); + query = ApplyFilters(query, parameters); + + var descending = string.Equals( + parameters.SortDirection, + "desc", + StringComparison.OrdinalIgnoreCase + ); + var sortBy = parameters.SortBy?.Trim().ToLowerInvariant(); + IQueryable orderedQuery = ApplySaleSorting(query, sortBy, descending); + + var totalCount = await orderedQuery.CountAsync(cancellationToken); + List sales = await orderedQuery + .Skip((page - 1) * pageSize) + .Take(pageSize) + .ToListAsync(cancellationToken); + + return new PaginatedResponseDto> + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = sales, + CurrentPage = page, + PageSize = pageSize, + TotalCount = totalCount, + }; + } + catch (Exception ex) + { + return new PaginatedResponseDto> + { + RequestFailed = true, + ResponseCode = HttpStatusCode.InternalServerError, + ErrorMessage = ex.Message, + Data = [], + CurrentPage = page, + PageSize = pageSize, + TotalCount = 0, + }; + } + } + + private IQueryable GetBaseSalesQuery() + { + return _db + .Sales.TagWith("SaleRepository.GetBaseSalesQuery") + .AsNoTracking() + .AsSplitQuery() + .Include(s => s.SaleItems) + .ThenInclude(si => si.Product) + .Include(s => s.Categories) + .AsQueryable(); + } + + private static IQueryable ApplyFilters( + IQueryable query, + SaleQueryParameters parameters + ) + { + if (parameters.StartDate is { } startDate) + { + query = query.Where(s => s.SaleDate >= startDate); + } + + if (parameters.EndDate is { } endDate) + { + query = query.Where(s => s.SaleDate <= endDate); + } + + var customerName = parameters.CustomerName?.Trim(); + if (!string.IsNullOrEmpty(customerName)) + { + var likePattern = $"%{customerName}%"; + query = query.Where(s => EF.Functions.Like(s.CustomerName, likePattern)); + } + + var customerEmail = parameters.CustomerEmail?.Trim(); + if (!string.IsNullOrEmpty(customerEmail)) + { + query = query.Where(s => s.CustomerEmail == customerEmail); + } + + return query; + } + + public async Task> AddAsync( + Sale entity, + CancellationToken cancellationToken = default + ) + { + try + { + await _db.Sales.AddAsync(entity, cancellationToken); + await _db.SaveChangesAsync(cancellationToken); + + // Reload the sale with related SaleItems and Categories + Sale? createdSale = await _db + .Sales.AsNoTracking() + .Include(s => s.SaleItems) + .ThenInclude(si => si.Product) + .Include(s => s.Categories) + .FirstOrDefaultAsync(s => s.SaleId == entity.SaleId, cancellationToken); + + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.Created, + ErrorMessage = string.Empty, + Data = createdSale, + }; + } + catch (DbUpdateConcurrencyException) + { + return CreateErrorResponse( + HttpStatusCode.Conflict, + "Concurrency conflict occurred while adding the sale." + ); + } + catch (DbUpdateException) + { + return CreateErrorResponse( + HttpStatusCode.BadRequest, + "Failed to add sale. Please check the data and try again." + ); + } + catch (Exception) + { + return CreateErrorResponse( + HttpStatusCode.InternalServerError, + "An unexpected error occurred while adding the sale." + ); + } + } + + public async Task> UpdateAsync( + Sale entity, + CancellationToken cancellationToken = default + ) + { + try + { + _db.Sales.Update(entity); + await _db.SaveChangesAsync(cancellationToken); + + // Reload the sale with related SaleItems and Categories + Sale? updatedSale = await _db + .Sales.AsNoTracking() + .Include(s => s.SaleItems) + .ThenInclude(si => si.Product) + .Include(s => s.Categories) + .FirstOrDefaultAsync(s => s.SaleId == entity.SaleId, cancellationToken); + + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = updatedSale, + }; + } + catch (DbUpdateConcurrencyException) + { + return CreateErrorResponse( + HttpStatusCode.Conflict, + "Concurrency conflict occurred while updating the sale." + ); + } + catch (DbUpdateException) + { + return CreateErrorResponse( + HttpStatusCode.BadRequest, + "Failed to update sale. Please check the data and try again." + ); + } + catch (Exception) + { + return CreateErrorResponse( + HttpStatusCode.InternalServerError, + "An unexpected error occurred while updating the sale." + ); + } + } + + public async Task> DeleteAsync( + int id, + CancellationToken cancellationToken = default + ) + { + try + { + Sale? sale = await _db.Sales.FindAsync(new object[] { id }, cancellationToken); + if (sale == null) + { + return CreateErrorResponse(HttpStatusCode.NotFound, "Sale not found"); + } + _db.Sales.Remove(sale); + await _db.SaveChangesAsync(cancellationToken); + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.NoContent, + ErrorMessage = string.Empty, + Data = true, + }; + } + catch (DbUpdateConcurrencyException) + { + return CreateErrorResponse( + HttpStatusCode.Conflict, + "Concurrency conflict occurred while deleting the sale." + ); + } + catch (DbUpdateException) + { + return CreateErrorResponse( + HttpStatusCode.BadRequest, + "Failed to delete sale. Please try again." + ); + } + catch (Exception) + { + return CreateErrorResponse( + HttpStatusCode.InternalServerError, + "An unexpected error occurred while deleting the sale." + ); + } + } + + public async Task> GetByIdWithHistoricalProductsAsync( + int id, + CancellationToken cancellationToken = default + ) + { + try + { + Sale? sale = await GetSaleWithHistoricalProducts(id, cancellationToken); + + sale?.SaleItems = FilterHistoricalSaleItems(sale.SaleItems, sale.SaleDate); + + return new ApiResponseDto + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = sale, + }; + } + catch (Exception ex) + { + return new ApiResponseDto + { + RequestFailed = true, + ResponseCode = HttpStatusCode.InternalServerError, + ErrorMessage = ex.Message, + Data = null, + }; + } + } + + private Task GetSaleWithHistoricalProducts(int id, CancellationToken cancellationToken) + { + return _db + .Sales.AsNoTracking() + .Include(s => s.SaleItems) + .ThenInclude(si => si.Product) + .Include(s => s.Categories) + .IgnoreQueryFilters() + .FirstOrDefaultAsync(s => s.SaleId == id, cancellationToken); + } + + private static List FilterHistoricalSaleItems( + IEnumerable saleItems, + DateTime saleDate + ) + { + return saleItems + .Where(si => + si.Product != null && (!si.Product.IsDeleted || si.Product.DeletedAt > saleDate) + ) + .ToList(); + } + + public async Task>> GetHistoricalSalesAsync( + CancellationToken cancellationToken = default + ) + { + try + { + List list = await _db + .Sales.AsNoTracking() + .Include(s => s.SaleItems) + .ThenInclude(si => si.Product) + .Include(s => s.Categories) + .IgnoreQueryFilters() // Include deleted products + .ToListAsync(cancellationToken); + + FilterAllHistoricalSaleItems(list); + + return new ApiResponseDto> + { + RequestFailed = false, + ResponseCode = HttpStatusCode.OK, + ErrorMessage = string.Empty, + Data = list, + }; + } + catch (Exception ex) + { + return new ApiResponseDto> + { + RequestFailed = true, + ResponseCode = HttpStatusCode.InternalServerError, + ErrorMessage = ex.Message, + Data = [], + }; + } + } + + private static void FilterAllHistoricalSaleItems(List sales) + { + foreach (Sale sale in sales) + { + if (true) + { + sale.SaleItems = FilterHistoricalSaleItems(sale.SaleItems, sale.SaleDate); + } + } + } +} diff --git a/Services/CategoryService.cs b/Services/CategoryService.cs new file mode 100644 index 00000000..93658dbc --- /dev/null +++ b/Services/CategoryService.cs @@ -0,0 +1,240 @@ +using System.Net; +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Interfaces; +using ECommerceApp.RyanW84.Interfaces.Helpers; + +namespace ECommerceApp.RyanW84.Services; + +/// +/// Service layer for managing category operations. +/// Handles category creation, retrieval, updates, deletion, and restoration. +/// Ensures data consistency and validates business rules. +/// +public class CategoryService(ICategoryRepository categoryRepository, ICategoryProcessingHelper categoryProcessingHelper) : ICategoryService +{ + private readonly ICategoryRepository _categoryRepository = categoryRepository; + private readonly ICategoryProcessingHelper _categoryProcessingHelper = categoryProcessingHelper; + + /// + /// Creates a new category. + /// + /// The category creation request + /// Cancellation token + /// Created category or error response with conflict status if duplicate name + public async Task> CreateCategoryAsync( + ApiRequestDto request, + CancellationToken cancellationToken = default + ) + { + var validation = _categoryProcessingHelper.ValidateCreateRequest(request); + if (validation != null) return validation; + + var name = request.Payload!.Name.Trim(); + if (await _categoryRepository.CategoryExistsAsync(name, cancellationToken)) + return ApiResponseDto.Failure( + HttpStatusCode.Conflict, + $"Category with name '{name}' already exists." + ); + + var category = _categoryProcessingHelper.PrepareForCreate(request.Payload!); + + ApiResponseDto addResult = await _categoryRepository.AddAsync( + category, + cancellationToken + ); + return addResult.RequestFailed + ? addResult + : ApiResponseDto.Success(addResult.Data, HttpStatusCode.Created); + } + + public async Task> GetCategoryAsync( + int id, + CancellationToken cancellationToken = default + ) + { + ApiResponseDto repoResult = await _categoryRepository.GetByIdAsync( + id, + cancellationToken + ); + if (repoResult.RequestFailed || repoResult.Data is null) + return ApiResponseDto.Failure( + HttpStatusCode.NotFound, + $"Category with id {id} not found." + ); + return ApiResponseDto.Success(repoResult.Data); + } + + public async Task>> GetAllCategoriesAsync( + CategoryQueryParameters parameters, + CancellationToken cancellationToken = default + ) + { + PaginatedResponseDto> repoResult = + await _categoryRepository.GetAllCategoriesAsync(parameters, cancellationToken); + + return repoResult.RequestFailed + ? PaginatedResponseDto>.Failure( + repoResult.ResponseCode, + repoResult.ErrorMessage, + parameters.Page, + parameters.PageSize + ) + : PaginatedResponseDto>.Success( + repoResult.Data ?? [], + repoResult.CurrentPage, + repoResult.PageSize, + repoResult.TotalCount + ); + } + + public async Task> UpdateCategoryAsync( + int id, + ApiRequestDto request, + CancellationToken cancellationToken = default + ) + { + var validation = ValidateUpdateRequest(request); + if (validation != null) return validation; + + var existing = await GetExistingCategoryAsync(id, cancellationToken); + if (existing == null) + return ApiResponseDto.Failure( + HttpStatusCode.NotFound, + $"Category with id {id} not found." + ); + + var nameValidation = await ValidateCategoryNameAsync(request.Payload!, existing, cancellationToken); + if (nameValidation != null) return nameValidation; + + var updatedCategory = _categoryProcessingHelper.PrepareForUpdate(existing, request.Payload!, id); + var updateResult = await _categoryRepository.UpdateAsync(updatedCategory, cancellationToken); + + return updateResult.RequestFailed + ? updateResult + : ApiResponseDto.Success(updateResult.Data); + } + + private ApiResponseDto? ValidateUpdateRequest(ApiRequestDto request) + { + if (request.Payload is null) + return ApiResponseDto.Failure( + HttpStatusCode.BadRequest, + "Request payload is required." + ); + return null; + } + + private async Task GetExistingCategoryAsync(int id, CancellationToken cancellationToken) + { + var result = await _categoryRepository.GetByIdAsync(id, cancellationToken); + return result.Data; + } + + private async Task?> ValidateCategoryNameAsync( + Category incoming, Category existing, CancellationToken cancellationToken) + { + var newName = string.IsNullOrWhiteSpace(incoming.Name) ? existing.Name : incoming.Name.Trim(); + + if (string.Equals(newName, existing.Name, StringComparison.OrdinalIgnoreCase)) + return null; + + var byName = await _categoryRepository.GetByNameAsync(newName, cancellationToken); + var nameExists = !byName.RequestFailed && byName.Data?.CategoryId != existing.CategoryId; + + if (nameExists) + return ApiResponseDto.Failure( + HttpStatusCode.Conflict, + $"Category with name '{newName}' already exists." + ); + + return null; + } + + public async Task> DeleteCategoryAsync( + int id, + CancellationToken cancellationToken = default + ) + { + ApiResponseDto repoResult = await _categoryRepository.GetByIdAsync( + id, + cancellationToken + ); + if (repoResult.Data is null) + return ApiResponseDto.Failure( + HttpStatusCode.NotFound, + $"Category with id {id} not found." + ); + + ApiResponseDto delResult = await _categoryRepository.DeleteAsync( + repoResult.Data.CategoryId, + cancellationToken + ); + return delResult.RequestFailed + ? delResult + : ApiResponseDto.Success(true, HttpStatusCode.NoContent); + } + + public async Task> GetCategoryByNameAsync( + string name, + CancellationToken cancellationToken = default + ) + { + if (string.IsNullOrWhiteSpace(name)) + return ApiResponseDto.Failure(HttpStatusCode.BadRequest, "Name is required."); + + ApiResponseDto repoResult = await _categoryRepository.GetByNameAsync( + name.Trim(), + cancellationToken + ); + return repoResult.RequestFailed || repoResult.Data is null + ? ApiResponseDto.Failure( + HttpStatusCode.NotFound, + $"Category '{name}' not found." + ) + : ApiResponseDto.Success(repoResult.Data); + } + + public async Task>> GetDeletedCategoriesAsync( + CancellationToken cancellationToken = default + ) + { + try + { + ApiResponseDto> result = + await _categoryRepository.GetDeletedCategoriesAsync(cancellationToken); + return result.RequestFailed + ? result + : ApiResponseDto>.Success(result.Data); + } + catch (Exception ex) + { + return ApiResponseDto>.Failure( + HttpStatusCode.InternalServerError, + $"Failed to retrieve deleted categories: {ex.Message}" + ); + } + } + + public async Task> RestoreCategoryAsync( + int id, + CancellationToken cancellationToken = default + ) + { + try + { + ApiResponseDto result = await _categoryRepository.RestoreAsync( + id, + cancellationToken + ); + return result.RequestFailed ? result : ApiResponseDto.Success(result.Data); + } + catch (Exception ex) + { + return ApiResponseDto.Failure( + HttpStatusCode.InternalServerError, + $"Failed to restore category: {ex.Message}" + ); + } + } +} diff --git a/Services/Helpers/CategoryProcessingHelper.cs b/Services/Helpers/CategoryProcessingHelper.cs new file mode 100644 index 00000000..765db5d9 --- /dev/null +++ b/Services/Helpers/CategoryProcessingHelper.cs @@ -0,0 +1,40 @@ +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Interfaces.Helpers; + +namespace ECommerceApp.RyanW84.Services.Helpers; + +public class CategoryProcessingHelper : ICategoryProcessingHelper +{ + public ApiResponseDto? ValidateCreateRequest(ApiRequestDto request) + { + if (request.Payload is null || string.IsNullOrWhiteSpace(request.Payload.Name)) + return ApiResponseDto.Failure(System.Net.HttpStatusCode.BadRequest, "Category name is required."); + return null; + } + + public Category PrepareForCreate(Category incoming) + { + return new Category + { + Name = incoming.Name.Trim(), + Description = incoming.Description.Trim(), + }; + } + + public Category PrepareForUpdate(Category existing, Category incoming, int id) + { + return new Category + { + CategoryId = id, + Name = string.IsNullOrWhiteSpace(incoming.Name) ? existing.Name : incoming.Name.Trim(), + Description = string.IsNullOrWhiteSpace(incoming.Description) ? existing.Description : incoming.Description.Trim(), + Products = existing.Products, + Sales = existing.Sales, + IsDeleted = existing.IsDeleted, + DeletedAt = existing.DeletedAt, + CreatedAt = existing.CreatedAt, + UpdatedAt = DateTime.UtcNow + }; + } +} diff --git a/Services/Helpers/InputSanitizationHelper.cs b/Services/Helpers/InputSanitizationHelper.cs new file mode 100644 index 00000000..88563eb6 --- /dev/null +++ b/Services/Helpers/InputSanitizationHelper.cs @@ -0,0 +1,98 @@ +using System.Text.RegularExpressions; + +namespace ECommerceApp.RyanW84.Services.Helpers; + +/// +/// Utility helper class for input sanitization and string validation. +/// Provides methods for trimming, validation, and safe string handling. +/// +public static class InputSanitizationHelper +{ + /// + /// Safely trims a string and returns null if result is empty. + /// + /// The input string to trim + /// Trimmed string or null if empty after trimming + public static string? SafeTrim(string? input) + { + if (string.IsNullOrWhiteSpace(input)) + return null; + + var trimmed = input.Trim(); + return string.IsNullOrEmpty(trimmed) ? null : trimmed; + } + + /// + /// Validates that an email address has a basic valid format. + /// + /// The email address to validate + /// True if email format is valid + public static bool IsValidEmail(string? email) + { + if (string.IsNullOrWhiteSpace(email)) + return false; + + try + { + var addr = new System.Net.Mail.MailAddress(email); + return addr.Address == email; + } + catch + { + return false; + } + } + + /// + /// Removes potentially harmful characters from input. + /// Sanitizes common SQL injection and XSS attempt patterns. + /// + /// The input string to sanitize + /// Sanitized string + public static string SanitizeInput(string? input) + { + if (string.IsNullOrWhiteSpace(input)) + return string.Empty; + + // Remove potential SQL injection characters (basic protection) + // Note: Parameterized queries provide the primary defense + var sanitized = Regex.Replace(input, @"[;'""--]", ""); + return sanitized.Trim(); + } + + /// + /// Validates that a string is not null or contains only whitespace. + /// + /// The input string to check + /// The name of the field (for error messages) + /// Validation error message if invalid, null if valid + public static string? ValidateNotEmpty(string? input, string fieldName) + { + if (string.IsNullOrWhiteSpace(input)) + return $"{fieldName} is required and cannot be empty."; + + return null; + } + + /// + /// Validates that a string length is within acceptable bounds. + /// + /// The input string to check + /// Minimum acceptable length + /// Maximum acceptable length + /// The name of the field (for error messages) + /// Validation error message if invalid, null if valid + public static string? ValidateLength(string? input, int minLength, int maxLength, string fieldName) + { + if (string.IsNullOrWhiteSpace(input)) + return $"{fieldName} is required."; + + if (input.Length < minLength) + return $"{fieldName} must be at least {minLength} characters long."; + + if (input.Length > maxLength) + return $"{fieldName} must not exceed {maxLength} characters."; + + return null; + } +} diff --git a/Services/Helpers/ProductProcessingHelper.cs b/Services/Helpers/ProductProcessingHelper.cs new file mode 100644 index 00000000..21d9435d --- /dev/null +++ b/Services/Helpers/ProductProcessingHelper.cs @@ -0,0 +1,36 @@ +using System.Net; +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Interfaces.Helpers; + +namespace ECommerceApp.RyanW84.Services.Helpers; + +public class ProductProcessingHelper : IProductProcessingHelper +{ + public ApiResponseDto? ValidateCreateRequest(ApiRequestDto request) + { + if (request.Payload is null) + return ApiResponseDto.Failure(HttpStatusCode.BadRequest, "Product data is required"); + + return null; + } + + public Product PrepareForUpdate(Product existing, Product incoming, int id) + { + // Enforce route id; allow incoming values to override while keeping audit fields + return new Product + { + ProductId = id, + Name = string.IsNullOrWhiteSpace(incoming.Name) ? existing.Name : incoming.Name.Trim(), + Description = string.IsNullOrWhiteSpace(incoming.Description) ? existing.Description : incoming.Description.Trim(), + Price = incoming.Price, + Stock = incoming.Stock, + CategoryId = incoming.CategoryId, + IsActive = incoming.IsActive, + IsDeleted = existing.IsDeleted, + DeletedAt = existing.DeletedAt, + CreatedAt = existing.CreatedAt, + UpdatedAt = DateTime.UtcNow + }; + } +} diff --git a/Services/Helpers/ProductQueryHelper.cs b/Services/Helpers/ProductQueryHelper.cs new file mode 100644 index 00000000..78c3093e --- /dev/null +++ b/Services/Helpers/ProductQueryHelper.cs @@ -0,0 +1,15 @@ +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Interfaces.Helpers; + +namespace ECommerceApp.RyanW84.Services.Helpers; + +public class ProductQueryHelper : IProductQueryHelper +{ + public void NormalizePriceRange(ProductQueryParameters parameters) + { + if (parameters.MinPrice.HasValue && parameters.MaxPrice.HasValue && parameters.MinPrice > parameters.MaxPrice) + { + (parameters.MinPrice, parameters.MaxPrice) = (parameters.MaxPrice, parameters.MinPrice); + } + } +} diff --git a/Services/Helpers/SaleProcessingHelper.cs b/Services/Helpers/SaleProcessingHelper.cs new file mode 100644 index 00000000..0932bdca --- /dev/null +++ b/Services/Helpers/SaleProcessingHelper.cs @@ -0,0 +1,141 @@ +using System.Net; +using ECommerceApp.RyanW84.Data; +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Interfaces.Helpers; +using Microsoft.EntityFrameworkCore; + +namespace ECommerceApp.RyanW84.Services.Helpers; + +public class SaleProcessingHelper(ECommerceDbContext db) : ISaleProcessingHelper +{ + private readonly ECommerceDbContext _db = db; + + public ApiResponseDto? ValidateCreateSaleRequest(ApiRequestDto request) + { + var payload = request.Payload; + if (payload is null || payload.SaleItems.Count == 0) + return ApiResponseDto.Failure( + HttpStatusCode.BadRequest, + "Invalid request or no items provided." + ); + return null; + } + + public async Task<( + bool IsError, + List Data, + ApiResponseDto Error + )> FetchAndValidateProductsAsync(Sale payload, CancellationToken cancellationToken) + { + var productIds = payload.SaleItems.Select(si => si.ProductId).Distinct().ToList(); + var products = await _db + .Products.Where(p => productIds.Contains(p.ProductId)) + .ToListAsync(cancellationToken); + + foreach (var item in payload.SaleItems) + { + var product = products.FirstOrDefault(p => p.ProductId == item.ProductId); + if (product is null) + return ( + true, + [], + ApiResponseDto.Failure( + HttpStatusCode.BadRequest, + $"Product {item.ProductId} not found." + ) + ); + + if (product.Stock < item.Quantity) + return ( + true, + [], + ApiResponseDto.Failure( + HttpStatusCode.Conflict, + $"Insufficient stock for product {product.ProductId}." + ) + ); + } + + return (false, products, null!); + } + + public async Task> ExecuteSaleTransactionAsync( + Sale payload, + List products, + CancellationToken cancellationToken + ) + { + await using var tx = await _db.Database.BeginTransactionAsync(cancellationToken); + try + { + var sale = new Sale + { + SaleDate = payload.SaleDate, + CustomerName = payload.CustomerName, + CustomerEmail = payload.CustomerEmail, + CustomerAddress = payload.CustomerAddress, + TotalAmount = 0m, + }; + + _db.Sales.Add(sale); + await _db.SaveChangesAsync(cancellationToken); + + decimal total = await ProcessSaleItemsAsync( + sale, + payload.SaleItems.ToList(), + products, + cancellationToken + ); + + sale.TotalAmount = total; + _db.Sales.Update(sale); + await _db.SaveChangesAsync(cancellationToken); + await tx.CommitAsync(cancellationToken); + + await _db.Entry(sale) + .Collection(s => s.SaleItems) + .Query() + .Include(si => si.Product) + .LoadAsync(cancellationToken); + return ApiResponseDto.Success(sale, HttpStatusCode.Created); + } + catch (Exception ex) + { + await tx.RollbackAsync(cancellationToken); + return ApiResponseDto.Failure(HttpStatusCode.InternalServerError, ex.Message); + } + } + + private async Task ProcessSaleItemsAsync( + Sale sale, + List saleItems, + List products, + CancellationToken cancellationToken + ) + { + decimal total = 0m; + foreach (var item in saleItems) + { + var product = products.First(p => p.ProductId == item.ProductId); + var unitPrice = product.Price; + var lineTotal = unitPrice * item.Quantity; + total += lineTotal; + + var saleItem = new SaleItem + { + SaleId = sale.SaleId, + ProductId = product.ProductId, + Quantity = item.Quantity, + UnitPrice = unitPrice, + }; + _db.SaleItems.Add(saleItem); + + product.Stock -= item.Quantity; + _db.Products.Update(product); + } + + await _db.SaveChangesAsync(cancellationToken); + return total; + } +} diff --git a/Services/Helpers/SaleQueryHelper.cs b/Services/Helpers/SaleQueryHelper.cs new file mode 100644 index 00000000..914ce92a --- /dev/null +++ b/Services/Helpers/SaleQueryHelper.cs @@ -0,0 +1,33 @@ +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Interfaces.Helpers; + +namespace ECommerceApp.RyanW84.Services.Helpers; + +public class SaleQueryHelper : ISaleQueryHelper +{ + public void NormalizeDateRange(SaleQueryParameters parameters) + { + if ( + parameters is { StartDate: not null, EndDate: not null } + && parameters.StartDate > parameters.EndDate + ) + (parameters.StartDate, parameters.EndDate) = (parameters.EndDate, parameters.StartDate); + } + + public void FilterHistoricalItems(Sale sale) + { + DateTime saleDate = sale.SaleDate; + sale.SaleItems = sale + .SaleItems.Where(si => si.Product != null && (!si.Product.IsDeleted || si.Product.DeletedAt > saleDate)) + .ToList(); + } + + public void FilterHistoricalItems(IEnumerable sales) + { + foreach (var sale in sales) + { + FilterHistoricalItems(sale); + } + } +} diff --git a/Services/ProductService.cs b/Services/ProductService.cs new file mode 100644 index 00000000..1f32afc4 --- /dev/null +++ b/Services/ProductService.cs @@ -0,0 +1,256 @@ +using System.Net; +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Interfaces; +using ECommerceApp.RyanW84.Interfaces.Helpers; + +namespace ECommerceApp.RyanW84.Services +{ + /// + /// Service layer for managing product operations. + /// Handles product creation, retrieval, updates, and deletion. + /// Delegates validation to helpers and persistence to repositories. + /// + public class ProductService( + IProductRepository productRepository, + IProductProcessingHelper productProcessingHelper, + IProductQueryHelper productQueryHelper + ) : IProductService + { + private readonly IProductRepository _productRepository = productRepository; + private readonly IProductProcessingHelper _productProcessingHelper = productProcessingHelper; + private readonly IProductQueryHelper _productQueryHelper = productQueryHelper; + + /// + /// Creates a new product in the database. + /// + /// The product creation request + /// Cancellation token + /// Created product or error response + public async Task> CreateProductAsync( + ApiRequestDto request, + CancellationToken cancellationToken = default + ) + { + var validation = _productProcessingHelper.ValidateCreateRequest(request); + if (validation != null) return validation; + + try + { + ApiResponseDto result = await _productRepository.AddAsync( + request.Payload!, + cancellationToken + ); + return result.RequestFailed + ? result + : ApiResponseDto.Success(result.Data, HttpStatusCode.Created); + } + catch (Exception ex) + { + return ApiResponseDto.Failure( + HttpStatusCode.InternalServerError, + $"Failed to create product: {ex.Message}" + ); + } + } + + public async Task>> GetProductsAsync( + ProductQueryParameters parameters, + CancellationToken cancellationToken = default + ) + { + try + { + _productQueryHelper.NormalizePriceRange(parameters); + + PaginatedResponseDto> result = + await _productRepository.GetAllProductsAsync(parameters, cancellationToken); + if (result.RequestFailed) + return PaginatedResponseDto>.Failure( + result.ResponseCode, + result.ErrorMessage, + parameters.Page, + parameters.PageSize + ); + + return PaginatedResponseDto>.Success( + result.Data, + result.CurrentPage, + result.PageSize, + result.TotalCount + ); + } + catch (Exception ex) + { + return PaginatedResponseDto>.Failure( + HttpStatusCode.InternalServerError, + $"Failed to retrieve products: {ex.Message}", + parameters.Page, + parameters.PageSize + ); + } + } + + public async Task> GetProductByIdAsync( + int id, + CancellationToken cancellationToken = default + ) + { + try + { + ApiResponseDto result = await _productRepository.GetByIdAsync( + id, + cancellationToken + ); + if (result.RequestFailed) + return result; + if (result.Data is null) + return ApiResponseDto.Failure(HttpStatusCode.NotFound, "Product not found."); + + return ApiResponseDto.Success(result.Data); + } + catch (Exception ex) + { + return ApiResponseDto.Failure( + HttpStatusCode.InternalServerError, + $"Failed to retrieve product: {ex.Message}" + ); + } + } + + public async Task>> GetProductsByCategoryIdAsync( + int categoryId, + CancellationToken cancellationToken = default + ) + { + try + { + ApiResponseDto> result = + await _productRepository.GetProductsByCategoryIdAsync( + categoryId, + cancellationToken + ); + if (result.RequestFailed) + return result; + return ApiResponseDto>.Success(result.Data); + } + catch (Exception ex) + { + return ApiResponseDto>.Failure( + HttpStatusCode.InternalServerError, + $"Failed to retrieve products: {ex.Message}" + ); + } + } + + public async Task> UpdateProductAsync( + int id, + ApiRequestDto request, + CancellationToken cancellationToken = default + ) + { + try + { + if (request.Payload == null) + return ApiResponseDto.Failure( + HttpStatusCode.BadRequest, + "Product data is required" + ); + + ApiResponseDto existingResult = await _productRepository.GetByIdAsync( + id, + cancellationToken + ); + if (existingResult.RequestFailed || existingResult.Data == null) + return ApiResponseDto.Failure( + HttpStatusCode.NotFound, + "Product not found" + ); + + var prepared = _productProcessingHelper.PrepareForUpdate(existingResult.Data!, request.Payload, id); + + ApiResponseDto result = await _productRepository.UpdateAsync( + prepared, + cancellationToken + ); + return result.RequestFailed ? result : ApiResponseDto.Success(result.Data); + } + catch (Exception ex) + { + return ApiResponseDto.Failure( + HttpStatusCode.InternalServerError, + $"Failed to update product: {ex.Message}" + ); + } + } + + public async Task> DeleteProductAsync( + int id, + CancellationToken cancellationToken = default + ) + { + try + { + ApiResponseDto result = await _productRepository.DeleteAsync( + id, + cancellationToken + ); + if (result.RequestFailed) + return result; + return ApiResponseDto.Success(result.Data, HttpStatusCode.NoContent); + } + catch (Exception ex) + { + return ApiResponseDto.Failure( + HttpStatusCode.InternalServerError, + $"Failed to delete product: {ex.Message}" + ); + } + } + + public async Task>> GetDeletedProductsAsync( + CancellationToken cancellationToken = default + ) + { + try + { + ApiResponseDto> result = + await _productRepository.GetDeletedProductsAsync(cancellationToken); + if (result.RequestFailed) + return result; + return ApiResponseDto>.Success(result.Data); + } + catch (Exception ex) + { + return ApiResponseDto>.Failure( + HttpStatusCode.InternalServerError, + $"Failed to retrieve deleted products: {ex.Message}" + ); + } + } + + public async Task> RestoreProductAsync( + int id, + CancellationToken cancellationToken = default + ) + { + try + { + ApiResponseDto result = await _productRepository.RestoreAsync( + id, + cancellationToken + ); + if (result.RequestFailed) + return result; + return ApiResponseDto.Success(result.Data); + } + catch (Exception ex) + { + return ApiResponseDto.Failure( + HttpStatusCode.InternalServerError, + $"Failed to restore product: {ex.Message}" + ); + } + } + } +} diff --git a/Services/SaleService.cs b/Services/SaleService.cs new file mode 100644 index 00000000..77435b7c --- /dev/null +++ b/Services/SaleService.cs @@ -0,0 +1,185 @@ +using System.Net; +using ECommerceApp.RyanW84.Data; +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Interfaces; +using Microsoft.EntityFrameworkCore; +using ECommerceApp.RyanW84.Interfaces.Helpers; + +namespace ECommerceApp.RyanW84.Services; + +public class SaleService( + ECommerceDbContext db, + ISaleRepository saleRepository, + ISaleProcessingHelper saleProcessingHelper, + ISaleQueryHelper saleQueryHelper +) : ISaleService +{ + private readonly ECommerceDbContext _db = db; + private readonly ISaleRepository _saleRepository = saleRepository; + private readonly ISaleProcessingHelper _saleProcessingHelper = saleProcessingHelper; + private readonly ISaleQueryHelper _saleQueryHelper = saleQueryHelper; + + public async Task> CreateSaleAsync( + ApiRequestDto request, + CancellationToken cancellationToken = default + ) + { + var validationError = _saleProcessingHelper.ValidateCreateSaleRequest(request); + if (validationError != null) + return validationError; + + var products = await _saleProcessingHelper.FetchAndValidateProductsAsync(request.Payload!, cancellationToken); + if (products.IsError) + return products.Error; + + return await _saleProcessingHelper.ExecuteSaleTransactionAsync( + request.Payload!, + products.Data, + cancellationToken + ); + } + + // Extracted helper methods into ISaleProcessingHelper + + public async Task> GetSaleByIdAsync( + int id, + CancellationToken cancellationToken = default + ) + { + Sale? sale = await _db + .Sales.AsNoTracking() + .Include(s => s.SaleItems) + .ThenInclude(si => si.Product) + .FirstOrDefaultAsync(s => s.SaleId == id, cancellationToken); + + return sale is null + ? ApiResponseDto.Failure(HttpStatusCode.NotFound, $"Sale {id} not found.") + : ApiResponseDto.Success(sale); + } + + public async Task>> GetSalesAsync( + SaleQueryParameters? parameters, + CancellationToken cancellationToken = default + ) + { + try + { + parameters ??= new SaleQueryParameters + { + Page = 0, + PageSize = 0, + StartDate = null, + EndDate = null, + CustomerName = null, + CustomerEmail = null, + SortBy = null, + SortDirection = null + }; + _saleQueryHelper.NormalizeDateRange(parameters); + + var result = await _saleRepository.GetAllSalesAsync(parameters, cancellationToken); + return result.RequestFailed + ? PaginatedResponseDto>.Failure( + result.ResponseCode, + result.ErrorMessage, + parameters.Page, + parameters.PageSize + ) + : PaginatedResponseDto>.Success( + result.Data, + result.CurrentPage, + result.PageSize, + result.TotalCount + ); + } + catch (Exception ex) + { + return PaginatedResponseDto>.Failure( + HttpStatusCode.InternalServerError, + $"Failed to retrieve sales: {ex.Message}", + parameters?.Page ?? 1, + parameters?.PageSize ?? 10 + ); + } + } + + // Moved to ISaleQueryHelper + + public async Task> GetSaleByIdWithHistoricalProductsAsync( + int id, + CancellationToken cancellationToken = default + ) + { + Sale? sale = await _db + .Sales.AsNoTracking() + .Include(s => s.SaleItems) + .ThenInclude(si => si.Product) + .IgnoreQueryFilters() // Include deleted products + .FirstOrDefaultAsync(s => s.SaleId == id, cancellationToken); + + if (sale is null) + return ApiResponseDto.Failure(HttpStatusCode.NotFound, $"Sale {id} not found."); + + _saleQueryHelper.FilterHistoricalItems(sale); + + return ApiResponseDto.Success(sale); + } + + public async Task< + ApiResponseDto> + > GetHistoricalSalesAsync(CancellationToken cancellationToken = default) + { + List list = await _db + .Sales.AsNoTracking() + .Include(s => s.SaleItems) + .ThenInclude(si => si.Product) + .IgnoreQueryFilters() // Include deleted products + .ToListAsync(cancellationToken); + + _saleQueryHelper.FilterHistoricalItems(list); + + return ApiResponseDto>.Success(list); + } + + public async Task> UpdateSaleAsync( + int id, + ApiRequestDto request, + CancellationToken cancellationToken = default + ) + { + if (request.Payload is null) + return ApiResponseDto.Failure(HttpStatusCode.BadRequest, "Invalid request payload"); + + // Ensure route id is enforced + Sale incoming = request.Payload; + incoming.SaleId = id; + + // Ensure sale exists + ApiResponseDto existing = await _saleRepository.GetByIdAsync(id, cancellationToken); + if (existing.RequestFailed) + return ApiResponseDto.Failure(existing.ResponseCode, existing.ErrorMessage); + if (existing.Data is null) + return ApiResponseDto.Failure(HttpStatusCode.NotFound, $"Sale {id} not found."); + + // Delegate update to repository (keeps logic centralized) + ApiResponseDto updated = await _saleRepository.UpdateAsync(incoming, cancellationToken); + return updated; + } + + public async Task> DeleteSaleAsync( + int id, + CancellationToken cancellationToken = default + ) + { + // Ensure sale exists before attempting delete + ApiResponseDto existing = await _saleRepository.GetByIdAsync(id, cancellationToken); + if (existing.RequestFailed) + return ApiResponseDto.Failure(existing.ResponseCode, existing.ErrorMessage); + if (existing.Data is null) + return ApiResponseDto.Failure(HttpStatusCode.NotFound, $"Sale {id} not found."); + + ApiResponseDto result = await _saleRepository.DeleteAsync(id, cancellationToken); + return result; + } +} diff --git a/Services/SalesSummaryService.cs b/Services/SalesSummaryService.cs new file mode 100644 index 00000000..0e63e72d --- /dev/null +++ b/Services/SalesSummaryService.cs @@ -0,0 +1,43 @@ +using ECommerceApp.RyanW84.Data; +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Interfaces; +using Microsoft.EntityFrameworkCore; + +namespace ECommerceApp.RyanW84.Services; + +public class SalesSummaryService(ECommerceDbContext db) : ISalesSummaryService +{ + private readonly ECommerceDbContext _db = db; + + public async Task> GetSalesSummaryAsync( + CancellationToken cancellationToken = default + ) + { + List salesData = await _db + .Sales.AsNoTracking() + .Include(s => s.SaleItems) + .ThenInclude(si => si.Product!) + .ThenInclude(p => p.Category) + .ToListAsync(cancellationToken); + + return salesData + .SelectMany(s => s.SaleItems, (sale, item) => new { Sale = sale, Item = item }) + .Where(x => x.Item.Product is { Category: not null }) + .GroupBy(x => new + { + ProductName = x.Item.Product!.Name, + CategoryName = x.Item.Product!.Category!.Name, + }) + .Select(g => new SalesSummaryDto + { + ProductName = g.Key.ProductName, + CategoryName = g.Key.CategoryName, + TotalQuantitySold = g.Sum(x => x.Item.Quantity), + TotalRevenue = g.Sum(x => x.Item.LineTotal), + LastSaleDate = g.Max(x => x.Sale.SaleDate), + }) + .OrderByDescending(x => x.TotalRevenue) + .ToList(); + } +} diff --git a/appsettings.Development.json b/appsettings.Development.json new file mode 100644 index 00000000..dda1ed7e --- /dev/null +++ b/appsettings.Development.json @@ -0,0 +1,13 @@ +{ + "ScalarUi": { + "Title": "ECommerceApp.RyanW84 API Documentation", + "Theme": "blueplanet", + "Layout": "modern", + "DarkMode": true, + "HideModels": false, + "ShowSidebar": true, + "DefaultOpenAllTags": true, + "SearchHotKey": "k", + "CustomCss": "\n .scalar-api-reference {\n --scalar-color-primary: #2563eb;\n --scalar-color-secondary: #64748b;\n --scalar-color-accent: #06b6d4;\n }\n .dark-mode .scalar-api-reference {\n --scalar-color-background: #0f172a;\n --scalar-color-1: #4e668dff;\n --scalar-color-2: #67778fff;\n --scalar-color-3: #6d809bff;\n }" + } +} diff --git a/appsettings.json b/appsettings.json new file mode 100644 index 00000000..747fc5bf --- /dev/null +++ b/appsettings.json @@ -0,0 +1,18 @@ +{ + "ConnectionStrings": {}, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ApiConfiguration": { + "DefaultPageSize": 10, + "MaxPageSize": 100, + "ProductCacheDurationSeconds": 30, + "ProductDetailCacheDurationSeconds": 120, + "CategoryCacheDurationSeconds": 30, + "SaleCacheDurationSeconds": 30 + } +} \ No newline at end of file diff --git a/dotnet-tools.json b/dotnet-tools.json new file mode 100644 index 00000000..0d6d3049 --- /dev/null +++ b/dotnet-tools.json @@ -0,0 +1,13 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "dotnet-ef": { + "version": "10.0.0", + "commands": [ + "dotnet-ef" + ], + "rollForward": false + } + } +} \ No newline at end of file diff --git a/global.json b/global.json new file mode 100644 index 00000000..f72210ca --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "10.0.100", + "rollForward": "latestFeature" + } +} \ No newline at end of file diff --git a/tests/ArchitectureTests/ECommerceApp.ArchitectureTests.csproj b/tests/ArchitectureTests/ECommerceApp.ArchitectureTests.csproj new file mode 100644 index 00000000..82478834 --- /dev/null +++ b/tests/ArchitectureTests/ECommerceApp.ArchitectureTests.csproj @@ -0,0 +1,25 @@ + + + + net10.0 + false + enable + enable + true + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/tests/ArchitectureTests/SmokeTests.cs b/tests/ArchitectureTests/SmokeTests.cs new file mode 100644 index 00000000..d73b5187 --- /dev/null +++ b/tests/ArchitectureTests/SmokeTests.cs @@ -0,0 +1,12 @@ +using Xunit; + +namespace ECommerceApp.ArchitectureTests; + +public class SmokeTests +{ + [Fact] + public void True_is_true() + { + Assert.True(true); + } +} From 7c1914f0fc1a7bbb7be5facfdc9cf1b0f1d72512 Mon Sep 17 00:00:00 2001 From: Ryan Weavers Date: Sun, 11 Jan 2026 21:24:15 +0000 Subject: [PATCH 2/3] submitted for review --- .gitignore | 4 + Directory.Build.props | 20 + Dockerfile | 45 + ECommerceAPI.postman_collection.json | 1099 +++++++++++++++++ ECommerceApp.RyanW84.csproj | 45 + ECommerceApp.RyanW84.csproj.backup | 925 ++++++++++++++ NuGet.config | 10 + PostmanAPITests.json | 705 +++++++++++ README.md | 347 ++++++ Validators/ApiRequestDtoValidator.cs | 19 + .../CategoryQueryParametersValidator.cs | 41 + Validators/CategoryValidator.cs | 18 + Validators/ProductQueryParametersValidator.cs | 62 + Validators/ProductValidator.cs | 32 + Validators/SaleItemValidator.cs | 16 + Validators/SaleQueryParametersValidator.cs | 57 + Validators/SaleValidator.cs | 44 + appsettings.Production.json | 16 + docker-compose.yml | 51 + packages.lock.json | 682 ++++++++++ tests/ArchitectureTests/packages.lock.json | 671 ++++++++++ .../ApiIntegrationTests.cs | 240 ++++ .../ECommerceApp.IntegrationTests.csproj | 28 + .../EcommerceApiFactory.cs | 58 + .../packages.lock.json | 870 +++++++++++++ .../xunit.runner.json | 6 + .../CategoryValidatorTests.cs | 48 + .../ConsoleClient/SpectreConsoleCollection.cs | 9 + .../TableRendererDynamicPagingTests.cs | 179 +++ .../TableRendererDynamicPagingTheoryTests.cs | 337 +++++ .../ECommerceApp.UnitTests.csproj | 30 + .../ProductValidatorTests.cs | 78 ++ .../ECommerceApp.UnitTests/packages.lock.json | 711 +++++++++++ .../ECommerceApp.UnitTests/xunit.runner.json | 6 + 34 files changed, 7509 insertions(+) create mode 100644 Directory.Build.props create mode 100644 Dockerfile create mode 100644 ECommerceAPI.postman_collection.json create mode 100644 ECommerceApp.RyanW84.csproj create mode 100644 ECommerceApp.RyanW84.csproj.backup create mode 100644 NuGet.config create mode 100644 PostmanAPITests.json create mode 100644 README.md create mode 100644 Validators/ApiRequestDtoValidator.cs create mode 100644 Validators/CategoryQueryParametersValidator.cs create mode 100644 Validators/CategoryValidator.cs create mode 100644 Validators/ProductQueryParametersValidator.cs create mode 100644 Validators/ProductValidator.cs create mode 100644 Validators/SaleItemValidator.cs create mode 100644 Validators/SaleQueryParametersValidator.cs create mode 100644 Validators/SaleValidator.cs create mode 100644 appsettings.Production.json create mode 100644 docker-compose.yml create mode 100644 packages.lock.json create mode 100644 tests/ArchitectureTests/packages.lock.json create mode 100644 tests/ECommerceApp.IntegrationTests/ApiIntegrationTests.cs create mode 100644 tests/ECommerceApp.IntegrationTests/ECommerceApp.IntegrationTests.csproj create mode 100644 tests/ECommerceApp.IntegrationTests/EcommerceApiFactory.cs create mode 100644 tests/ECommerceApp.IntegrationTests/packages.lock.json create mode 100644 tests/ECommerceApp.IntegrationTests/xunit.runner.json create mode 100644 tests/ECommerceApp.UnitTests/CategoryValidatorTests.cs create mode 100644 tests/ECommerceApp.UnitTests/ConsoleClient/SpectreConsoleCollection.cs create mode 100644 tests/ECommerceApp.UnitTests/ConsoleClient/TableRendererDynamicPagingTests.cs create mode 100644 tests/ECommerceApp.UnitTests/ConsoleClient/TableRendererDynamicPagingTheoryTests.cs create mode 100644 tests/ECommerceApp.UnitTests/ECommerceApp.UnitTests.csproj create mode 100644 tests/ECommerceApp.UnitTests/ProductValidatorTests.cs create mode 100644 tests/ECommerceApp.UnitTests/packages.lock.json create mode 100644 tests/ECommerceApp.UnitTests/xunit.runner.json diff --git a/.gitignore b/.gitignore index 154e1272..ac78fb2e 100644 --- a/.gitignore +++ b/.gitignore @@ -475,3 +475,7 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk + + +#Ignore vscode AI rules +.github/instructions/codacy.instructions.md diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 00000000..f6a603e2 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,20 @@ + + + + true + true + + + $(DefaultItemExcludes);bin\**;obj\**;tests\**\bin\**;tests\**\obj\** + false + + + portable + none + false + + + true + true + + diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..356a9da6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,45 @@ +# Build stage +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build +WORKDIR /src + +# Copy project files + +# Restore dependencies + +# Copy source code + +# Build the application + +# Publish stage +FROM build AS publish +RUN dotnet publish "ECommerceApp.RyanW84.csproj" -c Release -o /app/publish /p:UseAppHost=false + +# Runtime stage + +# Create logs directory + +# Expose ports + +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base +WORKDIR /app + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +WORKDIR /src +COPY ["ECommerceApp.RyanW84.csproj", "."] +RUN dotnet restore "ECommerceApp.RyanW84.csproj" +COPY . . +WORKDIR "/src/" +RUN dotnet build "ECommerceApp.RyanW84.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "ECommerceApp.RyanW84.csproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . + +# Set environment variables for ASP.NET Core to use user secrets +ENV ASPNETCORE_ENVIRONMENT=Development + +# Entrypoint +ENTRYPOINT ["dotnet", "ECommerceApp.RyanW84.dll"] diff --git a/ECommerceAPI.postman_collection.json b/ECommerceAPI.postman_collection.json new file mode 100644 index 00000000..54c59b18 --- /dev/null +++ b/ECommerceAPI.postman_collection.json @@ -0,0 +1,1099 @@ +{ + "info": { + "name": "ECommerce API Collection", + "description": "Complete collection of ECommerce API endpoints for products, categories, and sales management", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Products", + "item": [ + { + "name": "Get All Products", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/products?page=1&pageSize=10", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "products" + ], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "pageSize", + "value": "10" + } + ] + }, + "description": "Retrieve all products with optional pagination" + }, + "response": [ + { + "name": "ECommerce API (CRUD)", + "description": "Collection for exercising ECommerce API CRUD endpoints (Categories, Product, Sales) with pagination, soft-delete/restore where supported, and helper scripts to capture created IDs into collection variables.", + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/products?page=1&pageSize=10", + "name": "Categories", + "path": [ + "api", + "products" + ], + "query": [ + "name": "List Categories (paged)", + "key": "page", + "value": "1""header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + { + "raw": "{{baseUrl}}/api/categories?page={{page}}&pageSize={{pageSize}}", + "value": "10""path": [ + "api", + "categories" + ], + ] + } + }, + "value": "{{page}}""code": 200, + "_postman_previewlanguage": "json", + "header": [ + "value": "{{pageSize}}""key": "Content-Type", + "value": "application/json" + } + "description": "Retrieve categories with pagination.""cookie": [], + "key": "Content-Type", + "value": "application/json""name": "Get Category by ID", + ], + "body": { + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "raw": "{\n \"payload\": {\n \"name\": \"New Product\",\n \"description\": \"Product description\",\n \"price\": 29.99,\n \"stock\": 100,\n \"isActive\": true,\n \"categoryId\": 1\n }\n}""raw": "{{baseUrl}}/api/categories/{{categoryId}}", + "url": { + "path": [ + "api", + "categories", + "{{categoryId}}" + ] + "host": [ + "{{baseUrl}}" + ], + "description": "Retrieve a specific category by ID." + }, + "description": "Create a new product" + } + "name": "Get Category by Name", + { + "name": "Update Product", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "method": "PUT", + "raw": "{{baseUrl}}/api/categories/name/{{categoryName}}", + { + "path": [ + "api", + "categories", + "name", + "{{categoryName}}" + ] + "value": "application/json""description": "Retrieve a category by its name." + ], + "body": { + "mode": "raw", + "name": "Create Category", + }, + "url": { + "raw": "{{baseUrl}}/api/products/1", + "host": [ + "{{baseUrl}}" + ], + "key": "Accept", + "value": "application/json" + }, + { + "path": [ + "api", + "products", + "1" + ] + }, + "description": "Update an existing product" + } + }, + { + "raw": "{\n \"payload\": {\n \"name\": \"New Category\",\n \"description\": \"Category description\"\n }\n}""request": { + "method": "DELETE", + "raw": "{{baseUrl}}/api/categories", + "url": { + "path": [ + "api", + "categories" + ] + "host": [ + "{{baseUrl}}" + ], + "description": "Create a new category. Saves the returned CategoryId into collection variable `categoryId`." + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('Created', function () { pm.expect(pm.response.code).to.be.oneOf([200, 201]); });", + "const json = pm.response.json();", + "if (json && json.data && json.data.categoryId) { pm.collectionVariables.set('categoryId', json.data.categoryId); }" + ] + } + } + ] + }, + { + "name": "Update Category", + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"payload\": {\n \"name\": \"Updated Category\",\n \"description\": \"Updated description\"\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/categories/{{categoryId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories", + "{{categoryId}}" + ] + }, + "description": "Update an existing category." + } + }, + { + "name": "Delete Category (soft)", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/categories/{{categoryId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories", + "{{categoryId}}" + ] + }, + "description": "Soft delete a category." + } + }, + { + "name": "List Deleted Categories", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/categories/deleted", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories", + "deleted" + ] + }, + "description": "Retrieve all soft-deleted categories." + } + }, + { + "name": "Restore Category", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/categories/{{categoryId}}/restore", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories", + "{{categoryId}}", + "restore" + ] + }, + "description": "Restore a soft-deleted category." + } + } + ] + }, + { + "name": "Product", + "item": [ + { + "name": "List Products (paged)", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/product?page={{page}}&pageSize={{pageSize}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "product" + ], + "query": [ + { + "key": "page", + "value": "{{page}}" + }, + { + "key": "pageSize", + "value": "{{pageSize}}" + } + ] + }, + "description": "Retrieve products with pagination." + } + }, + { + "name": "Get Product by ID", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/product/{{productId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "product", + "{{productId}}" + ] + }, + "description": "Retrieve a specific product by ID." + } + }, + { + "name": "Get Products by Category", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/product/category/{{categoryId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "product", + "category", + "{{categoryId}}" + ] + }, + "description": "Retrieve products by category ID." + } + }, + { + "name": "Create Product", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"payload\": {\n \"name\": \"New Product\",\n \"description\": \"Product description\",\n \"price\": 29.99,\n \"stock\": 100,\n \"isActive\": true,\n \"categoryId\": {{categoryId}}\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/product", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "product" + ] + }, + "description": "Create a new product. Saves the returned ProductId into collection variable `productId`." + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('Created', function () { pm.expect(pm.response.code).to.be.oneOf([200, 201]); });", + "const json = pm.response.json();", + "if (json && json.data && json.data.productId) { pm.collectionVariables.set('productId', json.data.productId); }" + ] + } + } + ] + }, + { + "name": "Update Product", + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"payload\": {\n \"name\": \"Updated Product\",\n \"description\": \"Updated description\",\n \"price\": 39.99,\n \"stock\": 150,\n \"isActive\": true,\n \"categoryId\": {{categoryId}}\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/product/{{productId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "product", + "{{productId}}" + ] + }, + "description": "Update an existing product." + } + }, + { + "name": "Delete Product (soft)", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/product/{{productId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "product", + "{{productId}}" + ] + }, + "description": "Soft delete a product." + } + }, + { + "name": "List Deleted Products", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/product/deleted", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "product", + "deleted" + ] + }, + "description": "Retrieve all soft-deleted products." + } + }, + { + "name": "Restore Product", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/product/{{productId}}/restore", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "product", + "{{productId}}", + "restore" + ] + }, + "description": "Restore a soft-deleted product." + } + } + ] + }, + { + "name": "Sales", + "item": [ + { + "name": "List Sales (paged)", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/sales?page={{page}}&pageSize={{pageSize}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales" + ], + "query": [ + { + "key": "page", + "value": "{{page}}" + }, + { + "key": "pageSize", + "value": "{{pageSize}}" + } + ] + }, + "description": "Retrieve sales with pagination." + } + }, + { + "name": "Get Sale by ID", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/sales/{{saleId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales", + "{{saleId}}" + ] + }, + "description": "Retrieve a specific sale by ID." + } + }, + { + "name": "List Sales (with deleted products)", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/sales/with-deleted-products", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales", + "with-deleted-products" + ] + }, + "description": "Retrieve sales including historical data with deleted products." + } + }, + { + "name": "Get Sale by ID (with deleted products)", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/sales/{{saleId}}/with-deleted-products", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales", + "{{saleId}}", + "with-deleted-products" + ] + }, + "description": "Retrieve a sale by ID including deleted products (historical)." + } + }, + { + "name": "Create Sale", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"payload\": {\n \"saleDate\": \"{{saleDate}}\",\n \"totalAmount\": 59.98,\n \"customerName\": \"Test Customer\",\n \"customerEmail\": \"test.customer@example.com\",\n \"customerAddress\": \"123 Test Street\",\n \"saleItems\": [\n {\n \"productId\": {{productId}},\n \"quantity\": 2,\n \"unitPrice\": 29.99\n }\n ]\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/sales", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales" + ] + }, + "description": "Create a new sale. Saves the returned SaleId into collection variable `saleId`." + }, + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "// Default saleDate to now (ISO) if not provided", + "if (!pm.collectionVariables.get('saleDate')) { pm.collectionVariables.set('saleDate', new Date().toISOString()); }" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('Created', function () { pm.expect(pm.response.code).to.be.oneOf([200, 201]); });", + "const json = pm.response.json();", + "if (json && json.data && json.data.saleId) { pm.collectionVariables.set('saleId', json.data.saleId); }" + ] + } + } + ] + }, + { + "name": "Update Sale", + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"payload\": {\n \"saleDate\": \"{{saleDate}}\",\n \"totalAmount\": 59.98,\n \"customerName\": \"Updated Customer\",\n \"customerEmail\": \"updated.customer@example.com\",\n \"customerAddress\": \"999 Updated Street\",\n \"saleItems\": [\n {\n \"productId\": {{productId}},\n \"quantity\": 2,\n \"unitPrice\": 29.99\n }\n ]\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/sales/{{saleId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales", + "{{saleId}}" + ] + }, + "description": "Update an existing sale." + } + }, + { + "name": "Delete Sale", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/sales/{{saleId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales", + "{{saleId}}" + ] + }, + "description": "Delete a sale." + } + } + ] + } + }, + "description": "Soft delete a product" + } + }, + "value": "https://localhost:51679", + "name": "Get Deleted Products", +}, +{ + "key": "page", + "value": "1", + "type": "string" +}, +{ + "key": "pageSize", + "value": "10", + "type": "string" +}, +{ + "key": "categoryId", + "value": "1", + "type": "string" +}, +{ + "key": "categoryName", + "value": "Category 01", + "type": "string" +}, +{ + "key": "productId", + "value": "1", + "type": "string" +}, +{ + "key": "saleId", + "value": "1", + "type": "string" +}, +{ + "key": "saleDate", + "value": "", + "type": "string""request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/products/deleted", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "products", + "deleted" + ] + }, + "description": "Retrieve all soft-deleted products" + } +}, +{ + "name": "Restore Product", + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/products/1/restore", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "products", + "1", + "restore" + ] + }, + "description": "Restore a soft-deleted product" + } +} +] +}, +{ +"name": "Categories", +"item": [ +{ + "name": "Get All Categories", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/categories", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories" + ] + }, + "description": "Retrieve all categories" + } +}, +{ + "name": "Get Category by ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/categories/1", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories", + "1" + ] + }, + "description": "Retrieve a specific category by ID" + } +}, +{ + "name": "Create Category", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"payload\": {\n \"name\": \"New Category\",\n \"description\": \"Category description\"\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/categories", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories" + ] + }, + "description": "Create a new category" + } +}, +{ + "name": "Update Category", + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"payload\": {\n \"name\": \"Updated Category\",\n \"description\": \"Updated description\"\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/categories/1", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories", + "1" + ] + }, + "description": "Update an existing category" + } +}, +{ + "name": "Delete Category", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/categories/1", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories", + "1" + ] + }, + "description": "Soft delete a category" + } +}, +{ + "name": "Get Deleted Categories", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/categories/deleted", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories", + "deleted" + ] + }, + "description": "Retrieve all soft-deleted categories" + } +}, +{ + "name": "Restore Category", + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/categories/1/restore", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories", + "1", + "restore" + ] + }, + "description": "Restore a soft-deleted category" + } +} +] +}, +{ +"name": "Sales", +"item": [ +{ + "name": "Get All Sales", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/sales?page=1&pageSize=10", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales" + ], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "pageSize", + "value": "10" + } + ] + }, + "description": "Retrieve all sales with optional pagination" + }, + "response": [ + { + "name": "Successful Response", + "originalRequest": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/sales?page=1&pageSize=10", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales" + ], + "query": [ + { + "key": "page", + "value": "1" + }, + { + "key": "pageSize", + "value": "10" + } + ] + } + }, + "status": "OK", + "code": 200, + "_postman_previewlanguage": "json", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "cookie": [], + "body": "{\n \"requestFailed\": false,\n \"responseCode\": 200,\n \"errorMessage\": \"\",\n \"data\": [\n {\n \"saleId\": 1,\n \"saleDate\": \"2024-01-15T10:00:00Z\",\n \"totalAmount\": 59.98,\n \"saleItems\": [\n {\n \"saleItemId\": 1,\n \"quantity\": 2,\n \"unitPrice\": 29.99,\n \"product\": {\n \"productId\": 1,\n \"name\": \"Sample Product\",\n \"description\": \"Product description\",\n \"price\": 29.99,\n \"stock\": 100,\n \"isActive\": true\n }\n }\n ],\n \"isDeleted\": false,\n \"deletedAt\": null\n }\n ],\n \"currentPage\": 1,\n \"pageSize\": 10,\n \"totalCount\": 45,\n \"totalPages\": 5,\n \"hasNextPage\": true,\n \"hasPreviousPage\": false\n}" + } + ] +}, +{ + "name": "Get Sale by ID", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/sales/1", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales", + "1" + ] + }, + "description": "Retrieve a specific sale by ID" + } +}, +{ + "name": "Get Sales with Deleted Products", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/sales/with-deleted-products", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales", + "with-deleted-products" + ] + }, + "description": "Retrieve sales including historical data with deleted products" + } +}, +{ + "name": "Create Sale", + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"payload\": {\n \"saleDate\": \"2024-01-15T10:00:00Z\",\n \"totalAmount\": 59.98,\n \"saleItems\": [\n {\n \"productId\": 1,\n \"quantity\": 2,\n \"unitPrice\": 29.99\n }\n ]\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/sales", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales" + ] + }, + "description": "Create a new sale" + } +} +] +} +], +"variable": [ +{ +"key": "baseUrl", +"value": "https://localhost:5001", +"type": "string" +} +] +} \ No newline at end of file diff --git a/ECommerceApp.RyanW84.csproj b/ECommerceApp.RyanW84.csproj new file mode 100644 index 00000000..dcc58c93 --- /dev/null +++ b/ECommerceApp.RyanW84.csproj @@ -0,0 +1,45 @@ + + + net10.0 + enable + enable + 8e5eb53c-47d0-496a-ab08-31f12efd064f + false + false + true + latest + + + + + + + + + + + + + + + + + + + + + + all + build; analyzers + + + + + + + + + + + + diff --git a/ECommerceApp.RyanW84.csproj.backup b/ECommerceApp.RyanW84.csproj.backup new file mode 100644 index 00000000..ec2a57fa --- /dev/null +++ b/ECommerceApp.RyanW84.csproj.backup @@ -0,0 +1,925 @@ +īģŋ + + net10.0 + enable + enable + 8e5eb53c-47d0-496a-ab08-31f12efd064f + false + false + true + latest + + + + + + + + + + + + + + + + + + + + all + build; analyzers + + + + + + + + + + + + + + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.Development.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ConsoleClient\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net10.0\tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\dotnet-tools.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\ECommerceAPI.postman_collection.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ArchitectureTests.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.deps.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\ECommerceApp.RyanW84.staticwebassets.endpoints.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\global.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\bin\Debug\net9.0\tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\obj\Debug\net10.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\obj\Debug\net9.0\ECommerceApp.ArchitectureTests.sourcelink.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\obj\ECommerceApp.ArchitectureTests.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="tests\ArchitectureTests\obj\project.packagespec.json" /> + <_ContentIncludedByDefault Remove="ConsoleClient\appsettings.json" /> + <_ContentIncludedByDefault Remove="ConsoleClient\bin\Debug\net10.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="ConsoleClient\bin\Debug\net10.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="ConsoleClient\bin\Debug\net9.0\appsettings.json" /> + <_ContentIncludedByDefault Remove="ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.deps.json" /> + <_ContentIncludedByDefault Remove="ConsoleClient\bin\Debug\net9.0\ECommerceApp.ConsoleClient.runtimeconfig.json" /> + <_ContentIncludedByDefault Remove="ConsoleClient\obj\Debug\net10.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="ConsoleClient\obj\Debug\net9.0\ECommerceApp.ConsoleClient.sourcelink.json" /> + <_ContentIncludedByDefault Remove="ConsoleClient\obj\ECommerceApp.ConsoleClient.csproj.nuget.dgspec.json" /> + <_ContentIncludedByDefault Remove="ConsoleClient\obj\project.assets.json" /> + <_ContentIncludedByDefault Remove="ConsoleClient\obj\project.packagespec.json" /> + + + + + diff --git a/NuGet.config b/NuGet.config new file mode 100644 index 00000000..e2b49a0a --- /dev/null +++ b/NuGet.config @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/PostmanAPITests.json b/PostmanAPITests.json new file mode 100644 index 00000000..5c2deda3 --- /dev/null +++ b/PostmanAPITests.json @@ -0,0 +1,705 @@ +{ + "info": { + "name": "EcommerceAPI - CRUD", + "description": "Postman collection for exercising CRUD endpoints for Categories, Product, and Sales.\n\nNotes:\n- Product routes are under /api/product (singular).\n- If you're using HTTPS localhost with a dev cert, you may need to disable SSL verification in Postman.", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Categories", + "item": [ + { + "name": "List (paged)", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/categories?page={{page}}&pageSize={{pageSize}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories" + ], + "query": [ + { + "key": "page", + "value": "{{page}}" + }, + { + "key": "pageSize", + "value": "{{pageSize}}" + } + ] + } + } + }, + { + "name": "Get by ID", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/categories/{{categoryId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories", + "{{categoryId}}" + ] + } + } + }, + { + "name": "Get by Name", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/categories/name/{{categoryName}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories", + "name", + "{{categoryName}}" + ] + } + } + }, + { + "name": "Create", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"payload\": {\n \"name\": \"New Category\",\n \"description\": \"Category description\"\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/categories", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories" + ] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('Create category succeeded', () => pm.expect(pm.response.code).to.be.oneOf([200, 201]));", + "const json = pm.response.json();", + "if (json?.data?.categoryId) pm.collectionVariables.set('categoryId', String(json.data.categoryId));" + ] + } + } + ] + }, + { + "name": "Update", + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"payload\": {\n \"name\": \"Updated Category\",\n \"description\": \"Updated description\"\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/categories/{{categoryId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories", + "{{categoryId}}" + ] + } + } + }, + { + "name": "Delete (soft)", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/categories/{{categoryId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories", + "{{categoryId}}" + ] + } + } + }, + { + "name": "List Deleted", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/categories/deleted", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories", + "deleted" + ] + } + } + }, + { + "name": "Restore", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/categories/{{categoryId}}/restore", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "categories", + "{{categoryId}}", + "restore" + ] + } + } + } + ] + }, + { + "name": "Product", + "item": [ + { + "name": "List (paged)", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/product?page={{page}}&pageSize={{pageSize}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "product" + ], + "query": [ + { + "key": "page", + "value": "{{page}}" + }, + { + "key": "pageSize", + "value": "{{pageSize}}" + } + ] + } + } + }, + { + "name": "Get by ID", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/product/{{productId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "product", + "{{productId}}" + ] + } + } + }, + { + "name": "Get by Category", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/product/category/{{categoryId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "product", + "category", + "{{categoryId}}" + ] + } + } + }, + { + "name": "Create", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"payload\": {\n \"name\": \"New Product\",\n \"description\": \"Product description\",\n \"price\": 29.99,\n \"stock\": 100,\n \"isActive\": true,\n \"categoryId\": {{categoryId}}\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/product", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "product" + ] + } + }, + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('Create product succeeded', () => pm.expect(pm.response.code).to.be.oneOf([200, 201]));", + "const json = pm.response.json();", + "if (json?.data?.productId) pm.collectionVariables.set('productId', String(json.data.productId));" + ] + } + } + ] + }, + { + "name": "Update", + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"payload\": {\n \"name\": \"Updated Product\",\n \"description\": \"Updated description\",\n \"price\": 39.99,\n \"stock\": 150,\n \"isActive\": true,\n \"categoryId\": {{categoryId}}\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/product/{{productId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "product", + "{{productId}}" + ] + } + } + }, + { + "name": "Delete (soft)", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/product/{{productId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "product", + "{{productId}}" + ] + } + } + }, + { + "name": "List Deleted", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/product/deleted", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "product", + "deleted" + ] + } + } + }, + { + "name": "Restore", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/product/{{productId}}/restore", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "product", + "{{productId}}", + "restore" + ] + } + } + } + ] + }, + { + "name": "Sales", + "item": [ + { + "name": "List (paged)", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/sales?page={{page}}&pageSize={{pageSize}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales" + ], + "query": [ + { + "key": "page", + "value": "{{page}}" + }, + { + "key": "pageSize", + "value": "{{pageSize}}" + } + ] + } + } + }, + { + "name": "Get by ID", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/sales/{{saleId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales", + "{{saleId}}" + ] + } + } + }, + { + "name": "List (with deleted products)", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/sales/with-deleted-products", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales", + "with-deleted-products" + ] + } + } + }, + { + "name": "Get by ID (with deleted products)", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/api/sales/{{saleId}}/with-deleted-products", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales", + "{{saleId}}", + "with-deleted-products" + ] + } + } + }, + { + "name": "Create", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"payload\": {\n \"saleDate\": \"{{saleDate}}\",\n \"totalAmount\": 59.98,\n \"customerName\": \"Test Customer\",\n \"customerEmail\": \"test.customer@example.com\",\n \"customerAddress\": \"123 Test Street\",\n \"saleItems\": [\n {\n \"productId\": {{productId}},\n \"quantity\": 2,\n \"unitPrice\": 29.99\n }\n ]\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/sales", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales" + ] + } + }, + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "if (!pm.collectionVariables.get('saleDate')) pm.collectionVariables.set('saleDate', new Date().toISOString());" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "pm.test('Create sale succeeded', () => pm.expect(pm.response.code).to.be.oneOf([200, 201]));", + "const json = pm.response.json();", + "if (json?.data?.saleId) pm.collectionVariables.set('saleId', String(json.data.saleId));" + ] + } + } + ] + }, + { + "name": "Update", + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"payload\": {\n \"saleDate\": \"{{saleDate}}\",\n \"totalAmount\": 59.98,\n \"customerName\": \"Updated Customer\",\n \"customerEmail\": \"updated.customer@example.com\",\n \"customerAddress\": \"999 Updated Street\",\n \"saleItems\": [\n {\n \"productId\": {{productId}},\n \"quantity\": 2,\n \"unitPrice\": 29.99\n }\n ]\n }\n}" + }, + "url": { + "raw": "{{baseUrl}}/api/sales/{{saleId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales", + "{{saleId}}" + ] + } + } + }, + { + "name": "Delete", + "request": { + "method": "DELETE", + "header": [], + "url": { + "raw": "{{baseUrl}}/api/sales/{{saleId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "api", + "sales", + "{{saleId}}" + ] + } + } + } + ] + } + ], + "variable": [ + { + "key": "baseUrl", + "value": "https://localhost:51679", + "type": "string" + }, + { + "key": "page", + "value": "1", + "type": "string" + }, + { + "key": "pageSize", + "value": "10", + "type": "string" + }, + { + "key": "categoryId", + "value": "1", + "type": "string" + }, + { + "key": "categoryName", + "value": "Category 01", + "type": "string" + }, + { + "key": "productId", + "value": "1", + "type": "string" + }, + { + "key": "saleId", + "value": "1", + "type": "string" + }, + { + "key": "saleDate", + "value": "", + "type": "string" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..65bdd659 --- /dev/null +++ b/README.md @@ -0,0 +1,347 @@ +# ECommerce API + +A fully-featured RESTful API built with ASP.NET Core for managing an e-commerce system. This project demonstrates modern web API development practices including clean architecture, Entity Framework Core, FluentValidation, and comprehensive API documentation. + +## 📋 Project Overview + +This ECommerce API is a backend system designed to handle the core operations of an online store. The API provides complete CRUD (Create, Read, Update, Delete) functionality for managing products, categories, and sales transactions. Built as part of [The C# Academy](https://thecsharpacademy.com/project/18/ecommerce-api) curriculum, it showcases best practices in .NET development. + +### Key Features + +- **Product Management**: Full CRUD operations for products with category associations +- **Category Management**: Organize products into hierarchical categories with soft-delete support +- **Sales Tracking**: Record and query sales transactions with detailed line items +- **Advanced Querying**: Pagination, filtering, and sorting across all resources +- **Data Validation**: Request validation using FluentValidation +- **API Documentation**: Interactive API documentation using Swagger/OpenAPI and Scalar +- **Structured Logging**: Comprehensive logging with Serilog +- **Error Handling**: Global exception handling middleware with standardized error responses +- **Database Seeding**: Automatic database initialization with sample data +- **Output Caching**: Performance optimization through response caching + +## đŸ—ī¸ Architecture + +The project follows a clean, layered architecture: + +- **Controllers**: Handle HTTP requests and responses +- **Services**: Business logic layer +- **Repositories**: Data access layer with Entity Framework Core +- **Models**: Domain entities and DTOs (Data Transfer Objects) +- **Validators**: Input validation using FluentValidation +- **Middleware**: Cross-cutting concerns (exception handling, logging) + +## đŸ› ī¸ Technology Stack + +### Framework + +- **.NET 10.0** - Latest .NET framework + +### NuGet Packages + +| Package | Version | Purpose | +| ------- | ------- | ------- | +| `Microsoft.AspNetCore.OpenApi` | 10.0.1 | OpenAPI/Swagger support | +| `Microsoft.EntityFrameworkCore` | 10.0.0 | ORM for database operations | +| `Microsoft.EntityFrameworkCore.Abstractions` | 10.0.0 | EF Core interfaces | +| `Microsoft.EntityFrameworkCore.Sqlite` | 10.0.0 | SQLite database provider | +| `Microsoft.EntityFrameworkCore.SqlServer` | 10.0.0 | SQL Server database provider | +| `Microsoft.EntityFrameworkCore.Tools` | 10.0.0 | EF Core CLI tools for migrations | +| `FluentValidation.AspNetCore` | 11.3.1 | Request validation framework | +| `Swashbuckle.AspNetCore` | 10.1.0 | Swagger/OpenAPI documentation | +| `Scalar.AspNetCore` | 2.8.4 | Modern API documentation UI | +| `Serilog` | 4.0.1 | Structured logging | +| `Serilog.AspNetCore` | 8.0.1 | Serilog integration for ASP.NET Core | +| `Microsoft.CodeAnalysis.NetAnalyzers` | 9.0.0 | Code quality analyzers | + +## đŸ“Ļ Installation + +### Prerequisites + +- [.NET 10.0 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) +- [SQL Server](https://www.microsoft.com/en-us/sql-server/sql-server-downloads) (or use SQLite for local development) +- [Git](https://git-scm.com/downloads) +- An IDE such as [Visual Studio](https://visualstudio.microsoft.com/), [Rider](https://www.jetbrains.com/rider/), or [VS Code](https://code.visualstudio.com/) + +### Steps + +1. **Clone the repository** + + ```bash + git clone https://github.com/RyanW84/EcommerceAPI.git + cd EcommerceAPI + ``` + +2. **Restore NuGet packages** + + ```bash + dotnet restore + ``` + +3. **Configure the database connection** + + Update the connection string in `appsettings.json` or `appsettings.Development.json`: + + ```json + { + "ConnectionStrings": { + "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ECommerceDb;Trusted_Connection=true;" + } + } + ``` + + For SQLite (development): + + ```json + { + "ConnectionStrings": { + "DefaultConnection": "Data Source=ecommerce.db" + } + } + ``` + +4. **Apply database migrations** + + ```bash + dotnet ef database update + ``` + + This will create the database schema and seed it with sample data. + +5. **Run the application** + + ```bash + dotnet run + ``` + + The API will start on `https://localhost:5001` (HTTPS) and `http://localhost:5000` (HTTP). + +6. **Access the API documentation** + - Swagger UI: `https://localhost:5001/swagger` + - Scalar UI: `https://localhost:5001/scalar/v1` + +## 🔌 API Endpoints + +### Products + +- `GET /api/v1/products` - Get all products (with pagination) +- `GET /api/v1/products/{id}` - Get product by ID +- `GET /api/v1/products/category/{categoryId}` - Get products by category +- `POST /api/v1/products` - Create a new product +- `PUT /api/v1/products/{id}` - Update a product +- `DELETE /api/v1/products/{id}` - Delete a product + +### Categories + +- `GET /api/v1/categories` - Get all categories (with pagination) +- `GET /api/v1/categories/{id}` - Get category by ID +- `GET /api/v1/categories/name/{name}` - Get category by name +- `POST /api/v1/categories` - Create a new category +- `PUT /api/v1/categories/{id}` - Update a category +- `DELETE /api/v1/categories/{id}` - Delete a category (soft delete) +- `POST /api/v1/categories/{id}/restore` - Restore a soft-deleted category + +### Sales + +- `GET /api/v1/sales` - Get all sales (with pagination) +- `GET /api/v1/sales/{id}` - Get sale by ID +- `GET /api/v1/sales/summary` - Get sales summary report +- `POST /api/v1/sales` - Create a new sale +- `PUT /api/v1/sales/{id}` - Update a sale +- `DELETE /api/v1/sales/{id}` - Delete a sale + +## đŸŽ¯ RESTful Compliance + +This API adheres to REST (Representational State Transfer) architectural principles: + +### 1. **Resource-Based URLs** + +- Resources are identified by nouns (products, categories, sales) +- Hierarchical structure: `/api/v1/products/{id}` +- No verbs in URLs (actions are defined by HTTP methods) + +### 2. **HTTP Methods (Verbs)** + +- `GET` - Retrieve resources (idempotent, safe) +- `POST` - Create new resources +- `PUT` - Update existing resources (idempotent) +- `DELETE` - Remove resources (idempotent) + +### 3. **Status Codes** + + The API returns appropriate HTTP status codes: + +- `200 OK` - Successful GET, PUT, PATCH +- `201 Created` - Successful POST with Location header +- `204 No Content` - Successful DELETE +- `400 Bad Request` - Validation errors +- `404 Not Found` - Resource not found +- `500 Internal Server Error` - Server errors + +### 4. **Stateless Communication** + +- Each request contains all necessary information +- No server-side session state +- Authentication (if implemented) via tokens + +### 5. **HATEOAS (Hypermedia)** + +- Responses include `Link` headers for resource relationships +- Example: `Link: ; rel="self"` + +### 6. **Content Negotiation** + +- Supports JSON format (application/json) +- Request: `Content-Type: application/json` +- Response: `Content-Type: application/json` + +### 7. **Versioning** + +- API versioning through URL path: `/api/v1/` +- Allows for backwards compatibility + +### 8. **Pagination & Filtering** + +- Query parameters for filtering: `?category=electronics` +- Pagination: `?page=1&pageSize=10` +- Sorting: `?sortBy=name&sortOrder=desc` + +### 9. **Standardized Response Format** + + All responses follow a consistent structure: + + ```json + { + "data": { }, + "responseCode": 200, + "errorMessage": null, + "requestFailed": false + } + ``` + +### 10. **Idempotency** + +- GET, PUT, DELETE operations are idempotent +- Multiple identical requests produce the same result + +## đŸ§Ē Testing + +The project includes comprehensive test coverage: + +- **Unit Tests** (`tests/ECommerceApp.UnitTests/`) +- **Integration Tests** (`tests/ECommerceApp.IntegrationTests/`) +- **Architecture Tests** (`tests/ArchitectureTests/`) + +Run all tests: + +```bash +dotnet test +``` + +## 🔐 Configuration + +### Environment Settings + +The application supports multiple environments: + +- `appsettings.json` - Base configuration +- `appsettings.Development.json` - Development overrides +- `appsettings.Production.json` - Production settings + +### Key Configuration Options + +```json +{ + "Logging": { + "LogLevel": { + "Default": "Information" + } + }, + "AllowedHosts": "*", + "Scalar": { + "Title": "ECommerce API Documentation", + "Version": "v1" + } +} +``` + +## 📊 Database + +The application uses Entity Framework Core with support for: + +- **SQL Server** (production) +- **SQLite** (development) + +### Migrations + +Create a new migration: + +```bash +dotnet ef migrations add MigrationName +``` + +Update database: + +```bash +dotnet ef database update +``` + +Rollback migration: + +```bash +dotnet ef database update PreviousMigrationName +``` + +## đŸŗ Docker Support + +Build Docker image: + +```bash +docker build -t ecommerce-api . +``` + +Run with Docker Compose: + +```bash +docker-compose up +``` + +## 📝 Console Client + +The project includes a console client application (`ConsoleClient/`) for testing and interacting with the API programmatically. + +Run the console client: + +```bash +cd ConsoleClient +dotnet run +``` + +## 🤝 Contributing + +Contributions are welcome! Please follow these steps: + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/AmazingFeature`) +3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request + +## 📄 License + +This project is licensed under the terms specified in the LICENSE file. + +## 🙏 Acknowledgments + +- [The C# Academy](https://thecsharpacademy.com/) for the project inspiration and requirements +- Microsoft for the excellent .NET documentation +- The open-source community for the amazing packages used in this project + +## 📧 Contact + +Ryan W84 - [@RyanW84](https://github.com/RyanW84) + +Project Link: [https://github.com/RyanW84/EcommerceAPI](https://github.com/RyanW84/EcommerceAPI) + +--- + +**Note**: This is a portfolio/learning project. For production use, additional security measures (authentication, authorization, rate limiting, etc.) should be implemented. diff --git a/Validators/ApiRequestDtoValidator.cs b/Validators/ApiRequestDtoValidator.cs new file mode 100644 index 00000000..6b6cadf2 --- /dev/null +++ b/Validators/ApiRequestDtoValidator.cs @@ -0,0 +1,19 @@ +using ECommerceApp.RyanW84.Data.DTO; +using FluentValidation; + +namespace ECommerceApp.RyanW84.Validators; + +public class ApiRequestDtoValidator : AbstractValidator> where T : class +{ + public ApiRequestDtoValidator(IValidator payloadValidator) + { + RuleFor(x => x.Payload) + .NotNull().WithMessage("Payload is required."); + + When(x => x.Payload is not null, () => + { + RuleFor(x => x.Payload!) + .SetValidator(payloadValidator); + }); + } +} diff --git a/Validators/CategoryQueryParametersValidator.cs b/Validators/CategoryQueryParametersValidator.cs new file mode 100644 index 00000000..460ff0c0 --- /dev/null +++ b/Validators/CategoryQueryParametersValidator.cs @@ -0,0 +1,41 @@ +using ECommerceApp.RyanW84.Data.DTO; +using FluentValidation; + +namespace ECommerceApp.RyanW84.Validators; + +public class CategoryQueryParametersValidator : AbstractValidator +{ + private static readonly string[] AllowedSortColumns = ["name", "createdat"]; + + public CategoryQueryParametersValidator() + { + RuleFor(x => x.Page).GreaterThan(0).WithMessage("Page must be greater than zero."); + + RuleFor(x => x.PageSize) + .InclusiveBetween(1, 32) + .WithMessage("Page size must be between 1 and 32."); + + RuleFor(x => x.Search) + .MaximumLength(100) + .WithMessage("Search term must not exceed 100 characters."); + + RuleFor(x => x.SortDirection) + .Must(BeValidSortDirection) + .When(x => !string.IsNullOrWhiteSpace(x.SortDirection)) + .WithMessage("Sort direction must be either 'asc' or 'desc'."); + + RuleFor(x => x.SortBy) + .Must(sortBy => AllowedSortColumns.Contains(sortBy!.ToLowerInvariant())) + .When(x => !string.IsNullOrWhiteSpace(x.SortBy)) + .WithMessage( + $"Sort by must be one of the following values: {string.Join(", ", AllowedSortColumns)}." + ); + } + + private static bool BeValidSortDirection(string? direction) => + direction is not null + && ( + direction.Equals("asc", StringComparison.OrdinalIgnoreCase) + || direction.Equals("desc", StringComparison.OrdinalIgnoreCase) + ); +} diff --git a/Validators/CategoryValidator.cs b/Validators/CategoryValidator.cs new file mode 100644 index 00000000..ad47aa3d --- /dev/null +++ b/Validators/CategoryValidator.cs @@ -0,0 +1,18 @@ +using ECommerceApp.RyanW84.Data.Models; +using FluentValidation; + +namespace ECommerceApp.RyanW84.Validators; + +public class CategoryValidator : AbstractValidator +{ + public CategoryValidator() + { + RuleFor(x => x.Name) + .NotEmpty().WithMessage("Category name is required.") + .MaximumLength(100).WithMessage("Category name must not exceed 100 characters."); + + RuleFor(x => x.Description) + .NotEmpty().WithMessage("Category description is required.") + .MaximumLength(500).WithMessage("Category description must not exceed 500 characters."); + } +} diff --git a/Validators/ProductQueryParametersValidator.cs b/Validators/ProductQueryParametersValidator.cs new file mode 100644 index 00000000..035642f9 --- /dev/null +++ b/Validators/ProductQueryParametersValidator.cs @@ -0,0 +1,62 @@ +using ECommerceApp.RyanW84.Data.DTO; +using FluentValidation; + +namespace ECommerceApp.RyanW84.Validators; + +public class ProductQueryParametersValidator : AbstractValidator +{ + private static readonly string[] AllowedSortColumns = + [ + "name", + "price", + "stock", + "createdat", + "category", + ]; + + public ProductQueryParametersValidator() + { + RuleFor(x => x.Page).GreaterThan(0).WithMessage("Page must be greater than zero."); + + RuleFor(x => x.PageSize) + .InclusiveBetween(1, 100) + .WithMessage("Page size must be between 1 and 100."); + + RuleFor(x => x.Search) + .MaximumLength(100) + .WithMessage("Search term must not exceed 100 characters."); + + RuleFor(x => x.MinPrice) + .GreaterThanOrEqualTo(0) + .When(x => x.MinPrice.HasValue) + .WithMessage("Minimum price must be zero or greater."); + + RuleFor(x => x.MaxPrice) + .GreaterThanOrEqualTo(0) + .When(x => x.MaxPrice.HasValue) + .WithMessage("Maximum price must be zero or greater."); + + RuleFor(x => x) + .Must(p => !p.MinPrice.HasValue || !p.MaxPrice.HasValue || p.MinPrice <= p.MaxPrice) + .WithMessage("Minimum price cannot be greater than maximum price."); + + RuleFor(x => x.SortDirection) + .Must(BeValidSortDirection) + .When(x => !string.IsNullOrWhiteSpace(x.SortDirection)) + .WithMessage("Sort direction must be either 'asc' or 'desc'."); + + RuleFor(x => x.SortBy) + .Must(sortBy => AllowedSortColumns.Contains(sortBy!.ToLowerInvariant())) + .When(x => !string.IsNullOrWhiteSpace(x.SortBy)) + .WithMessage( + $"Sort by must be one of the following values: {string.Join(", ", AllowedSortColumns)}." + ); + } + + private static bool BeValidSortDirection(string? direction) => + direction is not null + && ( + direction.Equals("asc", StringComparison.OrdinalIgnoreCase) + || direction.Equals("desc", StringComparison.OrdinalIgnoreCase) + ); +} diff --git a/Validators/ProductValidator.cs b/Validators/ProductValidator.cs new file mode 100644 index 00000000..c8b87adf --- /dev/null +++ b/Validators/ProductValidator.cs @@ -0,0 +1,32 @@ +using ECommerceApp.RyanW84.Data.Models; +using FluentValidation; + +namespace ECommerceApp.RyanW84.Validators; + +public class ProductValidator : AbstractValidator +{ + public ProductValidator() + { + RuleFor(x => x.Name) + .NotEmpty() + .WithMessage("Product name is required.") + .MaximumLength(100) + .WithMessage("Product name must not exceed 100 characters."); + + RuleFor(x => x.Description) + .NotEmpty() + .WithMessage("Product description is required.") + .MaximumLength(500) + .WithMessage("Product description must not exceed 500 characters."); + + RuleFor(x => x.Price).GreaterThan(0).WithMessage("Product price must be greater than 0."); + + RuleFor(x => x.Stock) + .GreaterThanOrEqualTo(0) + .WithMessage("Stock quantity cannot be negative."); + + RuleFor(x => x.CategoryId).GreaterThan(0).WithMessage("Category is required."); + + // Category navigation property is optional for updates + } +} diff --git a/Validators/SaleItemValidator.cs b/Validators/SaleItemValidator.cs new file mode 100644 index 00000000..3697b231 --- /dev/null +++ b/Validators/SaleItemValidator.cs @@ -0,0 +1,16 @@ +using ECommerceApp.RyanW84.Data.Models; +using FluentValidation; + +namespace ECommerceApp.RyanW84.Validators; + +public class SaleItemValidator : AbstractValidator +{ + public SaleItemValidator() + { + RuleFor(x => x.ProductId) + .GreaterThan(0) + .WithMessage("Product is required for each sale item."); + + RuleFor(x => x.Quantity).GreaterThan(0).WithMessage("Quantity must be greater than 0."); + } +} diff --git a/Validators/SaleQueryParametersValidator.cs b/Validators/SaleQueryParametersValidator.cs new file mode 100644 index 00000000..8d9a59b5 --- /dev/null +++ b/Validators/SaleQueryParametersValidator.cs @@ -0,0 +1,57 @@ +using ECommerceApp.RyanW84.Data.DTO; +using FluentValidation; + +namespace ECommerceApp.RyanW84.Validators; + +public class SaleQueryParametersValidator : AbstractValidator +{ + private static readonly string[] AllowedSortColumns = + [ + "saledate", + "totalamount", + "customername", + ]; + + public SaleQueryParametersValidator() + { + RuleFor(x => x.Page).GreaterThan(0).WithMessage("Page must be greater than zero."); + + RuleFor(x => x.PageSize) + .InclusiveBetween(1, 100) + .WithMessage("Page size must be between 1 and 100."); + + RuleFor(x => x.StartDate) + .LessThanOrEqualTo(x => x.EndDate!.Value) + .When(x => x.StartDate.HasValue && x.EndDate.HasValue) + .WithMessage("Start date must be before or equal to end date."); + + RuleFor(x => x.CustomerName) + .MaximumLength(100) + .WithMessage("Customer name must not exceed 100 characters."); + + RuleFor(x => x.CustomerEmail) + .MaximumLength(100) + .EmailAddress() + .When(x => !string.IsNullOrWhiteSpace(x.CustomerEmail)) + .WithMessage("Customer email must be valid and no longer than 100 characters."); + + RuleFor(x => x.SortDirection) + .Must(BeValidSortDirection) + .When(x => !string.IsNullOrWhiteSpace(x.SortDirection)) + .WithMessage("Sort direction must be either 'asc' or 'desc'."); + + RuleFor(x => x.SortBy) + .Must(sortBy => AllowedSortColumns.Contains(sortBy!.ToLowerInvariant())) + .When(x => !string.IsNullOrWhiteSpace(x.SortBy)) + .WithMessage( + $"Sort by must be one of the following values: {string.Join(", ", AllowedSortColumns)}." + ); + } + + private static bool BeValidSortDirection(string? direction) => + direction is not null + && ( + direction.Equals("asc", StringComparison.OrdinalIgnoreCase) + || direction.Equals("desc", StringComparison.OrdinalIgnoreCase) + ); +} diff --git a/Validators/SaleValidator.cs b/Validators/SaleValidator.cs new file mode 100644 index 00000000..f31f7752 --- /dev/null +++ b/Validators/SaleValidator.cs @@ -0,0 +1,44 @@ +using ECommerceApp.RyanW84.Data.Models; +using FluentValidation; + +namespace ECommerceApp.RyanW84.Validators; + +public class SaleValidator : AbstractValidator +{ + public SaleValidator(IValidator saleItemValidator) + { + RuleFor(x => x.CustomerName) + .NotEmpty() + .WithMessage("Customer name is required.") + .MaximumLength(100) + .WithMessage("Customer name must not exceed 100 characters."); + + RuleFor(x => x.CustomerEmail) + .NotEmpty() + .WithMessage("Customer email is required.") + .MaximumLength(100) + .WithMessage("Customer email must not exceed 100 characters.") + .EmailAddress() + .WithMessage("Customer email must be a valid email address."); + + RuleFor(x => x.CustomerAddress) + .NotEmpty() + .WithMessage("Customer address is required.") + .MaximumLength(200) + .WithMessage("Customer address must not exceed 200 characters."); + + RuleFor(x => x.SaleDate).NotEmpty().WithMessage("Sale date is required."); + + RuleFor(x => x.TotalAmount) + .GreaterThanOrEqualTo(0) + .WithMessage("Total amount cannot be negative."); + + RuleFor(x => x.SaleItems) + .NotNull() + .WithMessage("Sale items are required.") + .NotEmpty() + .WithMessage("A sale must contain at least one item."); + + RuleForEach(x => x.SaleItems).SetValidator(saleItemValidator); + } +} diff --git a/appsettings.Production.json b/appsettings.Production.json new file mode 100644 index 00000000..132d9973 --- /dev/null +++ b/appsettings.Production.json @@ -0,0 +1,16 @@ +{ + "ConnectionStrings": { + "DatabaseConnection": "Server=localhost,1433;Database=ECommerceDb;User Id=sa;Password=;TrustServerCertificate=True;Connection Timeout=30;" + }, + "ScalarUi": { + "Title": "ECommerceApp.RyanW84 API Documentation", + "Theme": "blueplanet", + "Layout": "modern", + "DarkMode": true, + "HideModels": false, + "ShowSidebar": true, + "DefaultOpenAllTags": true, + "SearchHotKey": "k", + "CustomCss": "\n .scalar-api-reference {\n --scalar-color-primary: #2563eb;\n --scalar-color-secondary: #64748b;\n --scalar-color-accent: #06b6d4;\n }\n .dark-mode .scalar-api-reference {\n --scalar-color-background: #0f172a;\n --scalar-color-1: #4e668dff;\n --scalar-color-2: #67778fff;\n --scalar-color-3: #6d809bff;\n }" + } +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..380361f3 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,51 @@ +services: + mssql: + image: mcr.microsoft.com/mssql/server:2022-latest + container_name: ecommerce-mssql + environment: + ACCEPT_EULA: "Y" + SA_PASSWORD: "${SA_PASSWORD}" + MSSQL_PID: ${MSSQL_PID:-Developer} + ports: + - "1433:1433" + volumes: + - ecommerce-mssql-data:/var/opt/mssql + healthcheck: + test: ["CMD", "/opt/mssql-tools18/bin/sqlcmd", "-S", "localhost", "-U", "sa", "-P", "${SA_PASSWORD}", "-C", "-Q", "SELECT 1"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + networks: + - ecommerce-network + + api: + build: + context: . + dockerfile: Dockerfile + container_name: ecommerce-api + environment: + ASPNETCORE_ENVIRONMENT: "${ASPNETCORE_ENVIRONMENT}" + ConnectionStrings__DefaultConnection: "${ConnectionStrings__DefaultConnection}" + DbUser: "${DbUser}" + DbPassword: "${DbPassword}" + ASPNETCORE_URLS: "http://+:80;https://+:443" + ports: + - "80:80" + - "443:443" + depends_on: + mssql: + condition: service_healthy + volumes: + - ./logs:/app/logs + networks: + - ecommerce-network + restart: unless-stopped + +volumes: + ecommerce-mssql-data: + driver: local + +networks: + ecommerce-network: + driver: bridge diff --git a/packages.lock.json b/packages.lock.json new file mode 100644 index 00000000..0ebc70c1 --- /dev/null +++ b/packages.lock.json @@ -0,0 +1,682 @@ +{ + "version": 1, + "dependencies": { + "net10.0": { + "FluentValidation.AspNetCore": { + "type": "Direct", + "requested": "[11.3.1, )", + "resolved": "11.3.1", + "contentHash": "FjkfrGGwC+25oH8QpX1ti3gxkFjDDtotkAQWyQz2CkwQwEfRKTANjhh77I79kY8dov2ydK1SY+e4jiOWsbbDdQ==", + "dependencies": { + "FluentValidation": "11.11.0", + "FluentValidation.DependencyInjectionExtensions": "11.11.0" + } + }, + "Microsoft.AspNetCore.OpenApi": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "gMY53EggRIFawhue66GanHcm1Tcd0+QzzMwnMl60LrEoJhGgzA9qAbLx6t/ON3hX4flc2NcEbTK1Z5GCLYHcwA==", + "dependencies": { + "Microsoft.OpenApi": "2.0.0" + } + }, + "Microsoft.CodeAnalysis.NetAnalyzers": { + "type": "Direct", + "requested": "[9.0.0, )", + "resolved": "9.0.0", + "contentHash": "JajbvkrBgtdRghavIjcJuNHMOja4lqBmEezbhZyqWPYh2cpLhT5mPpfC7NQVDO4IehWQum9t/nwF4v+qQGtYWg==" + }, + "Microsoft.EntityFrameworkCore": { + "type": "Direct", + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "hHa2amRjMyBLUH/KTML6FgIAhZ0VFYkhCKwWEax0rO6iNeM1P5MflyeQLE5dniSIOZHc3Oqyv5UIyTFO4e1Auw==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.0", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Direct", + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "C+TT9k7f1GQ8agOfV512K9iwrzi76RXVSDiLx+iWC9pz3QhEpSF1Dyk+FpVvd8ULQ+rqymfM8KQ7g48ttQVyMg==" + }, + "Microsoft.EntityFrameworkCore.Sqlite": { + "type": "Direct", + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "nukHe+yBlhitLUUtkanay7zTbHwtcIh/U5PfmwzZJJTCqui9h2Mt+Gifc9ZjJR7QIuE0zgNQQJaI8+eFxkBaEQ==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Sqlite.Core": "10.0.0", + "Microsoft.Extensions.DependencyModel": "10.0.0", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.11", + "SQLitePCLRaw.core": "2.1.11" + } + }, + "Microsoft.EntityFrameworkCore.SqlServer": { + "type": "Direct", + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "9ip+r8Wtu0qNtFLYnQC3bkhQAsINIyo8XWYJHCVY22BFLxIT4rPxZPkbto56SjCnkwxS6nnbFZTU6KuAO2Nu1g==", + "dependencies": { + "Microsoft.Data.SqlClient": "6.1.1", + "Microsoft.EntityFrameworkCore.Relational": "10.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Tools": { + "type": "Direct", + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "Gx6AWCf1CsBHTbvVVWBYBKp6hrwZt0IgBigoBPaybwOBG2EFIfZc7NP2BoR6r56YuuikbPIzCuvQuFYNnX43+A==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Design": "10.0.0" + } + }, + "Scalar.AspNetCore": { + "type": "Direct", + "requested": "[2.8.4, )", + "resolved": "2.8.4", + "contentHash": "Ydmi6mtnIMa9cLTHxSdGy/CelCfaI6AvEOZ4nfAd6Tc6tE5uY9L7hU1M87gLRPf78+zCqBZMj0CI/rRa+uj7+g==" + }, + "Serilog": { + "type": "Direct", + "requested": "[4.0.1, )", + "resolved": "4.0.1", + "contentHash": "pzeDRXdpSLSsgBHpZcmpIDxqMy845Ab4s+dfnBg0sN9h8q/4Wo3vAoe0QCGPze1Q06EVtEPupS+UvLm8iXQmTQ==" + }, + "Serilog.AspNetCore": { + "type": "Direct", + "requested": "[8.0.1, )", + "resolved": "8.0.1", + "contentHash": "B/X+wAfS7yWLVOTD83B+Ip9yl4MkhioaXj90JSoWi1Ayi8XHepEnsBdrkojg08eodCnmOKmShFUN2GgEc6c0CQ==", + "dependencies": { + "Serilog": "3.1.1", + "Serilog.Extensions.Hosting": "8.0.0", + "Serilog.Extensions.Logging": "8.0.0", + "Serilog.Formatting.Compact": "2.0.0", + "Serilog.Settings.Configuration": "8.0.0", + "Serilog.Sinks.Console": "5.0.0", + "Serilog.Sinks.Debug": "2.0.0", + "Serilog.Sinks.File": "5.0.0" + } + }, + "Swashbuckle.AspNetCore": { + "type": "Direct", + "requested": "[10.1.0, )", + "resolved": "10.1.0", + "contentHash": "CvOffaJKStoP0hdfCIX4V/9wuwRkSOJd+PehGo/Y5ALh0WYliLwuMlyh1BbgG7uQtJNe1k5P7QIhIaFfZ/aJAw==", + "dependencies": { + "Microsoft.Extensions.ApiDescription.Server": "10.0.0", + "Swashbuckle.AspNetCore.Swagger": "10.1.0", + "Swashbuckle.AspNetCore.SwaggerGen": "10.1.0", + "Swashbuckle.AspNetCore.SwaggerUI": "10.1.0" + } + }, + "Azure.Core": { + "type": "Transitive", + "resolved": "1.47.1", + "contentHash": "oPcncSsDHuxB8SC522z47xbp2+ttkcKv2YZ90KXhRKN0YQd2+7l1UURT9EBzUNEXtkLZUOAB5xbByMTrYRh3yA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "System.ClientModel": "1.5.1", + "System.Memory.Data": "8.0.1" + } + }, + "Azure.Identity": { + "type": "Transitive", + "resolved": "1.14.2", + "contentHash": "YhNMwOTwT+I2wIcJKSdP0ADyB2aK+JaYWZxO8LSRDm5w77LFr0ykR9xmt2ZV5T1gaI7xU6iNFIh/yW1dAlpddQ==", + "dependencies": { + "Azure.Core": "1.46.1", + "Microsoft.Identity.Client": "4.73.1", + "Microsoft.Identity.Client.Extensions.Msal": "4.73.1" + } + }, + "FluentValidation": { + "type": "Transitive", + "resolved": "11.11.0", + "contentHash": "cyIVdQBwSipxWG8MA3Rqox7iNbUNUTK5bfJi9tIdm4CAfH71Oo5ABLP4/QyrUwuakqpUEPGtE43BDddvEehuYw==" + }, + "FluentValidation.DependencyInjectionExtensions": { + "type": "Transitive", + "resolved": "11.11.0", + "contentHash": "viTKWaMbL3yJYgGI0DiCeavNbE9UPMWFVK2XS9nYXGbm3NDMd0/L5ER4wBzmTtW3BYh3SrlSXm9RACiKZ6stlA==", + "dependencies": { + "FluentValidation": "11.11.0" + } + }, + "Humanizer.Core": { + "type": "Transitive", + "resolved": "2.14.1", + "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.Cryptography": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "YgZYAWzyNuPVtPq6WNm0bqOWNjYaWgl5mBWTGZyNoXitYBUYSp6iUB9AwK0V1mo793qRJUXz2t6UZrWITZSvuQ==" + }, + "Microsoft.Build": { + "type": "Transitive", + "resolved": "17.7.2", + "contentHash": "AmWnumxsMiRycFfE3kq/XnFFTAoPpCWl3UuiKQWCa5Z0+hBKVoiydzS2iXJGd3x+jry+qaTR9GzoezjV9NFT5A==", + "dependencies": { + "Microsoft.Build.Framework": "17.7.2", + "Microsoft.NET.StringTools": "17.7.2", + "System.Configuration.ConfigurationManager": "7.0.0", + "System.Reflection.MetadataLoadContext": "7.0.0", + "System.Security.Permissions": "7.0.0" + } + }, + "Microsoft.Build.Framework": { + "type": "Transitive", + "resolved": "17.14.28", + "contentHash": "wRcyTzGV0LRAtFdrddtioh59Ky4/zbvyraP0cQkDzRSRkhgAQb0K88D/JNC6VHLIXanRi3mtV1jU0uQkBwmiVg==" + }, + "Microsoft.Build.Tasks.Core": { + "type": "Transitive", + "resolved": "17.14.28", + "contentHash": "jk3O0tXp9QWPXhLJ7Pl8wm/eGtGgA1++vwHGWEmnwMU6eP//ghtcCUpQh9CQMwEKGDnH0aJf285V1s8yiSlKfQ==", + "dependencies": { + "Microsoft.Build.Framework": "17.14.28", + "Microsoft.Build.Utilities.Core": "17.14.28", + "Microsoft.NET.StringTools": "17.14.28", + "System.CodeDom": "9.0.0", + "System.Configuration.ConfigurationManager": "9.0.0", + "System.Formats.Nrbf": "9.0.0", + "System.Resources.Extensions": "9.0.0", + "System.Security.Cryptography.Pkcs": "9.0.0", + "System.Security.Cryptography.ProtectedData": "9.0.0" + } + }, + "Microsoft.Build.Utilities.Core": { + "type": "Transitive", + "resolved": "17.14.28", + "contentHash": "rhSdPo8QfLXXWM+rY0x0z1G4KK4ZhMoIbHROyDj8MUBFab9nvHR0NaMnjzOgXldhmD2zi2ir8d6xCatNzlhF5g==", + "dependencies": { + "Microsoft.Build.Framework": "17.14.28", + "Microsoft.NET.StringTools": "17.14.28", + "System.Configuration.ConfigurationManager": "9.0.0", + "System.Security.Cryptography.ProtectedData": "9.0.0" + } + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "3.11.0", + "contentHash": "v/EW3UE8/lbEYHoC2Qq7AR/DnmvpgdtAMndfQNmpuIMx/Mto8L5JnuCfdBYtgvalQOtfNCnxFejxuRrryvUTsg==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "PC3tuwZYnC+idaPuoC/AZpEdwrtX7qFpmnrfQkgobGIWiYmGi5MCRtl5mx6QrfMGQpK78X2lfIEoZDLg/qnuHg==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "568a6wcTivauIhbeWcCwfWwIn7UV7MeHEBvFB2uzGIpM2OhJ4eM/FZ8KS0yhPoNxnSpjGzz7x7CIjTxhslojQA==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.Common": "[4.14.0]" + } + }, + "Microsoft.CodeAnalysis.CSharp.Workspaces": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "QkgCEM4qJo6gdtblXtNgHqtykS61fxW+820hx5JN6n9DD4mQtqNB+6fPeJ3GQWg6jkkGz6oG9yZq7H3Gf0zwYw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.CSharp": "[4.14.0]", + "Microsoft.CodeAnalysis.Common": "[4.14.0]", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.14.0]", + "System.Composition": "9.0.0" + } + }, + "Microsoft.CodeAnalysis.Workspaces.Common": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "wNVK9JrqjqDC/WgBUFV6henDfrW87NPfo98nzah/+M/G1D6sBOPtXwqce3UQNn+6AjTnmkHYN1WV9XmTlPemTw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.Common": "[4.14.0]", + "System.Composition": "9.0.0" + } + }, + "Microsoft.CodeAnalysis.Workspaces.MSBuild": { + "type": "Transitive", + "resolved": "4.14.0", + "contentHash": "YU7Sguzm1Cuhi2U6S0DRKcVpqAdBd2QmatpyE0KqYMJogJ9E27KHOWGUzAOjsyjAM7sNaUk+a8VPz24knDseFw==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.Build": "17.7.2", + "Microsoft.Build.Framework": "17.7.2", + "Microsoft.Build.Tasks.Core": "17.7.2", + "Microsoft.Build.Utilities.Core": "17.7.2", + "Microsoft.CodeAnalysis.Analyzers": "3.11.0", + "Microsoft.CodeAnalysis.Workspaces.Common": "[4.14.0]", + "Newtonsoft.Json": "13.0.3", + "System.CodeDom": "7.0.0", + "System.Composition": "9.0.0", + "System.Configuration.ConfigurationManager": "9.0.0", + "System.Resources.Extensions": "9.0.0", + "System.Security.Cryptography.Pkcs": "7.0.2", + "System.Security.Cryptography.ProtectedData": "9.0.0", + "System.Security.Permissions": "9.0.0", + "System.Windows.Extensions": "9.0.0" + } + }, + "Microsoft.Data.SqlClient": { + "type": "Transitive", + "resolved": "6.1.1", + "contentHash": "syGQmIUPAYYHAHyTD8FCkTNThpQWvoA7crnIQRMfp8dyB5A2cWU3fQexlRTFkVmV7S0TjVmthi0LJEFVjHo8AQ==", + "dependencies": { + "Azure.Core": "1.47.1", + "Azure.Identity": "1.14.2", + "Microsoft.Bcl.Cryptography": "9.0.4", + "Microsoft.Data.SqlClient.SNI.runtime": "6.0.2", + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.7.1", + "Microsoft.SqlServer.Server": "1.0.0", + "System.Configuration.ConfigurationManager": "9.0.4", + "System.Security.Cryptography.Pkcs": "9.0.4" + } + }, + "Microsoft.Data.SqlClient.SNI.runtime": { + "type": "Transitive", + "resolved": "6.0.2", + "contentHash": "f+pRODTWX7Y67jXO3T5S2dIPZ9qMJNySjlZT/TKmWVNWe19N8jcWmHaqHnnchaq3gxEKv1SWVY5EFzOD06l41w==" + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "wPKG/Ym6tSMCo06UOZXzVfeFGzawnOZrTba/R3PfK+RhNuNELZ9I7nFns4WGTtv2kKlrlmmErgJ+kgTXHaNdHg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.11" + } + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "TxHQq0kn0tpYs2ljeRl8jtmWk720B0nteqI6mAZM77HWJpYT9Zj8SkkBBlj8K3Yeq18a6NBjz6YutE+shEk4Ag==" + }, + "Microsoft.EntityFrameworkCore.Design": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "R7BeFniEpBrHw8kKVtWiMG4PRAwJ4K1RZoQWB32Ak8ws3uvYH98DVp9Y2UBUgbwY5lR9wPlrxp7P3GGDQ7LUSQ==", + "dependencies": { + "Humanizer.Core": "2.14.1", + "Microsoft.Build.Framework": "17.14.28", + "Microsoft.Build.Tasks.Core": "17.14.28", + "Microsoft.Build.Utilities.Core": "17.14.28", + "Microsoft.CodeAnalysis.CSharp": "4.14.0", + "Microsoft.CodeAnalysis.CSharp.Workspaces": "4.14.0", + "Microsoft.CodeAnalysis.Workspaces.MSBuild": "4.14.0", + "Microsoft.EntityFrameworkCore.Relational": "10.0.0", + "Microsoft.Extensions.DependencyModel": "10.0.0", + "Mono.TextTemplating": "3.0.0", + "Newtonsoft.Json": "13.0.3" + } + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "A3MX1ee7RDxWCUdx/KqP+74fbksz0UIhkVZh56YHvbPkEKsffCXgHU3LGkRDwqR/MrBNWLCWC/IVX79tzM30ZA==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "10.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Sqlite.Core": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "VThKv9UqVxFEuuHvjAgMwy6ZFCeKJXOH+ISAR4IMuwlkozv26ptZhr09+6YxWrWwSR/Sinp8BxQ7qePCJFSALQ==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "10.0.0", + "Microsoft.EntityFrameworkCore.Relational": "10.0.0", + "Microsoft.Extensions.DependencyModel": "10.0.0", + "SQLitePCLRaw.core": "2.1.11" + } + }, + "Microsoft.Extensions.ApiDescription.Server": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "NCWCGiwRwje8773yzPQhvucYnnfeR+ZoB1VRIrIMp4uaeUNw7jvEPHij3HIbwCDuNCrNcphA00KSAR9yD9qmbg==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "RFYJR7APio/BiqdQunRq6DB+nDB6nc2qhHr77mlvZ0q0BT8PubMXN7XicmfzCbrDE/dzhBnUKBRXLTcqUiZDGg==" + }, + "Microsoft.Identity.Client": { + "type": "Transitive", + "resolved": "4.73.1", + "contentHash": "NnDLS8QwYqO5ZZecL2oioi1LUqjh5Ewk4bMLzbgiXJbQmZhDLtKwLxL3DpGMlQAJ2G4KgEnvGPKa+OOgffeJbw==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "6.35.0" + } + }, + "Microsoft.Identity.Client.Extensions.Msal": { + "type": "Transitive", + "resolved": "4.73.1", + "contentHash": "xDztAiV2F0wI0W8FLKv5cbaBefyLD6JVaAsvgSN7bjWNCzGYzHbcOEIP5s4TJXUpQzMfUyBsFl1mC6Zmgpz0PQ==", + "dependencies": { + "Microsoft.Identity.Client": "4.73.1", + "System.Security.Cryptography.ProtectedData": "4.5.0" + } + }, + "Microsoft.IdentityModel.Abstractions": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "S7sHg6gLg7oFqNGLwN1qSbJDI+QcRRj8SuJ1jHyCmKSipnF6ZQL+tFV2NzVfGj/xmGT9TykQdQiBN+p5Idl4TA==" + }, + "Microsoft.IdentityModel.JsonWebTokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "3Izi75UCUssvo8LPx3OVnEeZay58qaFicrtSnbtUt7q8qQi0gy46gh4V8VUTkMVMKXV6VMyjBVmeNNgeCUJuIw==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Logging": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "BZNgSq/o8gsKExdYoBKPR65fdsxW0cTF8PsdqB8y011AGUJJW300S/ZIsEUD0+sOmGc003Gwv3FYbjrVjvsLNQ==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "h+fHHBGokepmCX+QZXJk4Ij8OApCb2n2ktoDkNX5CXteXsOxTHMNgjPGpAwdJMFvAL7TtGarUnk3o97NmBq2QQ==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "yT2Hdj8LpPbcT9C9KlLVxXl09C8zjFaVSaApdOwuecMuoV4s6Sof/mnTDz/+F/lILPIBvrWugR9CC7iRVZgbfQ==", + "dependencies": { + "Microsoft.IdentityModel.Protocols": "7.7.1", + "System.IdentityModel.Tokens.Jwt": "7.7.1" + } + }, + "Microsoft.IdentityModel.Tokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "fQ0VVCba75lknUHGldi3iTKAYUQqbzp1Un8+d9cm9nON0Gs8NAkXddNg8iaUB0qi/ybtAmNWizTR4avdkCJ9pQ==", + "dependencies": { + "Microsoft.IdentityModel.Logging": "7.7.1" + } + }, + "Microsoft.NET.StringTools": { + "type": "Transitive", + "resolved": "17.14.28", + "contentHash": "DMIeWDlxe0Wz0DIhJZ2FMoGQAN2yrGZOi5jjFhRYHWR5ONd0CS6IpAHlRnA7uA/5BF+BADvgsETxW2XrPiFc1A==" + }, + "Microsoft.OpenApi": { + "type": "Transitive", + "resolved": "2.3.0", + "contentHash": "5RZpjyt0JMmoc/aEgY9c1vE5pusdDGvkPl9qKIy9KFbRiIXD+w7gBJxX+unSjzzOcfgRoYxnO4okZyqDAL2WEw==" + }, + "Microsoft.SqlServer.Server": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" + }, + "Mono.TextTemplating": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "YqueG52R/Xej4VVbKuRIodjiAhV0HR/XVbLbNrJhCZnzjnSjgMJ/dCdV0akQQxavX6hp/LC6rqLGLcXeQYU7XA==", + "dependencies": { + "System.CodeDom": "6.0.0" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.3", + "contentHash": "HrC5BXdl00IP9zeV+0Z848QWPAoCr9P3bDEZguI+gkLcBKAOxix/tLEAAHC+UvDNPv4a2d18lOReHMOagPa+zQ==" + }, + "Serilog.Extensions.Hosting": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "db0OcbWeSCvYQkHWu6n0v40N4kKaTAXNjlM3BKvcbwvNzYphQFcBR+36eQ/7hMMwOkJvAyLC2a9/jNdUL5NjtQ==", + "dependencies": { + "Serilog": "3.1.1", + "Serilog.Extensions.Logging": "8.0.0" + } + }, + "Serilog.Extensions.Logging": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "YEAMWu1UnWgf1c1KP85l1SgXGfiVo0Rz6x08pCiPOIBt2Qe18tcZLvdBUuV5o1QHvrs8FAry9wTIhgBRtjIlEg==", + "dependencies": { + "Serilog": "3.1.1" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "ob6z3ikzFM3D1xalhFuBIK1IOWf+XrQq+H4KeH4VqBcPpNcmUgZlRQ2h3Q7wvthpdZBBoY86qZOI2LCXNaLlNA==", + "dependencies": { + "Serilog": "3.1.0" + } + }, + "Serilog.Settings.Configuration": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "nR0iL5HwKj5v6ULo3/zpP8NMcq9E2pxYA6XKTSWCbugVs4YqPyvaqaKOY+OMpPivKp7zMEpax2UKHnDodbRB0Q==", + "dependencies": { + "Microsoft.Extensions.DependencyModel": "8.0.0", + "Serilog": "3.1.1" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "IZ6bn79k+3SRXOBpwSOClUHikSkp2toGPCZ0teUkscv4dpDg9E2R2xVsNkLmwddE4OpNVO3N0xiYsAH556vN8Q==", + "dependencies": { + "Serilog": "3.1.0" + } + }, + "Serilog.Sinks.Debug": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "Y6g3OBJ4JzTyyw16fDqtFcQ41qQAydnEvEqmXjhwhgjsnG/FaJ8GUqF5ldsC/bVkK8KYmqrPhDO+tm4dF6xx4A==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.11", + "contentHash": "DC4nA7yWnf4UZdgJDF+9Mus4/cb0Y3Sfgi3gDnAoKNAIBwzkskNAbNbyu+u4atT0ruVlZNJfwZmwiEwE5oz9LQ==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.11", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.11" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.11", + "contentHash": "PK0GLFkfhZzLQeR3PJf71FmhtHox+U3vcY6ZtswoMjrefkB9k6ErNJEnwXqc5KgXDSjige2XXrezqS39gkpQKA==" + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.11", + "contentHash": "Ev2ytaXiOlWZ4b3R67GZBsemTINslLD1DCJr2xiacpn4tbapu0Q4dHEzSvZSMnVWeE5nlObU3VZN2p81q3XOYQ==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.11", + "contentHash": "Y/0ZkR+r0Cg3DQFuCl1RBnv/tmxpIZRU3HUvelPw6MVaKHwYYR8YNvgs0vuNuXCMvlyJ+Fh88U1D4tah1tt6qw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.11" + } + }, + "Swashbuckle.AspNetCore.Swagger": { + "type": "Transitive", + "resolved": "10.1.0", + "contentHash": "9Kp35Jhkzw73UXnvgGWVgRjvfGx5h1V4fdL9JcPZMKoTyO/bnKD/1i86n8u2p+rVTDed0cDSH4PKbX4WicZ/gg==", + "dependencies": { + "Microsoft.OpenApi": "2.3.0" + } + }, + "Swashbuckle.AspNetCore.SwaggerGen": { + "type": "Transitive", + "resolved": "10.1.0", + "contentHash": "XV2gyKmjWs5K5QaSq9rNYtO/E7vr/RcyBkMbbCVUlUtI5OY0HKj260Wee9zsJ7scJf7kPCxeseBiUMRp67ZWxA==", + "dependencies": { + "Swashbuckle.AspNetCore.Swagger": "10.1.0" + } + }, + "Swashbuckle.AspNetCore.SwaggerUI": { + "type": "Transitive", + "resolved": "10.1.0", + "contentHash": "ilUsTvGA9hO1ulR7ibdWMWSg3438Iu+pDFcEYUorp+/ClHwaHFdpp/ATfBsFXv2sIRVDbQlEwd5BWBOdMdtKCA==" + }, + "System.ClientModel": { + "type": "Transitive", + "resolved": "1.5.1", + "contentHash": "k2jKSO0X45IqhVOT9iQB4xralNN9foRQsRvXBTyRpAVxyzCJlG895T9qYrQWbcJ6OQXxOouJQ37x5nZH5XKK+A==", + "dependencies": { + "System.Memory.Data": "8.0.1" + } + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "oTE5IfuMoET8yaZP/vdvy9xO47guAv/rOhe4DODuFBN3ySprcQOlXqO3j+e/H/YpKKR5sglrxRaZ2HYOhNJrqA==" + }, + "System.Composition": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "3Djj70fFTraOarSKmRnmRy/zm4YurICm+kiCtI0dYRqGJnLX6nJ+G3WYuFJ173cAPax/gh96REcbNiVqcrypFQ==", + "dependencies": { + "System.Composition.AttributedModel": "9.0.0", + "System.Composition.Convention": "9.0.0", + "System.Composition.Hosting": "9.0.0", + "System.Composition.Runtime": "9.0.0", + "System.Composition.TypedParts": "9.0.0" + } + }, + "System.Composition.AttributedModel": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "iri00l/zIX9g4lHMY+Nz0qV1n40+jFYAmgsaiNn16xvt2RDwlqByNG4wgblagnDYxm3YSQQ0jLlC/7Xlk9CzyA==" + }, + "System.Composition.Convention": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "+vuqVP6xpi582XIjJi6OCsIxuoTZfR0M7WWufk3uGDeCl3wGW6KnpylUJ3iiXdPByPE0vR5TjJgR6hDLez4FQg==", + "dependencies": { + "System.Composition.AttributedModel": "9.0.0" + } + }, + "System.Composition.Hosting": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "OFqSeFeJYr7kHxDfaViGM1ymk7d4JxK//VSoNF9Ux0gpqkLsauDZpu89kTHHNdCWfSljbFcvAafGyBoY094btQ==", + "dependencies": { + "System.Composition.Runtime": "9.0.0" + } + }, + "System.Composition.Runtime": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "w1HOlQY1zsOWYussjFGZCEYF2UZXgvoYnS94NIu2CBnAGMbXFAX8PY8c92KwUItPmowal68jnVLBCzdrWLeEKA==" + }, + "System.Composition.TypedParts": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "aRZlojCCGEHDKqh43jaDgaVpYETsgd7Nx4g1zwLKMtv4iTo0627715ajEFNpEEBTgLmvZuv8K0EVxc3sM4NWJA==", + "dependencies": { + "System.Composition.AttributedModel": "9.0.0", + "System.Composition.Hosting": "9.0.0", + "System.Composition.Runtime": "9.0.0" + } + }, + "System.Configuration.ConfigurationManager": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "dvjqKp+2LpGid6phzrdrS/2mmEPxFl3jE1+L7614q4ZChKbLJCpHXg6sBILlCCED1t//EE+un/UdAetzIMpqnw==", + "dependencies": { + "System.Security.Cryptography.ProtectedData": "9.0.4" + } + }, + "System.Formats.Nrbf": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "F/6tNE+ckmdFeSQAyQo26bQOqfPFKEfZcuqnp4kBE6/7jP26diP+QTHCJJ6vpEfaY6bLy+hBLiIQUSxSmNwLkA==" + }, + "System.IdentityModel.Tokens.Jwt": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "rQkO1YbAjLwnDJSMpRhRtrc6XwIcEOcUvoEcge+evurpzSZM3UNK+MZfD3sKyTlYsvknZ6eJjSBfnmXqwOsT9Q==", + "dependencies": { + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "System.Memory.Data": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "BVYuec3jV23EMRDeR7Dr1/qhx7369dZzJ9IWy2xylvb4YfXsrUxspWc4UWYid/tj4zZK58uGZqn2WQiaDMhmAg==" + }, + "System.Reflection.MetadataLoadContext": { + "type": "Transitive", + "resolved": "7.0.0", + "contentHash": "z9PvtMJra5hK8n+g0wmPtaG7HQRZpTmIPRw5Z0LEemlcdQMHuTD5D7OAY/fZuuz1L9db++QOcDF0gJTLpbMtZQ==" + }, + "System.Resources.Extensions": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "tvhuT1D2OwPROdL1kRWtaTJliQo0WdyhvwDpd8RM997G7m3Hya5nhbYhNTS75x6Vu+ypSOgL5qxDCn8IROtCxw==", + "dependencies": { + "System.Formats.Nrbf": "9.0.0" + } + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "cUFTcMlz/Qw9s90b2wnWSCvHdjv51Bau9FQqhsr4TlwSe1OX+7SoXUqphis5G74MLOvMOCghxPPlEqOdCrVVGA==" + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "o94k2RKuAce3GeDMlUvIXlhVa1kWpJw95E6C9LwW0KlG0nj5+SgCiIxJ2Eroqb9sLtG1mEMbFttZIBZ13EJPvQ==" + }, + "System.Security.Permissions": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "H2VFD4SFVxieywNxn9/epb63/IOcPPfA0WOtfkljzNfu7GCcHIBQNuwP6zGCEIi7Ci/oj8aLPUNK9sYImMFf4Q==", + "dependencies": { + "System.Windows.Extensions": "9.0.0" + } + }, + "System.Windows.Extensions": { + "type": "Transitive", + "resolved": "9.0.0", + "contentHash": "U9msthvnH2Fsw7xwAvIhNHOdnIjOQTwOc8Vd0oGOsiRcGMGoBFlUD6qtYawRUoQdKH9ysxesZ9juFElt1Jw/7A==" + } + } + } +} \ No newline at end of file diff --git a/tests/ArchitectureTests/packages.lock.json b/tests/ArchitectureTests/packages.lock.json new file mode 100644 index 00000000..96780c60 --- /dev/null +++ b/tests/ArchitectureTests/packages.lock.json @@ -0,0 +1,671 @@ +{ + "version": 1, + "dependencies": { + "net10.0": { + "coverlet.collector": { + "type": "Direct", + "requested": "[6.0.2, )", + "resolved": "6.0.2", + "contentHash": "bJShQ6uWRTQ100ZeyiMqcFlhP7WJ+bCuabUs885dJiBEzMsJMSFr7BOyeCw4rgvQokteGi5rKQTlkhfQPUXg2A==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.11.1, )", + "resolved": "17.11.1", + "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==", + "dependencies": { + "Microsoft.CodeCoverage": "17.11.1", + "Microsoft.TestPlatform.TestHost": "17.11.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.9.2, )", + "resolved": "2.9.2", + "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==", + "dependencies": { + "xunit.analyzers": "1.16.0", + "xunit.assert": "2.9.2", + "xunit.core": "[2.9.2]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[2.8.2, )", + "resolved": "2.8.2", + "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ==" + }, + "Azure.Core": { + "type": "Transitive", + "resolved": "1.47.1", + "contentHash": "oPcncSsDHuxB8SC522z47xbp2+ttkcKv2YZ90KXhRKN0YQd2+7l1UURT9EBzUNEXtkLZUOAB5xbByMTrYRh3yA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "System.ClientModel": "1.5.1", + "System.Memory.Data": "8.0.1" + } + }, + "Azure.Identity": { + "type": "Transitive", + "resolved": "1.14.2", + "contentHash": "YhNMwOTwT+I2wIcJKSdP0ADyB2aK+JaYWZxO8LSRDm5w77LFr0ykR9xmt2ZV5T1gaI7xU6iNFIh/yW1dAlpddQ==", + "dependencies": { + "Azure.Core": "1.46.1", + "Microsoft.Identity.Client": "4.73.1", + "Microsoft.Identity.Client.Extensions.Msal": "4.73.1" + } + }, + "FluentValidation": { + "type": "Transitive", + "resolved": "11.11.0", + "contentHash": "cyIVdQBwSipxWG8MA3Rqox7iNbUNUTK5bfJi9tIdm4CAfH71Oo5ABLP4/QyrUwuakqpUEPGtE43BDddvEehuYw==" + }, + "FluentValidation.AspNetCore": { + "type": "Transitive", + "resolved": "11.3.1", + "contentHash": "FjkfrGGwC+25oH8QpX1ti3gxkFjDDtotkAQWyQz2CkwQwEfRKTANjhh77I79kY8dov2ydK1SY+e4jiOWsbbDdQ==", + "dependencies": { + "FluentValidation": "11.11.0", + "FluentValidation.DependencyInjectionExtensions": "11.11.0" + } + }, + "FluentValidation.DependencyInjectionExtensions": { + "type": "Transitive", + "resolved": "11.11.0", + "contentHash": "viTKWaMbL3yJYgGI0DiCeavNbE9UPMWFVK2XS9nYXGbm3NDMd0/L5ER4wBzmTtW3BYh3SrlSXm9RACiKZ6stlA==", + "dependencies": { + "FluentValidation": "11.11.0", + "Microsoft.Extensions.Dependencyinjection.Abstractions": "2.1.0" + } + }, + "Microsoft.AspNetCore.OpenApi": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "gMY53EggRIFawhue66GanHcm1Tcd0+QzzMwnMl60LrEoJhGgzA9qAbLx6t/ON3hX4flc2NcEbTK1Z5GCLYHcwA==", + "dependencies": { + "Microsoft.OpenApi": "2.0.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.Cryptography": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "YgZYAWzyNuPVtPq6WNm0bqOWNjYaWgl5mBWTGZyNoXitYBUYSp6iUB9AwK0V1mo793qRJUXz2t6UZrWITZSvuQ==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.11.1", + "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA==" + }, + "Microsoft.Data.SqlClient": { + "type": "Transitive", + "resolved": "6.1.1", + "contentHash": "syGQmIUPAYYHAHyTD8FCkTNThpQWvoA7crnIQRMfp8dyB5A2cWU3fQexlRTFkVmV7S0TjVmthi0LJEFVjHo8AQ==", + "dependencies": { + "Azure.Core": "1.47.1", + "Azure.Identity": "1.14.2", + "Microsoft.Bcl.Cryptography": "9.0.4", + "Microsoft.Data.SqlClient.SNI.runtime": "6.0.2", + "Microsoft.Extensions.Caching.Memory": "9.0.4", + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.7.1", + "Microsoft.SqlServer.Server": "1.0.0", + "System.Configuration.ConfigurationManager": "9.0.4", + "System.Security.Cryptography.Pkcs": "9.0.4" + } + }, + "Microsoft.Data.SqlClient.SNI.runtime": { + "type": "Transitive", + "resolved": "6.0.2", + "contentHash": "f+pRODTWX7Y67jXO3T5S2dIPZ9qMJNySjlZT/TKmWVNWe19N8jcWmHaqHnnchaq3gxEKv1SWVY5EFzOD06l41w==" + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "wPKG/Ym6tSMCo06UOZXzVfeFGzawnOZrTba/R3PfK+RhNuNELZ9I7nFns4WGTtv2kKlrlmmErgJ+kgTXHaNdHg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.11" + } + }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "hHa2amRjMyBLUH/KTML6FgIAhZ0VFYkhCKwWEax0rO6iNeM1P5MflyeQLE5dniSIOZHc3Oqyv5UIyTFO4e1Auw==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.0", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.0", + "Microsoft.Extensions.Caching.Memory": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "C+TT9k7f1GQ8agOfV512K9iwrzi76RXVSDiLx+iWC9pz3QhEpSF1Dyk+FpVvd8ULQ+rqymfM8KQ7g48ttQVyMg==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "TxHQq0kn0tpYs2ljeRl8jtmWk720B0nteqI6mAZM77HWJpYT9Zj8SkkBBlj8K3Yeq18a6NBjz6YutE+shEk4Ag==" + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "A3MX1ee7RDxWCUdx/KqP+74fbksz0UIhkVZh56YHvbPkEKsffCXgHU3LGkRDwqR/MrBNWLCWC/IVX79tzM30ZA==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "10.0.0", + "Microsoft.Extensions.Caching.Memory": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Sqlite": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "nukHe+yBlhitLUUtkanay7zTbHwtcIh/U5PfmwzZJJTCqui9h2Mt+Gifc9ZjJR7QIuE0zgNQQJaI8+eFxkBaEQ==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Sqlite.Core": "10.0.0", + "Microsoft.Extensions.Caching.Memory": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.DependencyModel": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.11", + "SQLitePCLRaw.core": "2.1.11" + } + }, + "Microsoft.EntityFrameworkCore.Sqlite.Core": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "VThKv9UqVxFEuuHvjAgMwy6ZFCeKJXOH+ISAR4IMuwlkozv26ptZhr09+6YxWrWwSR/Sinp8BxQ7qePCJFSALQ==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "10.0.0", + "Microsoft.EntityFrameworkCore.Relational": "10.0.0", + "Microsoft.Extensions.Caching.Memory": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.DependencyModel": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0", + "SQLitePCLRaw.core": "2.1.11" + } + }, + "Microsoft.EntityFrameworkCore.SqlServer": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "9ip+r8Wtu0qNtFLYnQC3bkhQAsINIyo8XWYJHCVY22BFLxIT4rPxZPkbto56SjCnkwxS6nnbFZTU6KuAO2Nu1g==", + "dependencies": { + "Microsoft.Data.SqlClient": "6.1.1", + "Microsoft.EntityFrameworkCore.Relational": "10.0.0", + "Microsoft.Extensions.Caching.Memory": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0" + } + }, + "Microsoft.Extensions.ApiDescription.Server": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "NCWCGiwRwje8773yzPQhvucYnnfeR+ZoB1VRIrIMp4uaeUNw7jvEPHij3HIbwCDuNCrNcphA00KSAR9yD9qmbg==" + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "Zcoy6H9mSoGyvr7UvlGokEZrlZkcPCICPZr8mCsSt9U/N8eeCwCXwKF5bShdA66R0obxBCwP4AxomQHvVkC/uA==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "krK19MKp0BNiR9rpBDW7PKSrTMLVlifS9am3CVc4O1Jq6GWz0o4F+sw5OSL4L3mVd56W8l6JRgghUa2KB51vOw==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging.Abstractions": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "d2kDKnCsJvY7mBVhcjPSp9BkJk48DsaHPg5u+Oy4f8XaOqnEedRy/USyvnpHL92wpJ6DrTPy7htppUUzskbCXQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "mBMoXLsr5s1y2zOHWmKsE9veDcx8h1x/c3rz4baEdQKTeDcmQAPNbB54Pi/lhFO3K431eEq6PFbMgLaa6PHFfA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "f0RBabswJq+gRu5a+hWIobrLWiUYPKMhCD9WO3sYBAdSy3FFH14LMvLVFZc2kPSCimBLxSuitUhsd6tb0TAY6A==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "L3AdmZ1WOK4XXT5YFPEwyt0ep6l8lGIPs7F5OOBZc77Zqeo01Of7XXICy47628sdVl0v/owxYJTe86DTgFwKCA==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "RFYJR7APio/BiqdQunRq6DB+nDB6nc2qhHr77mlvZ0q0BT8PubMXN7XicmfzCbrDE/dzhBnUKBRXLTcqUiZDGg==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "JHYCQG7HmugNYUhOl368g+NMxYE/N/AiclCYRNlgCY9eVyiBkOHMwK4x60RYMxv9EL3+rmj1mqHvdCiPpC+D4Q==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "AG7HWwVRdCHlaA++1oKDxLsXIBxmDpMPb3VoyOoAghEWnkUvEAdYQUwnV4jJbAaa/nMYNiEh5ByoLauZBEiovg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "BStFkd5CcnEtarlcgYDBcFzGYCuuNMzPs02wN3WBsOFoYIEmYoUdAiU+au6opzoqfTYJsMTW00AeqDdnXH2CvA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "10.0.0", + "Microsoft.Extensions.Logging.Abstractions": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "FU/IfjDfwaMuKr414SSQNTIti/69bHEMb+QKrskRb26oVqpx3lNFXMjs/RC9ZUuhBhcwDM2BwOgoMw+PZ+beqQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "8oCAgXOow5XDrY9HaXX1QmH3ORsyZO/ANVHBlhLyCeWTH5Sg4UuqZeOTWJi6484M+LqSx0RqQXDJtdYy2BNiLQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "inRnbpCS0nwO/RuoZIAqxQUuyjaknOOnCEZB55KSMMjRhl0RQDttSmLSGsUJN3RQ3ocf5NDLFd2mOQViHqMK5w==" + }, + "Microsoft.Identity.Client": { + "type": "Transitive", + "resolved": "4.73.1", + "contentHash": "NnDLS8QwYqO5ZZecL2oioi1LUqjh5Ewk4bMLzbgiXJbQmZhDLtKwLxL3DpGMlQAJ2G4KgEnvGPKa+OOgffeJbw==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "6.35.0" + } + }, + "Microsoft.Identity.Client.Extensions.Msal": { + "type": "Transitive", + "resolved": "4.73.1", + "contentHash": "xDztAiV2F0wI0W8FLKv5cbaBefyLD6JVaAsvgSN7bjWNCzGYzHbcOEIP5s4TJXUpQzMfUyBsFl1mC6Zmgpz0PQ==", + "dependencies": { + "Microsoft.Identity.Client": "4.73.1", + "System.Security.Cryptography.ProtectedData": "4.5.0" + } + }, + "Microsoft.IdentityModel.Abstractions": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "S7sHg6gLg7oFqNGLwN1qSbJDI+QcRRj8SuJ1jHyCmKSipnF6ZQL+tFV2NzVfGj/xmGT9TykQdQiBN+p5Idl4TA==" + }, + "Microsoft.IdentityModel.JsonWebTokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "3Izi75UCUssvo8LPx3OVnEeZay58qaFicrtSnbtUt7q8qQi0gy46gh4V8VUTkMVMKXV6VMyjBVmeNNgeCUJuIw==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Logging": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "BZNgSq/o8gsKExdYoBKPR65fdsxW0cTF8PsdqB8y011AGUJJW300S/ZIsEUD0+sOmGc003Gwv3FYbjrVjvsLNQ==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "h+fHHBGokepmCX+QZXJk4Ij8OApCb2n2ktoDkNX5CXteXsOxTHMNgjPGpAwdJMFvAL7TtGarUnk3o97NmBq2QQ==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "yT2Hdj8LpPbcT9C9KlLVxXl09C8zjFaVSaApdOwuecMuoV4s6Sof/mnTDz/+F/lILPIBvrWugR9CC7iRVZgbfQ==", + "dependencies": { + "Microsoft.IdentityModel.Protocols": "7.7.1", + "System.IdentityModel.Tokens.Jwt": "7.7.1" + } + }, + "Microsoft.IdentityModel.Tokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "fQ0VVCba75lknUHGldi3iTKAYUQqbzp1Un8+d9cm9nON0Gs8NAkXddNg8iaUB0qi/ybtAmNWizTR4avdkCJ9pQ==", + "dependencies": { + "Microsoft.IdentityModel.Logging": "7.7.1" + } + }, + "Microsoft.OpenApi": { + "type": "Transitive", + "resolved": "2.3.0", + "contentHash": "5RZpjyt0JMmoc/aEgY9c1vE5pusdDGvkPl9qKIy9KFbRiIXD+w7gBJxX+unSjzzOcfgRoYxnO4okZyqDAL2WEw==" + }, + "Microsoft.SqlServer.Server": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.11.1", + "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.11.1", + "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.11.1", + "Newtonsoft.Json": "13.0.1" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "Scalar.AspNetCore": { + "type": "Transitive", + "resolved": "2.8.4", + "contentHash": "Ydmi6mtnIMa9cLTHxSdGy/CelCfaI6AvEOZ4nfAd6Tc6tE5uY9L7hU1M87gLRPf78+zCqBZMj0CI/rRa+uj7+g==" + }, + "Serilog": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "pzeDRXdpSLSsgBHpZcmpIDxqMy845Ab4s+dfnBg0sN9h8q/4Wo3vAoe0QCGPze1Q06EVtEPupS+UvLm8iXQmTQ==" + }, + "Serilog.AspNetCore": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "B/X+wAfS7yWLVOTD83B+Ip9yl4MkhioaXj90JSoWi1Ayi8XHepEnsBdrkojg08eodCnmOKmShFUN2GgEc6c0CQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "Serilog": "3.1.1", + "Serilog.Extensions.Hosting": "8.0.0", + "Serilog.Extensions.Logging": "8.0.0", + "Serilog.Formatting.Compact": "2.0.0", + "Serilog.Settings.Configuration": "8.0.0", + "Serilog.Sinks.Console": "5.0.0", + "Serilog.Sinks.Debug": "2.0.0", + "Serilog.Sinks.File": "5.0.0" + } + }, + "Serilog.Extensions.Hosting": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "db0OcbWeSCvYQkHWu6n0v40N4kKaTAXNjlM3BKvcbwvNzYphQFcBR+36eQ/7hMMwOkJvAyLC2a9/jNdUL5NjtQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Serilog": "3.1.1", + "Serilog.Extensions.Logging": "8.0.0" + } + }, + "Serilog.Extensions.Logging": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "YEAMWu1UnWgf1c1KP85l1SgXGfiVo0Rz6x08pCiPOIBt2Qe18tcZLvdBUuV5o1QHvrs8FAry9wTIhgBRtjIlEg==", + "dependencies": { + "Microsoft.Extensions.Logging": "8.0.0", + "Serilog": "3.1.1" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "ob6z3ikzFM3D1xalhFuBIK1IOWf+XrQq+H4KeH4VqBcPpNcmUgZlRQ2h3Q7wvthpdZBBoY86qZOI2LCXNaLlNA==", + "dependencies": { + "Serilog": "3.1.0" + } + }, + "Serilog.Settings.Configuration": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "nR0iL5HwKj5v6ULo3/zpP8NMcq9E2pxYA6XKTSWCbugVs4YqPyvaqaKOY+OMpPivKp7zMEpax2UKHnDodbRB0Q==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "8.0.0", + "Microsoft.Extensions.DependencyModel": "8.0.0", + "Serilog": "3.1.1" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "IZ6bn79k+3SRXOBpwSOClUHikSkp2toGPCZ0teUkscv4dpDg9E2R2xVsNkLmwddE4OpNVO3N0xiYsAH556vN8Q==", + "dependencies": { + "Serilog": "3.1.0" + } + }, + "Serilog.Sinks.Debug": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "Y6g3OBJ4JzTyyw16fDqtFcQ41qQAydnEvEqmXjhwhgjsnG/FaJ8GUqF5ldsC/bVkK8KYmqrPhDO+tm4dF6xx4A==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.11", + "contentHash": "DC4nA7yWnf4UZdgJDF+9Mus4/cb0Y3Sfgi3gDnAoKNAIBwzkskNAbNbyu+u4atT0ruVlZNJfwZmwiEwE5oz9LQ==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.11", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.11" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.11", + "contentHash": "PK0GLFkfhZzLQeR3PJf71FmhtHox+U3vcY6ZtswoMjrefkB9k6ErNJEnwXqc5KgXDSjige2XXrezqS39gkpQKA==" + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.11", + "contentHash": "Ev2ytaXiOlWZ4b3R67GZBsemTINslLD1DCJr2xiacpn4tbapu0Q4dHEzSvZSMnVWeE5nlObU3VZN2p81q3XOYQ==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.11", + "contentHash": "Y/0ZkR+r0Cg3DQFuCl1RBnv/tmxpIZRU3HUvelPw6MVaKHwYYR8YNvgs0vuNuXCMvlyJ+Fh88U1D4tah1tt6qw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.11" + } + }, + "Swashbuckle.AspNetCore": { + "type": "Transitive", + "resolved": "10.1.0", + "contentHash": "CvOffaJKStoP0hdfCIX4V/9wuwRkSOJd+PehGo/Y5ALh0WYliLwuMlyh1BbgG7uQtJNe1k5P7QIhIaFfZ/aJAw==", + "dependencies": { + "Microsoft.Extensions.ApiDescription.Server": "10.0.0", + "Swashbuckle.AspNetCore.Swagger": "10.1.0", + "Swashbuckle.AspNetCore.SwaggerGen": "10.1.0", + "Swashbuckle.AspNetCore.SwaggerUI": "10.1.0" + } + }, + "Swashbuckle.AspNetCore.Swagger": { + "type": "Transitive", + "resolved": "10.1.0", + "contentHash": "9Kp35Jhkzw73UXnvgGWVgRjvfGx5h1V4fdL9JcPZMKoTyO/bnKD/1i86n8u2p+rVTDed0cDSH4PKbX4WicZ/gg==", + "dependencies": { + "Microsoft.OpenApi": "2.3.0" + } + }, + "Swashbuckle.AspNetCore.SwaggerGen": { + "type": "Transitive", + "resolved": "10.1.0", + "contentHash": "XV2gyKmjWs5K5QaSq9rNYtO/E7vr/RcyBkMbbCVUlUtI5OY0HKj260Wee9zsJ7scJf7kPCxeseBiUMRp67ZWxA==", + "dependencies": { + "Swashbuckle.AspNetCore.Swagger": "10.1.0" + } + }, + "Swashbuckle.AspNetCore.SwaggerUI": { + "type": "Transitive", + "resolved": "10.1.0", + "contentHash": "ilUsTvGA9hO1ulR7ibdWMWSg3438Iu+pDFcEYUorp+/ClHwaHFdpp/ATfBsFXv2sIRVDbQlEwd5BWBOdMdtKCA==" + }, + "System.ClientModel": { + "type": "Transitive", + "resolved": "1.5.1", + "contentHash": "k2jKSO0X45IqhVOT9iQB4xralNN9foRQsRvXBTyRpAVxyzCJlG895T9qYrQWbcJ6OQXxOouJQ37x5nZH5XKK+A==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "System.Memory.Data": "8.0.1" + } + }, + "System.Configuration.ConfigurationManager": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "dvjqKp+2LpGid6phzrdrS/2mmEPxFl3jE1+L7614q4ZChKbLJCpHXg6sBILlCCED1t//EE+un/UdAetzIMpqnw==", + "dependencies": { + "System.Diagnostics.EventLog": "9.0.4", + "System.Security.Cryptography.ProtectedData": "9.0.4" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "getRQEXD8idlpb1KW56XuxImMy0FKp2WJPDf3Qr0kI/QKxxJSftqfDFVo0DZ3HCJRLU73qHSruv5q2l5O47jQQ==" + }, + "System.IdentityModel.Tokens.Jwt": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "rQkO1YbAjLwnDJSMpRhRtrc6XwIcEOcUvoEcge+evurpzSZM3UNK+MZfD3sKyTlYsvknZ6eJjSBfnmXqwOsT9Q==", + "dependencies": { + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "System.Memory.Data": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "BVYuec3jV23EMRDeR7Dr1/qhx7369dZzJ9IWy2xylvb4YfXsrUxspWc4UWYid/tj4zZK58uGZqn2WQiaDMhmAg==" + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "cUFTcMlz/Qw9s90b2wnWSCvHdjv51Bau9FQqhsr4TlwSe1OX+7SoXUqphis5G74MLOvMOCghxPPlEqOdCrVVGA==" + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "o94k2RKuAce3GeDMlUvIXlhVa1kWpJw95E6C9LwW0KlG0nj5+SgCiIxJ2Eroqb9sLtG1mEMbFttZIBZ13EJPvQ==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.16.0", + "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.9.2", + "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.9.2", + "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==", + "dependencies": { + "xunit.extensibility.core": "[2.9.2]", + "xunit.extensibility.execution": "[2.9.2]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.9.2", + "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.9.2", + "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==", + "dependencies": { + "xunit.extensibility.core": "[2.9.2]" + } + }, + "ecommerceapp.ryanw84": { + "type": "Project", + "dependencies": { + "FluentValidation.AspNetCore": "[11.3.1, )", + "Microsoft.AspNetCore.OpenApi": "[10.0.1, )", + "Microsoft.EntityFrameworkCore": "[10.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[10.0.0, )", + "Microsoft.EntityFrameworkCore.SqlServer": "[10.0.0, )", + "Microsoft.EntityFrameworkCore.Sqlite": "[10.0.0, )", + "Scalar.AspNetCore": "[2.8.4, )", + "Serilog": "[4.0.1, )", + "Serilog.AspNetCore": "[8.0.1, )", + "Swashbuckle.AspNetCore": "[10.1.0, )" + } + } + } + } +} \ No newline at end of file diff --git a/tests/ECommerceApp.IntegrationTests/ApiIntegrationTests.cs b/tests/ECommerceApp.IntegrationTests/ApiIntegrationTests.cs new file mode 100644 index 00000000..df1017ed --- /dev/null +++ b/tests/ECommerceApp.IntegrationTests/ApiIntegrationTests.cs @@ -0,0 +1,240 @@ +using System.Net; +using System.Net.Http.Json; +using ECommerceApp.RyanW84.Data; +using ECommerceApp.RyanW84.Data.DTO; +using ECommerceApp.RyanW84.Data.Models; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace ECommerceApp.IntegrationTests; + +public class ApiIntegrationTests +{ + private static HttpClient CreateHttpsClient(EcommerceApiFactory factory) => + factory.CreateClient( + new Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactoryClientOptions + { + BaseAddress = new Uri("https://localhost"), + AllowAutoRedirect = false, + } + ); + + private static async Task SeedAsync( + EcommerceApiFactory factory, + Action seed + ) + { + using var scope = factory.Services.CreateScope(); + var db = scope.ServiceProvider.GetRequiredService(); + await db.Database.EnsureCreatedAsync(); + seed(db); + await db.SaveChangesAsync(); + } + + private static Task EnsureDatabaseCreatedAsync(EcommerceApiFactory factory) => + SeedAsync(factory, _ => { }); + + [Fact] + public async Task GetCategories_PaginatesCorrectly() + { + await using var factory = new EcommerceApiFactory(); + var client = CreateHttpsClient(factory); + + await SeedAsync( + factory, + db => + { + for (var i = 1; i <= 25; i++) + { + db.Categories.Add( + new Category { Name = $"Category {i:00}", Description = "Test category" } + ); + } + } + ); + + var response = await client.GetAsync("/api/categories?page=1&pageSize=10"); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + + var dto = await response.Content.ReadFromJsonAsync>>(); + Assert.NotNull(dto); + Assert.False(dto!.RequestFailed); + Assert.NotNull(dto.Data); + Assert.Equal(10, dto.Data!.Count); + Assert.Equal(25, dto.TotalCount); + Assert.Equal(1, dto.CurrentPage); + Assert.Equal(10, dto.PageSize); + Assert.True(dto.HasNextPage); + Assert.False(dto.HasPreviousPage); + } + + [Fact] + public async Task GetProducts_Page2_ReturnsRemainingItems() + { + await using var factory = new EcommerceApiFactory(); + var client = CreateHttpsClient(factory); + + await SeedAsync( + factory, + db => + { + var cat = new Category { Name = "Cat", Description = "Cat desc" }; + db.Categories.Add(cat); + db.SaveChanges(); + + for (var i = 1; i <= 40; i++) + { + db.Products.Add( + new Product + { + Name = $"Product {i:00}", + Description = "Test product", + Price = 9.99m, + Stock = 10, + IsActive = true, + CategoryId = cat.CategoryId, + } + ); + } + } + ); + + var page1 = await client.GetFromJsonAsync>>( + "/api/product?page=1&pageSize=32" + ); + Assert.NotNull(page1); + Assert.False(page1!.RequestFailed); + Assert.NotNull(page1.Data); + Assert.Equal(32, page1.Data!.Count); + Assert.Equal(40, page1.TotalCount); + Assert.True(page1.HasNextPage); + + var page2 = await client.GetFromJsonAsync>>( + "/api/product?page=2&pageSize=32" + ); + Assert.NotNull(page2); + Assert.False(page2!.RequestFailed); + Assert.NotNull(page2.Data); + Assert.Equal(8, page2.Data!.Count); + Assert.Equal(40, page2.TotalCount); + Assert.False(page2.HasNextPage); + Assert.True(page2.HasPreviousPage); + + // Ensure page 2 starts after page 1 + Assert.Equal("Product 33", page2.Data![0].Name); + } + + [Fact] + public async Task CreateCategory_ThenGetById_Works() + { + await using var factory = new EcommerceApiFactory(); + var client = CreateHttpsClient(factory); + + await EnsureDatabaseCreatedAsync(factory); + + var createResponse = await client.PostAsJsonAsync( + "/api/categories", + new ApiRequestDto( + new Category { Name = "New Category", Description = "New category description" } + ) + ); + + Assert.Equal(HttpStatusCode.Created, createResponse.StatusCode); + + var created = await createResponse.Content.ReadFromJsonAsync>(); + Assert.NotNull(created); + Assert.False(created!.RequestFailed); + Assert.NotNull(created.Data); + Assert.True(created.Data!.CategoryId > 0); + + var getResponse = await client.GetAsync($"/api/categories/{created.Data.CategoryId}"); + Assert.Equal(HttpStatusCode.OK, getResponse.StatusCode); + + var fetched = await getResponse.Content.ReadFromJsonAsync>(); + Assert.NotNull(fetched); + Assert.False(fetched!.RequestFailed); + Assert.NotNull(fetched.Data); + Assert.Equal("New Category", fetched.Data!.Name); + } + + [Fact] + public async Task CreateProduct_ThenGetById_Works() + { + await using var factory = new EcommerceApiFactory(); + var client = CreateHttpsClient(factory); + + int categoryId = 0; + await SeedAsync( + factory, + db => + { + var cat = new Category { Name = "Cat", Description = "Cat desc" }; + db.Categories.Add(cat); + db.SaveChanges(); + categoryId = cat.CategoryId; + } + ); + + var createResponse = await client.PostAsJsonAsync( + "/api/product", + new ApiRequestDto( + new Product + { + Name = "Created Product", + Description = "Created product description", + Price = 19.99m, + Stock = 5, + IsActive = true, + CategoryId = categoryId, + } + ) + ); + + Assert.Equal(HttpStatusCode.Created, createResponse.StatusCode); + + var created = await createResponse.Content.ReadFromJsonAsync>(); + Assert.NotNull(created); + Assert.False(created!.RequestFailed); + Assert.NotNull(created.Data); + Assert.True(created.Data!.ProductId > 0); + + var getResponse = await client.GetAsync($"/api/product/{created.Data.ProductId}"); + Assert.Equal(HttpStatusCode.OK, getResponse.StatusCode); + + var fetched = await getResponse.Content.ReadFromJsonAsync>(); + Assert.NotNull(fetched); + Assert.False(fetched!.RequestFailed); + Assert.NotNull(fetched.Data); + Assert.Equal("Created Product", fetched.Data!.Name); + } + + [Fact] + public async Task DeleteCategory_SoftDeletes_AndIsExcludedFromList() + { + await using var factory = new EcommerceApiFactory(); + var client = CreateHttpsClient(factory); + + int categoryId = 0; + await SeedAsync( + factory, + db => + { + var cat = new Category { Name = "ToDelete", Description = "ToDelete desc" }; + db.Categories.Add(cat); + db.SaveChanges(); + categoryId = cat.CategoryId; + } + ); + + var deleteResponse = await client.DeleteAsync($"/api/categories/{categoryId}"); + Assert.Equal(HttpStatusCode.NoContent, deleteResponse.StatusCode); + + var listDto = await client.GetFromJsonAsync>>( + "/api/categories?page=1&pageSize=50" + ); + Assert.NotNull(listDto); + Assert.False(listDto!.RequestFailed); + Assert.NotNull(listDto.Data); + Assert.DoesNotContain(listDto.Data!, c => c.CategoryId == categoryId); + } +} diff --git a/tests/ECommerceApp.IntegrationTests/ECommerceApp.IntegrationTests.csproj b/tests/ECommerceApp.IntegrationTests/ECommerceApp.IntegrationTests.csproj new file mode 100644 index 00000000..7b39f027 --- /dev/null +++ b/tests/ECommerceApp.IntegrationTests/ECommerceApp.IntegrationTests.csproj @@ -0,0 +1,28 @@ + + + net10.0 + enable + enable + true + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + PreserveNewest + + + + + + + diff --git a/tests/ECommerceApp.IntegrationTests/EcommerceApiFactory.cs b/tests/ECommerceApp.IntegrationTests/EcommerceApiFactory.cs new file mode 100644 index 00000000..6159fe33 --- /dev/null +++ b/tests/ECommerceApp.IntegrationTests/EcommerceApiFactory.cs @@ -0,0 +1,58 @@ +using ECommerceApp.RyanW84.Data; +using ECommerceApp.RyanW84.Hosting; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Data.Sqlite; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace ECommerceApp.IntegrationTests; + +public sealed class EcommerceApiFactory : WebApplicationFactory +{ + private SqliteConnection? _connection; + + protected override void ConfigureWebHost(IWebHostBuilder builder) + { + builder.UseEnvironment("Development"); + + builder.ConfigureServices(services => + { + // Keep tests isolated and fast: swap DB for an in-memory SQLite connection + // and remove the migration/seeding hosted service. + _connection ??= new SqliteConnection("DataSource=:memory:"); + _connection.Open(); + + var hostedServiceDescriptors = services + .Where(d => + d.ServiceType == typeof(Microsoft.Extensions.Hosting.IHostedService) + && d.ImplementationType == typeof(DatabaseInitializerHostedService) + ) + .ToList(); + + foreach (var descriptor in hostedServiceDescriptors) + services.Remove(descriptor); + + services.RemoveAll>(); + + // Use DbContext (not pooling) for tests to avoid shared state issues + services.AddDbContext(options => + { + options.UseSqlite(_connection); + options.EnableSensitiveDataLogging(); + options.EnableDetailedErrors(); + }); + }); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + { + _connection?.Dispose(); + _connection = null; + } + } +} diff --git a/tests/ECommerceApp.IntegrationTests/packages.lock.json b/tests/ECommerceApp.IntegrationTests/packages.lock.json new file mode 100644 index 00000000..62f54119 --- /dev/null +++ b/tests/ECommerceApp.IntegrationTests/packages.lock.json @@ -0,0 +1,870 @@ +{ + "version": 1, + "dependencies": { + "net10.0": { + "Microsoft.AspNetCore.Mvc.Testing": { + "type": "Direct", + "requested": "[10.0.1, )", + "resolved": "10.0.1", + "contentHash": "84aSUoB++qrL9mlkAT1ybV9KQ5bv7sbpx2B5uo9se0ryYjNPIeiuknVy7r0FOwRk8T58PYybhIBa7WOkdMgOZQ==", + "dependencies": { + "Microsoft.AspNetCore.TestHost": "10.0.1", + "Microsoft.Extensions.DependencyModel": "10.0.1", + "Microsoft.Extensions.Hosting": "10.0.1" + } + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.11.1, )", + "resolved": "17.11.1", + "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==", + "dependencies": { + "Microsoft.CodeCoverage": "17.11.1", + "Microsoft.TestPlatform.TestHost": "17.11.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "MLBz2NQp3rtSIoJdjj3DBEr/EeOFlQYF3oCCljat3DY9GQ7yYmtjIAv8Zyfm5BcwYso5sjvIe5scuHaJPVCGIQ==", + "dependencies": { + "xunit.analyzers": "1.14.0", + "xunit.assert": "2.8.1", + "xunit.core": "[2.8.1]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "qBTK0WAcnw65mymIjVDqWUTdqjMyzjwu9e9SF0oGYfYELgbcteDZ4fQLJaXw8mzkvpAD7YdoexBbg8VYQFkWWA==" + }, + "Azure.Core": { + "type": "Transitive", + "resolved": "1.47.1", + "contentHash": "oPcncSsDHuxB8SC522z47xbp2+ttkcKv2YZ90KXhRKN0YQd2+7l1UURT9EBzUNEXtkLZUOAB5xbByMTrYRh3yA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "System.ClientModel": "1.5.1", + "System.Memory.Data": "8.0.1" + } + }, + "Azure.Identity": { + "type": "Transitive", + "resolved": "1.14.2", + "contentHash": "YhNMwOTwT+I2wIcJKSdP0ADyB2aK+JaYWZxO8LSRDm5w77LFr0ykR9xmt2ZV5T1gaI7xU6iNFIh/yW1dAlpddQ==", + "dependencies": { + "Azure.Core": "1.46.1", + "Microsoft.Identity.Client": "4.73.1", + "Microsoft.Identity.Client.Extensions.Msal": "4.73.1" + } + }, + "FluentValidation": { + "type": "Transitive", + "resolved": "11.11.0", + "contentHash": "cyIVdQBwSipxWG8MA3Rqox7iNbUNUTK5bfJi9tIdm4CAfH71Oo5ABLP4/QyrUwuakqpUEPGtE43BDddvEehuYw==" + }, + "FluentValidation.AspNetCore": { + "type": "Transitive", + "resolved": "11.3.1", + "contentHash": "FjkfrGGwC+25oH8QpX1ti3gxkFjDDtotkAQWyQz2CkwQwEfRKTANjhh77I79kY8dov2ydK1SY+e4jiOWsbbDdQ==", + "dependencies": { + "FluentValidation": "11.11.0", + "FluentValidation.DependencyInjectionExtensions": "11.11.0" + } + }, + "FluentValidation.DependencyInjectionExtensions": { + "type": "Transitive", + "resolved": "11.11.0", + "contentHash": "viTKWaMbL3yJYgGI0DiCeavNbE9UPMWFVK2XS9nYXGbm3NDMd0/L5ER4wBzmTtW3BYh3SrlSXm9RACiKZ6stlA==", + "dependencies": { + "FluentValidation": "11.11.0", + "Microsoft.Extensions.Dependencyinjection.Abstractions": "2.1.0" + } + }, + "Microsoft.AspNetCore.OpenApi": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "gMY53EggRIFawhue66GanHcm1Tcd0+QzzMwnMl60LrEoJhGgzA9qAbLx6t/ON3hX4flc2NcEbTK1Z5GCLYHcwA==", + "dependencies": { + "Microsoft.OpenApi": "2.0.0" + } + }, + "Microsoft.AspNetCore.TestHost": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Vvos4CyBam5dCsH3eD1c9MQI4ESWwzNSJsToFz4i6NmfPsaySzNSiv0QYRmSAAIBXb8GXxPmuy42TkIrw2xCzQ==" + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.Cryptography": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "YgZYAWzyNuPVtPq6WNm0bqOWNjYaWgl5mBWTGZyNoXitYBUYSp6iUB9AwK0V1mo793qRJUXz2t6UZrWITZSvuQ==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.11.1", + "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA==" + }, + "Microsoft.Data.SqlClient": { + "type": "Transitive", + "resolved": "6.1.1", + "contentHash": "syGQmIUPAYYHAHyTD8FCkTNThpQWvoA7crnIQRMfp8dyB5A2cWU3fQexlRTFkVmV7S0TjVmthi0LJEFVjHo8AQ==", + "dependencies": { + "Azure.Core": "1.47.1", + "Azure.Identity": "1.14.2", + "Microsoft.Bcl.Cryptography": "9.0.4", + "Microsoft.Data.SqlClient.SNI.runtime": "6.0.2", + "Microsoft.Extensions.Caching.Memory": "9.0.4", + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.7.1", + "Microsoft.SqlServer.Server": "1.0.0", + "System.Configuration.ConfigurationManager": "9.0.4", + "System.Security.Cryptography.Pkcs": "9.0.4" + } + }, + "Microsoft.Data.SqlClient.SNI.runtime": { + "type": "Transitive", + "resolved": "6.0.2", + "contentHash": "f+pRODTWX7Y67jXO3T5S2dIPZ9qMJNySjlZT/TKmWVNWe19N8jcWmHaqHnnchaq3gxEKv1SWVY5EFzOD06l41w==" + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "wPKG/Ym6tSMCo06UOZXzVfeFGzawnOZrTba/R3PfK+RhNuNELZ9I7nFns4WGTtv2kKlrlmmErgJ+kgTXHaNdHg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.11" + } + }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "hHa2amRjMyBLUH/KTML6FgIAhZ0VFYkhCKwWEax0rO6iNeM1P5MflyeQLE5dniSIOZHc3Oqyv5UIyTFO4e1Auw==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.0", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.0", + "Microsoft.Extensions.Caching.Memory": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "C+TT9k7f1GQ8agOfV512K9iwrzi76RXVSDiLx+iWC9pz3QhEpSF1Dyk+FpVvd8ULQ+rqymfM8KQ7g48ttQVyMg==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "TxHQq0kn0tpYs2ljeRl8jtmWk720B0nteqI6mAZM77HWJpYT9Zj8SkkBBlj8K3Yeq18a6NBjz6YutE+shEk4Ag==" + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "A3MX1ee7RDxWCUdx/KqP+74fbksz0UIhkVZh56YHvbPkEKsffCXgHU3LGkRDwqR/MrBNWLCWC/IVX79tzM30ZA==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "10.0.0", + "Microsoft.Extensions.Caching.Memory": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Sqlite": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "nukHe+yBlhitLUUtkanay7zTbHwtcIh/U5PfmwzZJJTCqui9h2Mt+Gifc9ZjJR7QIuE0zgNQQJaI8+eFxkBaEQ==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Sqlite.Core": "10.0.0", + "Microsoft.Extensions.Caching.Memory": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.DependencyModel": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.11", + "SQLitePCLRaw.core": "2.1.11" + } + }, + "Microsoft.EntityFrameworkCore.Sqlite.Core": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "VThKv9UqVxFEuuHvjAgMwy6ZFCeKJXOH+ISAR4IMuwlkozv26ptZhr09+6YxWrWwSR/Sinp8BxQ7qePCJFSALQ==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "10.0.0", + "Microsoft.EntityFrameworkCore.Relational": "10.0.0", + "Microsoft.Extensions.Caching.Memory": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.DependencyModel": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0", + "SQLitePCLRaw.core": "2.1.11" + } + }, + "Microsoft.EntityFrameworkCore.SqlServer": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "9ip+r8Wtu0qNtFLYnQC3bkhQAsINIyo8XWYJHCVY22BFLxIT4rPxZPkbto56SjCnkwxS6nnbFZTU6KuAO2Nu1g==", + "dependencies": { + "Microsoft.Data.SqlClient": "6.1.1", + "Microsoft.EntityFrameworkCore.Relational": "10.0.0", + "Microsoft.Extensions.Caching.Memory": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0" + } + }, + "Microsoft.Extensions.ApiDescription.Server": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "NCWCGiwRwje8773yzPQhvucYnnfeR+ZoB1VRIrIMp4uaeUNw7jvEPHij3HIbwCDuNCrNcphA00KSAR9yD9qmbg==" + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "Zcoy6H9mSoGyvr7UvlGokEZrlZkcPCICPZr8mCsSt9U/N8eeCwCXwKF5bShdA66R0obxBCwP4AxomQHvVkC/uA==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "krK19MKp0BNiR9rpBDW7PKSrTMLVlifS9am3CVc4O1Jq6GWz0o4F+sw5OSL4L3mVd56W8l6JRgghUa2KB51vOw==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging.Abstractions": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "njoRekyMIK+smav8B6KL2YgIfUtlsRNuT7wvurpLW+m/hoRKVnoELk2YxnUnWRGScCd1rukLMxShwLqEOKowDg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "kPlU11hql+L9RjrN2N9/0GcRcRcZrNFlLLjadasFWeBORT6pL6OE+RYRk90GGCyVGSxTK+e1/f3dsMj5zpFFiQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Lp4CZIuTVXtlvkAnTq6QvMSW7+H62gX2cU2vdFxHQUxvrWTpi7LwYI3X+YAyIS0r12/p7gaosco7efIxL4yFNw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.CommandLine": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "s5cxcdtIig66YT3J+7iHflMuorznK8kXuwBBPHMp4KImx5ZGE3FRa1Nj9fI/xMwFV+KzUMjqZ2MhOedPH8LiBQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "csD8Eps3HQ3yc0x6NhgTV+aIFKSs3qVlVCtFnMHz/JOjnv7eEj/qSXKXiK9LzBzB1qSfAVqFnB5iaX2nUmagIQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "N/6GiwiZFCBFZDk3vg1PhHW3zMqqu5WWpmeZAA9VTXv7Q8pr8NZR/EQsH0DjzqydDksJtY6EQBsu81d5okQOlA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Physical": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "0zW3eYBJlRctmgqk5s0kFIi5o5y2g80mvGCD8bkYxREPQlKUnr0ndU/Sop+UDIhyWN0fIi4RW63vo7BKTi7ncA==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.FileExtensions": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Configuration.UserSecrets": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "ULEJ0nkaW90JYJGkFujPcJtADXcJpXiSOLbokPcWJZ8iDbtDINifEYAUVqZVr81IDNTrRFul6O8RolOKOsgFPg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Json": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Physical": "10.0.1" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "zerXV0GAR9LCSXoSIApbWn+Dq1/T+6vbXMHGduq1LoVQRHT0BXsGQEau0jeLUBUcsoF/NaUT8ADPu8b+eNcIyg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "oIy8fQxxbUsSrrOvgBqlVgOeCtDmrcynnTG+FQufcUWBrwyPfwlUkCDB2vaiBeYPyT+20u9/HeuHeBf+H4F/8g==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "IiWPd4j8JLNjSkyXl5hvJwX2ZENDVQVPDHYgZmYdw8+YkY2xp9iQt0vjdnAQZLpo/ipeW1xgOqfSBEnivKWPYQ==" + }, + "Microsoft.Extensions.Diagnostics": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "YaocqxscJLxLit0F5yq2XyB+9C7rSRfeTL7MJIl7XwaOoUO3i0EqfO2kmtjiRduYWw7yjcSINEApYZbzjau2gQ==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1" + } + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "QMoMrkNpnQym5mpfdxfxpRDuqLpsOuztguFvzH9p+Ex+do+uLFoi7UkAsBO4e9/tNR3eMFraFf2fOAi2cp3jjA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "+b3DligYSZuoWltU5YdbMpIEUHNZPgPrzWfNiIuDkMdqOl93UxYB5KzS3lgpRfTXJhTNpo/CZ8w/sTkDTPDdxQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "4bxzGXIzZnz0Bf7czQ72jGvpOqJsRW/44PS7YLFXTTnu6cNcPvmSREDvBoH0ZVP2hAbMfL4sUoCUn54k70jPWw==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.FileSystemGlobbing": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "49dFvGJjLSwGn76eHnP1gBvCJkL8HRYpCrG0DCvsP6wRpEQRLN2Fq8rTxbP+6jS7jmYKCnSVO5C65v4mT3rzeA==" + }, + "Microsoft.Extensions.Hosting": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "0jjfjQSOFZlHhwOoIQw0WyzxtkDMYdnPY3iFrOLasxYq/5J4FDt1HWT8TktBclOVjFY1FOOkoOc99X7AhbqSIw==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.Configuration.CommandLine": "10.0.1", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "10.0.1", + "Microsoft.Extensions.Configuration.FileExtensions": "10.0.1", + "Microsoft.Extensions.Configuration.Json": "10.0.1", + "Microsoft.Extensions.Configuration.UserSecrets": "10.0.1", + "Microsoft.Extensions.DependencyInjection": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Diagnostics": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Physical": "10.0.1", + "Microsoft.Extensions.Hosting.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Configuration": "10.0.1", + "Microsoft.Extensions.Logging.Console": "10.0.1", + "Microsoft.Extensions.Logging.Debug": "10.0.1", + "Microsoft.Extensions.Logging.EventLog": "10.0.1", + "Microsoft.Extensions.Logging.EventSource": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "qmoQkVZcbm4/gFpted3W3Y+1kTATZTcUhV3mRkbtpfBXlxWCHwh/2oMffVcCruaGOfJuEnyAsGyaSUouSdECOw==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.1", + "Microsoft.Extensions.FileProviders.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "9ItMpMLFZFJFqCuHLLbR3LiA4ahA8dMtYuXpXl2YamSDWZhYS9BruPprkftY0tYi2bQ0slNrixdFm+4kpz1g5w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "YkmyiPIWAXVb+lPIrM0LE5bbtLOJkCiRTFiHpkVOvhI7uTvCfoOHLEN0LcsY56GpSD7NqX3gJNpsaDe87/B3zg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.Configuration": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Zg8LLnfZs5o2RCHD/+9NfDtJ40swauemwCa7sI8gQoAye/UJHRZNpCtC7a5XE7l9Z7mdI8iMWnLZ6m7Q6S3jLg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "10.0.1", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Options.ConfigurationExtensions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.Console": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "38Q8sEHwQ/+wVO/mwQBa0fcdHbezFpusHE+vBw/dSr6Fq/kzZm3H/NQX511Jki/R3FHd64IY559gdlHZQtYeEA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging.Configuration": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.Debug": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "VqfTvbX9C6BA0VeIlpzPlljnNsXxiI5CdUHb9ksWERH94WQ6ft3oLGUAa4xKcDGu4xF+rIZ8wj7IOAd6/q7vGw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.EventLog": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "Zp9MM+jFCa7oktIug62V9eNygpkf+6kFVatF+UC/ODeUwIr5givYKy8fYSSI9sWdxqDqv63y1x0mm2VjOl8GOw==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "System.Diagnostics.EventLog": "10.0.1" + } + }, + "Microsoft.Extensions.Logging.EventSource": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "WnFvZP+Y+lfeNFKPK/+mBpaCC7EeBDlobrQOqnP7rrw/+vE7yu8Rjczum1xbC0F/8cAHafog84DMp9200akMNQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Logging": "10.0.1", + "Microsoft.Extensions.Logging.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "G6VVwywpJI4XIobetGHwg7wDOYC2L2XBYdtskxLaKF/Ynb5QBwLl7Q//wxAR2aVCLkMpoQrjSP9VoORkyddsNQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "pL78/Im7O3WmxHzlKUsWTYchKL881udU7E26gCD3T0+/tPhWVfjPwMzfN/MRKU7aoFYcOiqcG2k1QTlH5woWow==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "10.0.1", + "Microsoft.Extensions.Configuration.Binder": "10.0.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1", + "Microsoft.Extensions.Options": "10.0.1", + "Microsoft.Extensions.Primitives": "10.0.1" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "DO8XrJkp5x4PddDuc/CH37yDBCs9BYN6ijlKyR3vMb55BP1Vwh90vOX8bNfnKxr5B2qEI3D8bvbY1fFbDveDHQ==" + }, + "Microsoft.Identity.Client": { + "type": "Transitive", + "resolved": "4.73.1", + "contentHash": "NnDLS8QwYqO5ZZecL2oioi1LUqjh5Ewk4bMLzbgiXJbQmZhDLtKwLxL3DpGMlQAJ2G4KgEnvGPKa+OOgffeJbw==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "6.35.0" + } + }, + "Microsoft.Identity.Client.Extensions.Msal": { + "type": "Transitive", + "resolved": "4.73.1", + "contentHash": "xDztAiV2F0wI0W8FLKv5cbaBefyLD6JVaAsvgSN7bjWNCzGYzHbcOEIP5s4TJXUpQzMfUyBsFl1mC6Zmgpz0PQ==", + "dependencies": { + "Microsoft.Identity.Client": "4.73.1", + "System.Security.Cryptography.ProtectedData": "4.5.0" + } + }, + "Microsoft.IdentityModel.Abstractions": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "S7sHg6gLg7oFqNGLwN1qSbJDI+QcRRj8SuJ1jHyCmKSipnF6ZQL+tFV2NzVfGj/xmGT9TykQdQiBN+p5Idl4TA==" + }, + "Microsoft.IdentityModel.JsonWebTokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "3Izi75UCUssvo8LPx3OVnEeZay58qaFicrtSnbtUt7q8qQi0gy46gh4V8VUTkMVMKXV6VMyjBVmeNNgeCUJuIw==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Logging": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "BZNgSq/o8gsKExdYoBKPR65fdsxW0cTF8PsdqB8y011AGUJJW300S/ZIsEUD0+sOmGc003Gwv3FYbjrVjvsLNQ==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "h+fHHBGokepmCX+QZXJk4Ij8OApCb2n2ktoDkNX5CXteXsOxTHMNgjPGpAwdJMFvAL7TtGarUnk3o97NmBq2QQ==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "yT2Hdj8LpPbcT9C9KlLVxXl09C8zjFaVSaApdOwuecMuoV4s6Sof/mnTDz/+F/lILPIBvrWugR9CC7iRVZgbfQ==", + "dependencies": { + "Microsoft.IdentityModel.Protocols": "7.7.1", + "System.IdentityModel.Tokens.Jwt": "7.7.1" + } + }, + "Microsoft.IdentityModel.Tokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "fQ0VVCba75lknUHGldi3iTKAYUQqbzp1Un8+d9cm9nON0Gs8NAkXddNg8iaUB0qi/ybtAmNWizTR4avdkCJ9pQ==", + "dependencies": { + "Microsoft.IdentityModel.Logging": "7.7.1" + } + }, + "Microsoft.OpenApi": { + "type": "Transitive", + "resolved": "2.3.0", + "contentHash": "5RZpjyt0JMmoc/aEgY9c1vE5pusdDGvkPl9qKIy9KFbRiIXD+w7gBJxX+unSjzzOcfgRoYxnO4okZyqDAL2WEw==" + }, + "Microsoft.SqlServer.Server": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.11.1", + "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.11.1", + "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.11.1", + "Newtonsoft.Json": "13.0.1" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "Scalar.AspNetCore": { + "type": "Transitive", + "resolved": "2.8.4", + "contentHash": "Ydmi6mtnIMa9cLTHxSdGy/CelCfaI6AvEOZ4nfAd6Tc6tE5uY9L7hU1M87gLRPf78+zCqBZMj0CI/rRa+uj7+g==" + }, + "Serilog": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "pzeDRXdpSLSsgBHpZcmpIDxqMy845Ab4s+dfnBg0sN9h8q/4Wo3vAoe0QCGPze1Q06EVtEPupS+UvLm8iXQmTQ==" + }, + "Serilog.AspNetCore": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "B/X+wAfS7yWLVOTD83B+Ip9yl4MkhioaXj90JSoWi1Ayi8XHepEnsBdrkojg08eodCnmOKmShFUN2GgEc6c0CQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "Serilog": "3.1.1", + "Serilog.Extensions.Hosting": "8.0.0", + "Serilog.Extensions.Logging": "8.0.0", + "Serilog.Formatting.Compact": "2.0.0", + "Serilog.Settings.Configuration": "8.0.0", + "Serilog.Sinks.Console": "5.0.0", + "Serilog.Sinks.Debug": "2.0.0", + "Serilog.Sinks.File": "5.0.0" + } + }, + "Serilog.Extensions.Hosting": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "db0OcbWeSCvYQkHWu6n0v40N4kKaTAXNjlM3BKvcbwvNzYphQFcBR+36eQ/7hMMwOkJvAyLC2a9/jNdUL5NjtQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Serilog": "3.1.1", + "Serilog.Extensions.Logging": "8.0.0" + } + }, + "Serilog.Extensions.Logging": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "YEAMWu1UnWgf1c1KP85l1SgXGfiVo0Rz6x08pCiPOIBt2Qe18tcZLvdBUuV5o1QHvrs8FAry9wTIhgBRtjIlEg==", + "dependencies": { + "Microsoft.Extensions.Logging": "8.0.0", + "Serilog": "3.1.1" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "ob6z3ikzFM3D1xalhFuBIK1IOWf+XrQq+H4KeH4VqBcPpNcmUgZlRQ2h3Q7wvthpdZBBoY86qZOI2LCXNaLlNA==", + "dependencies": { + "Serilog": "3.1.0" + } + }, + "Serilog.Settings.Configuration": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "nR0iL5HwKj5v6ULo3/zpP8NMcq9E2pxYA6XKTSWCbugVs4YqPyvaqaKOY+OMpPivKp7zMEpax2UKHnDodbRB0Q==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "8.0.0", + "Microsoft.Extensions.DependencyModel": "8.0.0", + "Serilog": "3.1.1" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "IZ6bn79k+3SRXOBpwSOClUHikSkp2toGPCZ0teUkscv4dpDg9E2R2xVsNkLmwddE4OpNVO3N0xiYsAH556vN8Q==", + "dependencies": { + "Serilog": "3.1.0" + } + }, + "Serilog.Sinks.Debug": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "Y6g3OBJ4JzTyyw16fDqtFcQ41qQAydnEvEqmXjhwhgjsnG/FaJ8GUqF5ldsC/bVkK8KYmqrPhDO+tm4dF6xx4A==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.11", + "contentHash": "DC4nA7yWnf4UZdgJDF+9Mus4/cb0Y3Sfgi3gDnAoKNAIBwzkskNAbNbyu+u4atT0ruVlZNJfwZmwiEwE5oz9LQ==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.11", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.11" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.11", + "contentHash": "PK0GLFkfhZzLQeR3PJf71FmhtHox+U3vcY6ZtswoMjrefkB9k6ErNJEnwXqc5KgXDSjige2XXrezqS39gkpQKA==" + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.11", + "contentHash": "Ev2ytaXiOlWZ4b3R67GZBsemTINslLD1DCJr2xiacpn4tbapu0Q4dHEzSvZSMnVWeE5nlObU3VZN2p81q3XOYQ==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.11", + "contentHash": "Y/0ZkR+r0Cg3DQFuCl1RBnv/tmxpIZRU3HUvelPw6MVaKHwYYR8YNvgs0vuNuXCMvlyJ+Fh88U1D4tah1tt6qw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.11" + } + }, + "Swashbuckle.AspNetCore": { + "type": "Transitive", + "resolved": "10.1.0", + "contentHash": "CvOffaJKStoP0hdfCIX4V/9wuwRkSOJd+PehGo/Y5ALh0WYliLwuMlyh1BbgG7uQtJNe1k5P7QIhIaFfZ/aJAw==", + "dependencies": { + "Microsoft.Extensions.ApiDescription.Server": "10.0.0", + "Swashbuckle.AspNetCore.Swagger": "10.1.0", + "Swashbuckle.AspNetCore.SwaggerGen": "10.1.0", + "Swashbuckle.AspNetCore.SwaggerUI": "10.1.0" + } + }, + "Swashbuckle.AspNetCore.Swagger": { + "type": "Transitive", + "resolved": "10.1.0", + "contentHash": "9Kp35Jhkzw73UXnvgGWVgRjvfGx5h1V4fdL9JcPZMKoTyO/bnKD/1i86n8u2p+rVTDed0cDSH4PKbX4WicZ/gg==", + "dependencies": { + "Microsoft.OpenApi": "2.3.0" + } + }, + "Swashbuckle.AspNetCore.SwaggerGen": { + "type": "Transitive", + "resolved": "10.1.0", + "contentHash": "XV2gyKmjWs5K5QaSq9rNYtO/E7vr/RcyBkMbbCVUlUtI5OY0HKj260Wee9zsJ7scJf7kPCxeseBiUMRp67ZWxA==", + "dependencies": { + "Swashbuckle.AspNetCore.Swagger": "10.1.0" + } + }, + "Swashbuckle.AspNetCore.SwaggerUI": { + "type": "Transitive", + "resolved": "10.1.0", + "contentHash": "ilUsTvGA9hO1ulR7ibdWMWSg3438Iu+pDFcEYUorp+/ClHwaHFdpp/ATfBsFXv2sIRVDbQlEwd5BWBOdMdtKCA==" + }, + "System.ClientModel": { + "type": "Transitive", + "resolved": "1.5.1", + "contentHash": "k2jKSO0X45IqhVOT9iQB4xralNN9foRQsRvXBTyRpAVxyzCJlG895T9qYrQWbcJ6OQXxOouJQ37x5nZH5XKK+A==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "System.Memory.Data": "8.0.1" + } + }, + "System.Configuration.ConfigurationManager": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "dvjqKp+2LpGid6phzrdrS/2mmEPxFl3jE1+L7614q4ZChKbLJCpHXg6sBILlCCED1t//EE+un/UdAetzIMpqnw==", + "dependencies": { + "System.Diagnostics.EventLog": "9.0.4", + "System.Security.Cryptography.ProtectedData": "9.0.4" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "xfaHEHVDkMOOZR5S6ZGezD0+vekdH1Nx/9Ih8/rOqOGSOk1fxiN3u94bYkBW/wigj0Uw2Wt3vvRj9mtYdgwEjw==" + }, + "System.IdentityModel.Tokens.Jwt": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "rQkO1YbAjLwnDJSMpRhRtrc6XwIcEOcUvoEcge+evurpzSZM3UNK+MZfD3sKyTlYsvknZ6eJjSBfnmXqwOsT9Q==", + "dependencies": { + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "System.Memory.Data": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "BVYuec3jV23EMRDeR7Dr1/qhx7369dZzJ9IWy2xylvb4YfXsrUxspWc4UWYid/tj4zZK58uGZqn2WQiaDMhmAg==" + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "cUFTcMlz/Qw9s90b2wnWSCvHdjv51Bau9FQqhsr4TlwSe1OX+7SoXUqphis5G74MLOvMOCghxPPlEqOdCrVVGA==" + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "o94k2RKuAce3GeDMlUvIXlhVa1kWpJw95E6C9LwW0KlG0nj5+SgCiIxJ2Eroqb9sLtG1mEMbFttZIBZ13EJPvQ==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "KcFBmV2150xZHPUebV3YLR5gGl8R4wLuPOoxMiwCf1L4bL8ls0dcwtGFzr6NvQRgg6dWgSqbE52I6SYyeB0VnQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.8.1", + "contentHash": "DDM18ur+PeNFhQ4w/vO+uvCUy8hA3OS5+AMf/CFov9Wco7Le49zzj0hovRWwa8f/3vaUfjL5r+IkPvqEHu2IIg==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.8.1", + "contentHash": "Ng4Q/DOwotESPl5CufcdqgP6O2KDpdEcIvNfA3upzfCiBrkj5WsmLhf/XUsCVolzvHA7b1WUlyeTo7j1ulG4gQ==", + "dependencies": { + "xunit.extensibility.core": "[2.8.1]", + "xunit.extensibility.execution": "[2.8.1]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.8.1", + "contentHash": "ilfAsxEhpne9AXXf3W+O65mRgGum94m2xHYm1yeJ1m7eiINM6OOwpaHhoNC/KWEQ2u/WF6/XiEs+Q0TOq7hiGA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.8.1", + "contentHash": "38UnJW+64Wn8QIabujcNEw0HKvWw2AlYCgU8GNwCCDqyrSuRYb7zwetn7SHoHfbL9e9FAvEiAMXmc2wSUY8sVQ==", + "dependencies": { + "xunit.extensibility.core": "[2.8.1]" + } + }, + "ecommerceapp.ryanw84": { + "type": "Project", + "dependencies": { + "FluentValidation.AspNetCore": "[11.3.1, )", + "Microsoft.AspNetCore.OpenApi": "[10.0.1, )", + "Microsoft.EntityFrameworkCore": "[10.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[10.0.0, )", + "Microsoft.EntityFrameworkCore.SqlServer": "[10.0.0, )", + "Microsoft.EntityFrameworkCore.Sqlite": "[10.0.0, )", + "Scalar.AspNetCore": "[2.8.4, )", + "Serilog": "[4.0.1, )", + "Serilog.AspNetCore": "[8.0.1, )", + "Swashbuckle.AspNetCore": "[10.1.0, )" + } + } + } + } +} \ No newline at end of file diff --git a/tests/ECommerceApp.IntegrationTests/xunit.runner.json b/tests/ECommerceApp.IntegrationTests/xunit.runner.json new file mode 100644 index 00000000..87a529d3 --- /dev/null +++ b/tests/ECommerceApp.IntegrationTests/xunit.runner.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "parallelizeAssembly": true, + "parallelizeTestCollections": true, + "maxParallelThreads": -1 +} \ No newline at end of file diff --git a/tests/ECommerceApp.UnitTests/CategoryValidatorTests.cs b/tests/ECommerceApp.UnitTests/CategoryValidatorTests.cs new file mode 100644 index 00000000..5230ae57 --- /dev/null +++ b/tests/ECommerceApp.UnitTests/CategoryValidatorTests.cs @@ -0,0 +1,48 @@ +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Validators; +using Xunit; + +namespace ECommerceApp.UnitTests.Validators; + +/// +/// Unit tests for CategoryValidator class +/// +public class CategoryValidatorTests +{ + private readonly CategoryValidator _validator = new(); + + [Fact] + public void Validate_WithValidCategory_ShouldSucceed() + { + // Arrange + var category = new Category + { + Name = "Electronics", + Description = "Electronic products" + }; + + // Act + var result = _validator.Validate(category); + + // Assert + Assert.True(result.IsValid); + } + + [Fact] + public void Validate_WithEmptyName_ShouldFail() + { + // Arrange + var category = new Category + { + Name = "", + Description = "Description" + }; + + // Act + var result = _validator.Validate(category); + + // Assert + Assert.False(result.IsValid); + Assert.Contains(result.Errors, e => e.PropertyName == "Name"); + } +} diff --git a/tests/ECommerceApp.UnitTests/ConsoleClient/SpectreConsoleCollection.cs b/tests/ECommerceApp.UnitTests/ConsoleClient/SpectreConsoleCollection.cs new file mode 100644 index 00000000..216a78a6 --- /dev/null +++ b/tests/ECommerceApp.UnitTests/ConsoleClient/SpectreConsoleCollection.cs @@ -0,0 +1,9 @@ +using Xunit; + +namespace ECommerceApp.UnitTests.ConsoleClient; + +[CollectionDefinition(Name, DisableParallelization = true)] +public class SpectreConsoleCollection +{ + public const string Name = "SpectreConsole"; +} diff --git a/tests/ECommerceApp.UnitTests/ConsoleClient/TableRendererDynamicPagingTests.cs b/tests/ECommerceApp.UnitTests/ConsoleClient/TableRendererDynamicPagingTests.cs new file mode 100644 index 00000000..22649f3d --- /dev/null +++ b/tests/ECommerceApp.UnitTests/ConsoleClient/TableRendererDynamicPagingTests.cs @@ -0,0 +1,179 @@ +using ECommerceApp.ConsoleClient.Utilities; +using Spectre.Console; +using Spectre.Console.Testing; +using Xunit; + +namespace ECommerceApp.UnitTests.ConsoleClient; + +[Collection(SpectreConsoleCollection.Name)] +public class TableRendererDynamicPagingTests +{ + private sealed record Item(int Id, string Name); + + private static TestConsole CreateInteractiveTestConsole() + { + var console = new TestConsole(); + console.Profile.Capabilities.Interactive = true; + return console; + } + + [Fact] + public async Task SelectFromPromptAsync_NextPage_ThenSelectItem_ReturnsItemFromSecondPage() + { + var items = Enumerable.Range(1, 40).Select(i => new Item(i, $"Item {i}")).ToList(); + const int pageSize = 32; + + Task> FetchPageAsync(int pageNum) + { + return Task.FromResult(items.Skip((pageNum - 1) * pageSize).Take(pageSize).ToList()); + } + + var originalConsole = AnsiConsole.Console; + var testConsole = CreateInteractiveTestConsole(); + AnsiConsole.Console = testConsole; + + try + { + // Page 1 choices: + // 32 items + "---" + "Next Page >" + "Cancel". + // Navigate to "Next Page >" (index 33) and select it. + for (var i = 0; i < 33; i++) + testConsole.Input.PushKey(ConsoleKey.DownArrow); + testConsole.Input.PushKey(ConsoleKey.Enter); + + // Page 2: select the 3rd item on the page (global item 35). + testConsole.Input.PushKey(ConsoleKey.DownArrow); + testConsole.Input.PushKey(ConsoleKey.DownArrow); + testConsole.Input.PushKey(ConsoleKey.Enter); + + var selected = await TableRenderer.SelectFromPromptAsync( + FetchPageAsync, + totalCount: items.Count, + pageSize: pageSize, + title: "Select an Item", + displayFormatter: x => x.Name + ); + + Assert.NotNull(selected); + Assert.Equal(35, selected!.Id); + } + finally + { + AnsiConsole.Console = originalConsole; + } + } + + [Fact] + public async Task SelectFromPromptAsync_Cancel_ReturnsNull() + { + var items = Enumerable.Range(1, 10).Select(i => new Item(i, $"Item {i}")).ToList(); + + Task> FetchPageAsync(int pageNum) => Task.FromResult(items); + + var originalConsole = AnsiConsole.Console; + var testConsole = CreateInteractiveTestConsole(); + AnsiConsole.Console = testConsole; + + try + { + // With 10 items, choices are: 10 items + "Cancel". + // Move down to "Cancel" and select it. + for (var i = 0; i < 10; i++) + testConsole.Input.PushKey(ConsoleKey.DownArrow); + testConsole.Input.PushKey(ConsoleKey.Enter); + + var selected = await TableRenderer.SelectFromPromptAsync( + FetchPageAsync, + totalCount: items.Count, + pageSize: 32, + title: "Select an Item", + displayFormatter: x => x.Name + ); + + Assert.Null(selected); + } + finally + { + AnsiConsole.Console = originalConsole; + } + } + + [Fact] + public async Task SelectFromPromptAsync_PreviousPageNavigation_Works() + { + var items = Enumerable.Range(1, 40).Select(i => new Item(i, $"Item {i}")).ToList(); + const int pageSize = 32; + + Task> FetchPageAsync(int pageNum) + { + return Task.FromResult(items.Skip((pageNum - 1) * pageSize).Take(pageSize).ToList()); + } + + var originalConsole = AnsiConsole.Console; + var testConsole = CreateInteractiveTestConsole(); + AnsiConsole.Console = testConsole; + + try + { + // Go to page 2 first. + for (var i = 0; i < 33; i++) + testConsole.Input.PushKey(ConsoleKey.DownArrow); + testConsole.Input.PushKey(ConsoleKey.Enter); + + // Page 2 choices: + // 8 items + "---" + "< Previous Page" + "Cancel". + // Navigate to "< Previous Page" (index 9) and select it. + for (var i = 0; i < 9; i++) + testConsole.Input.PushKey(ConsoleKey.DownArrow); + testConsole.Input.PushKey(ConsoleKey.Enter); + + // Back on page 1: select first item. + // Ensure we're at the first choice before selecting. + for (var i = 0; i < 50; i++) + testConsole.Input.PushKey(ConsoleKey.UpArrow); + testConsole.Input.PushKey(ConsoleKey.Enter); + + var selected = await TableRenderer.SelectFromPromptAsync( + FetchPageAsync, + totalCount: items.Count, + pageSize: pageSize, + title: "Select an Item", + displayFormatter: x => x.Name + ); + + Assert.NotNull(selected); + Assert.Equal(1, selected!.Id); + } + finally + { + AnsiConsole.Console = originalConsole; + } + } + + [Fact] + public async Task SelectFromPromptAsync_WhenFetchedPageIsEmpty_ReturnsNull() + { + Task> FetchPageAsync(int _) => Task.FromResult(new List()); + + var originalConsole = AnsiConsole.Console; + var testConsole = CreateInteractiveTestConsole(); + AnsiConsole.Console = testConsole; + + try + { + var selected = await TableRenderer.SelectFromPromptAsync( + FetchPageAsync, + totalCount: 10, + pageSize: 32, + title: "Select an Item", + displayFormatter: x => x.Name + ); + + Assert.Null(selected); + } + finally + { + AnsiConsole.Console = originalConsole; + } + } +} diff --git a/tests/ECommerceApp.UnitTests/ConsoleClient/TableRendererDynamicPagingTheoryTests.cs b/tests/ECommerceApp.UnitTests/ConsoleClient/TableRendererDynamicPagingTheoryTests.cs new file mode 100644 index 00000000..0ce51ab7 --- /dev/null +++ b/tests/ECommerceApp.UnitTests/ConsoleClient/TableRendererDynamicPagingTheoryTests.cs @@ -0,0 +1,337 @@ +using ECommerceApp.ConsoleClient.Utilities; +using Spectre.Console; +using Spectre.Console.Testing; +using Xunit; + +namespace ECommerceApp.UnitTests.ConsoleClient; + +[Collection(SpectreConsoleCollection.Name)] +public class TableRendererDynamicPagingTheoryTests +{ + private sealed record Item(int Id, string Name); + + private static TestConsole CreateInteractiveTestConsole() + { + var console = new TestConsole(); + console.Profile.Capabilities.Interactive = true; + return console; + } + + private static int TotalPages(int totalCount, int pageSize) => + (totalCount + pageSize - 1) / pageSize; + + private static int ItemsOnPage(int totalCount, int pageSize, int pageIndex) => + Math.Max(0, Math.Min(pageSize, totalCount - (pageIndex * pageSize))); + + private static int CancelIndex(int itemCountOnPage, bool hasPrev, bool hasNext, bool showPaging) + { + var extras = showPaging ? 1 : 0; // "---" + if (showPaging && hasPrev) + extras++; + if (showPaging && hasNext) + extras++; + return itemCountOnPage + extras; + } + + private static int NextIndex(int itemCountOnPage, bool hasPrev, bool showPaging) => + itemCountOnPage + 1 + (showPaging && hasPrev ? 1 : 0); + + private static int PreviousIndex(int itemCountOnPage) => itemCountOnPage + 1; // after "---" + + private static async Task<(Item? Selected, List PagesFetched)> RunSelectionAsync( + int totalCount, + int pageSize, + Action pushKeys, + Func>? pageProvider = null + ) + { + var items = Enumerable.Range(1, totalCount).Select(i => new Item(i, $"Item {i}")).ToList(); + var fetched = new List(); + + Task> FetchPageAsync(int pageNum) + { + fetched.Add(pageNum); + if (pageProvider != null) + return Task.FromResult(pageProvider(pageNum)); + + return Task.FromResult(items.Skip((pageNum - 1) * pageSize).Take(pageSize).ToList()); + } + + var originalConsole = AnsiConsole.Console; + var testConsole = CreateInteractiveTestConsole(); + AnsiConsole.Console = testConsole; + + try + { + pushKeys(testConsole); + + var selected = await TableRenderer.SelectFromPromptAsync( + FetchPageAsync, + totalCount: totalCount, + pageSize: pageSize, + title: "Select an Item", + displayFormatter: x => x.Name + ); + + return (selected, fetched); + } + finally + { + AnsiConsole.Console = originalConsole; + } + } + + // 15 test cases + [Theory] + [InlineData(1, 32)] + [InlineData(2, 32)] + [InlineData(10, 32)] + [InlineData(31, 32)] + [InlineData(32, 32)] + [InlineData(33, 32)] + [InlineData(40, 32)] + [InlineData(64, 32)] + [InlineData(65, 32)] + [InlineData(5, 5)] + [InlineData(6, 5)] + [InlineData(9, 5)] + [InlineData(10, 5)] + [InlineData(11, 5)] + [InlineData(100, 10)] + public async Task SelectFromPromptAsync_CancelAlways_ReturnsNull(int totalCount, int pageSize) + { + var totalPages = TotalPages(totalCount, pageSize); + var showPaging = totalPages > 1; + var itemCount = ItemsOnPage(totalCount, pageSize, pageIndex: 0); + + var cancelIndex = CancelIndex( + itemCount, + hasPrev: false, + hasNext: showPaging, + showPaging: showPaging + ); + + var (selected, pages) = await RunSelectionAsync( + totalCount, + pageSize, + console => + { + for (var i = 0; i < cancelIndex; i++) + console.Input.PushKey(ConsoleKey.DownArrow); + console.Input.PushKey(ConsoleKey.Enter); + } + ); + + Assert.Null(selected); + Assert.Equal(new[] { 1 }, pages); + } + + // 10 test cases + [Theory] + [InlineData(33, 32)] + [InlineData(40, 32)] + [InlineData(63, 32)] + [InlineData(64, 32)] + [InlineData(65, 32)] + [InlineData(6, 5)] + [InlineData(9, 5)] + [InlineData(11, 5)] + [InlineData(21, 10)] + [InlineData(101, 10)] + public async Task SelectFromPromptAsync_NextPageThenCancel_FetchesSecondPage( + int totalCount, + int pageSize + ) + { + var totalPages = TotalPages(totalCount, pageSize); + Assert.True(totalPages >= 2); + + var page1Items = ItemsOnPage(totalCount, pageSize, pageIndex: 0); + var nextIndex = NextIndex(page1Items, hasPrev: false, showPaging: true); + + var page2Items = ItemsOnPage(totalCount, pageSize, pageIndex: 1); + var page2CancelIndex = CancelIndex( + page2Items, + hasPrev: true, + hasNext: totalPages > 2, + showPaging: true + ); + + var (selected, pages) = await RunSelectionAsync( + totalCount, + pageSize, + console => + { + // select "Next Page >" + for (var i = 0; i < nextIndex; i++) + console.Input.PushKey(ConsoleKey.DownArrow); + console.Input.PushKey(ConsoleKey.Enter); + + // then cancel on page 2 + for (var i = 0; i < page2CancelIndex; i++) + console.Input.PushKey(ConsoleKey.DownArrow); + console.Input.PushKey(ConsoleKey.Enter); + } + ); + + Assert.Null(selected); + Assert.Equal(new[] { 1, 2 }, pages); + } + + // 15 test cases + [Theory] + // totalCount, pageSize, targetPageIndex (0-based), localIndexOnPage (0-based), expectedId + [InlineData(40, 32, 1, 0, 33)] + [InlineData(40, 32, 1, 7, 40)] + [InlineData(65, 32, 1, 0, 33)] + [InlineData(65, 32, 1, 31, 64)] + [InlineData(65, 32, 2, 0, 65)] + [InlineData(21, 10, 0, 0, 1)] + [InlineData(21, 10, 0, 9, 10)] + [InlineData(21, 10, 1, 0, 11)] + [InlineData(21, 10, 1, 9, 20)] + [InlineData(21, 10, 2, 0, 21)] + [InlineData(11, 5, 0, 4, 5)] + [InlineData(11, 5, 1, 0, 6)] + [InlineData(11, 5, 1, 4, 10)] + [InlineData(11, 5, 2, 0, 11)] + [InlineData(100, 10, 9, 9, 100)] + public async Task SelectFromPromptAsync_NavigateToPageAndSelect_ReturnsCorrectItem( + int totalCount, + int pageSize, + int targetPageIndex, + int localIndexOnPage, + int expectedId + ) + { + var totalPages = TotalPages(totalCount, pageSize); + Assert.InRange(targetPageIndex, 0, totalPages - 1); + + var (selected, pages) = await RunSelectionAsync( + totalCount, + pageSize, + console => + { + // Navigate to target page by selecting "Next Page >" repeatedly. + for ( + var currentPageIndex = 0; + currentPageIndex < targetPageIndex; + currentPageIndex++ + ) + { + var count = ItemsOnPage(totalCount, pageSize, currentPageIndex); + var nextIndex = NextIndex( + count, + hasPrev: currentPageIndex > 0, + showPaging: totalPages > 1 + ); + + for (var i = 0; i < nextIndex; i++) + console.Input.PushKey(ConsoleKey.DownArrow); + console.Input.PushKey(ConsoleKey.Enter); + } + + // Select the desired item on the target page. + for (var i = 0; i < localIndexOnPage; i++) + console.Input.PushKey(ConsoleKey.DownArrow); + console.Input.PushKey(ConsoleKey.Enter); + } + ); + + Assert.NotNull(selected); + Assert.Equal(expectedId, selected!.Id); + + // Sanity: pages fetched should start at 1 and include target page. + Assert.True(pages.Count >= 1); + Assert.Equal(1, pages[0]); + Assert.Contains(targetPageIndex + 1, pages); + } + + // 10 test cases + [Theory] + // totalCount, pageSize, selectLocalIndexOnPage1, expectedId + [InlineData(40, 32, 0, 1)] + [InlineData(40, 32, 5, 6)] + [InlineData(65, 32, 10, 11)] + [InlineData(65, 32, 31, 32)] + [InlineData(21, 10, 0, 1)] + [InlineData(21, 10, 9, 10)] + [InlineData(11, 5, 0, 1)] + [InlineData(11, 5, 4, 5)] + [InlineData(101, 10, 0, 1)] + [InlineData(101, 10, 9, 10)] + public async Task SelectFromPromptAsync_GoToPage2ThenPrevious_ThenSelectOnPage1_ReturnsCorrectItem( + int totalCount, + int pageSize, + int selectLocalIndexOnPage1, + int expectedId + ) + { + var totalPages = TotalPages(totalCount, pageSize); + Assert.True(totalPages >= 2); + + var (selected, pages) = await RunSelectionAsync( + totalCount, + pageSize, + console => + { + // Go to page 2 + var page1Items = ItemsOnPage(totalCount, pageSize, 0); + var nextIndex = NextIndex(page1Items, hasPrev: false, showPaging: true); + for (var i = 0; i < nextIndex; i++) + console.Input.PushKey(ConsoleKey.DownArrow); + console.Input.PushKey(ConsoleKey.Enter); + + // Back to page 1 via "< Previous Page" + var page2Items = ItemsOnPage(totalCount, pageSize, 1); + var prevIndex = PreviousIndex(page2Items); + for (var i = 0; i < prevIndex; i++) + console.Input.PushKey(ConsoleKey.DownArrow); + console.Input.PushKey(ConsoleKey.Enter); + + // Select an item on page 1 + for (var i = 0; i < selectLocalIndexOnPage1; i++) + console.Input.PushKey(ConsoleKey.DownArrow); + console.Input.PushKey(ConsoleKey.Enter); + } + ); + + Assert.NotNull(selected); + Assert.Equal(expectedId, selected!.Id); + + // Should have fetched page 1, then 2, then 1 again. + Assert.Contains(1, pages); + Assert.Contains(2, pages); + Assert.True(pages.Count >= 3); + } + + // Bonus correctness checks that also count as additional cases, if totalCount==0. + // (Kept as a single Fact to avoid altering the 50-case accounting above.) + [Fact] + public async Task SelectFromPromptAsync_TotalCountZero_ReturnsNullWithoutFetching() + { + var (selected, pages) = await RunSelectionAsync( + totalCount: 0, + pageSize: 10, + pushKeys: _ => { }, + pageProvider: _ => throw new InvalidOperationException("Should not fetch") + ); + + Assert.Null(selected); + Assert.Empty(pages); + } + + [Fact] + public async Task SelectFromPromptAsync_WhenProviderReturnsEmptyForCurrentPage_ReturnsNull() + { + var (selected, pages) = await RunSelectionAsync( + totalCount: 50, + pageSize: 10, + pushKeys: _ => { }, + pageProvider: _ => new List() + ); + + Assert.Null(selected); + Assert.Equal(new[] { 1 }, pages); + } +} diff --git a/tests/ECommerceApp.UnitTests/ECommerceApp.UnitTests.csproj b/tests/ECommerceApp.UnitTests/ECommerceApp.UnitTests.csproj new file mode 100644 index 00000000..2be1d2f4 --- /dev/null +++ b/tests/ECommerceApp.UnitTests/ECommerceApp.UnitTests.csproj @@ -0,0 +1,30 @@ + + + net10.0 + enable + enable + true + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + PreserveNewest + + + + + + + + diff --git a/tests/ECommerceApp.UnitTests/ProductValidatorTests.cs b/tests/ECommerceApp.UnitTests/ProductValidatorTests.cs new file mode 100644 index 00000000..40ab0b38 --- /dev/null +++ b/tests/ECommerceApp.UnitTests/ProductValidatorTests.cs @@ -0,0 +1,78 @@ +using ECommerceApp.RyanW84.Data.Models; +using ECommerceApp.RyanW84.Validators; +using Xunit; + +namespace ECommerceApp.UnitTests.Validators; + +/// +/// Unit tests for ProductValidator class +/// +public class ProductValidatorTests +{ + private readonly ProductValidator _validator = new(); + + [Fact] + public void Validate_WithValidProduct_ShouldSucceed() + { + // Arrange + var product = new Product + { + Name = "Test Product", + Description = "A valid product description", + Price = 99.99m, + Stock = 10, + IsActive = true, + CategoryId = 1 + }; + + // Act + var result = _validator.Validate(product); + + // Assert + Assert.True(result.IsValid); + } + + [Fact] + public void Validate_WithEmptyName_ShouldFail() + { + // Arrange + var product = new Product + { + Name = "", + Description = "Description", + Price = 10m, + Stock = 5, + IsActive = true, + CategoryId = 1 + }; + + // Act + var result = _validator.Validate(product); + + // Assert + Assert.False(result.IsValid); + Assert.Contains(result.Errors, e => e.PropertyName == "Name"); + } + + [Fact] + public void Validate_WithNegativePrice_ShouldFail() + { + // Arrange + var product = new Product + { + Name = "Product", + Description = "Description", + Price = -10m, + Stock = 5, + IsActive = true, + CategoryId = 1 + }; + + // Act + var result = _validator.Validate(product); + + // Assert + Assert.False(result.IsValid); + Assert.Contains(result.Errors, e => e.PropertyName == "Price"); + } +} diff --git a/tests/ECommerceApp.UnitTests/packages.lock.json b/tests/ECommerceApp.UnitTests/packages.lock.json new file mode 100644 index 00000000..6963020a --- /dev/null +++ b/tests/ECommerceApp.UnitTests/packages.lock.json @@ -0,0 +1,711 @@ +{ + "version": 1, + "dependencies": { + "net10.0": { + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[17.11.1, )", + "resolved": "17.11.1", + "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==", + "dependencies": { + "Microsoft.CodeCoverage": "17.11.1", + "Microsoft.TestPlatform.TestHost": "17.11.1" + } + }, + "Moq": { + "type": "Direct", + "requested": "[4.20.70, )", + "resolved": "4.20.70", + "contentHash": "4rNnAwdpXJBuxqrOCzCyICXHSImOTRktCgCWXWykuF1qwoIsVvEnR7PjbMk/eLOxWvhmj5Kwt+kDV3RGUYcNwg==", + "dependencies": { + "Castle.Core": "5.1.1" + } + }, + "Spectre.Console.Testing": { + "type": "Direct", + "requested": "[0.49.1, )", + "resolved": "0.49.1", + "contentHash": "i31qLh8VAxCVbTYnUT20d5KcizWz3ka81x4Ay4mrO2dgJT0YbpX2AD1CD8jBorpuDxTv0e7pNNSemFEfLgrMmg==", + "dependencies": { + "Spectre.Console": "0.49.1", + "Spectre.Console.Cli": "0.49.1" + } + }, + "xunit": { + "type": "Direct", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "MLBz2NQp3rtSIoJdjj3DBEr/EeOFlQYF3oCCljat3DY9GQ7yYmtjIAv8Zyfm5BcwYso5sjvIe5scuHaJPVCGIQ==", + "dependencies": { + "xunit.analyzers": "1.14.0", + "xunit.assert": "2.8.1", + "xunit.core": "[2.8.1]" + } + }, + "xunit.runner.visualstudio": { + "type": "Direct", + "requested": "[2.8.1, )", + "resolved": "2.8.1", + "contentHash": "qBTK0WAcnw65mymIjVDqWUTdqjMyzjwu9e9SF0oGYfYELgbcteDZ4fQLJaXw8mzkvpAD7YdoexBbg8VYQFkWWA==" + }, + "Azure.Core": { + "type": "Transitive", + "resolved": "1.47.1", + "contentHash": "oPcncSsDHuxB8SC522z47xbp2+ttkcKv2YZ90KXhRKN0YQd2+7l1UURT9EBzUNEXtkLZUOAB5xbByMTrYRh3yA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "8.0.0", + "System.ClientModel": "1.5.1", + "System.Memory.Data": "8.0.1" + } + }, + "Azure.Identity": { + "type": "Transitive", + "resolved": "1.14.2", + "contentHash": "YhNMwOTwT+I2wIcJKSdP0ADyB2aK+JaYWZxO8LSRDm5w77LFr0ykR9xmt2ZV5T1gaI7xU6iNFIh/yW1dAlpddQ==", + "dependencies": { + "Azure.Core": "1.46.1", + "Microsoft.Identity.Client": "4.73.1", + "Microsoft.Identity.Client.Extensions.Msal": "4.73.1" + } + }, + "Castle.Core": { + "type": "Transitive", + "resolved": "5.1.1", + "contentHash": "rpYtIczkzGpf+EkZgDr9CClTdemhsrwA/W5hMoPjLkRFnXzH44zDLoovXeKtmxb1ykXK9aJVODSpiJml8CTw2g==", + "dependencies": { + "System.Diagnostics.EventLog": "6.0.0" + } + }, + "FluentValidation": { + "type": "Transitive", + "resolved": "11.11.0", + "contentHash": "cyIVdQBwSipxWG8MA3Rqox7iNbUNUTK5bfJi9tIdm4CAfH71Oo5ABLP4/QyrUwuakqpUEPGtE43BDddvEehuYw==" + }, + "FluentValidation.AspNetCore": { + "type": "Transitive", + "resolved": "11.3.1", + "contentHash": "FjkfrGGwC+25oH8QpX1ti3gxkFjDDtotkAQWyQz2CkwQwEfRKTANjhh77I79kY8dov2ydK1SY+e4jiOWsbbDdQ==", + "dependencies": { + "FluentValidation": "11.11.0", + "FluentValidation.DependencyInjectionExtensions": "11.11.0" + } + }, + "FluentValidation.DependencyInjectionExtensions": { + "type": "Transitive", + "resolved": "11.11.0", + "contentHash": "viTKWaMbL3yJYgGI0DiCeavNbE9UPMWFVK2XS9nYXGbm3NDMd0/L5ER4wBzmTtW3BYh3SrlSXm9RACiKZ6stlA==", + "dependencies": { + "FluentValidation": "11.11.0", + "Microsoft.Extensions.Dependencyinjection.Abstractions": "2.1.0" + } + }, + "Microsoft.AspNetCore.OpenApi": { + "type": "Transitive", + "resolved": "10.0.1", + "contentHash": "gMY53EggRIFawhue66GanHcm1Tcd0+QzzMwnMl60LrEoJhGgzA9qAbLx6t/ON3hX4flc2NcEbTK1Z5GCLYHcwA==", + "dependencies": { + "Microsoft.OpenApi": "2.0.0" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==" + }, + "Microsoft.Bcl.Cryptography": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "YgZYAWzyNuPVtPq6WNm0bqOWNjYaWgl5mBWTGZyNoXitYBUYSp6iUB9AwK0V1mo793qRJUXz2t6UZrWITZSvuQ==" + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.11.1", + "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA==" + }, + "Microsoft.Data.SqlClient": { + "type": "Transitive", + "resolved": "6.1.1", + "contentHash": "syGQmIUPAYYHAHyTD8FCkTNThpQWvoA7crnIQRMfp8dyB5A2cWU3fQexlRTFkVmV7S0TjVmthi0LJEFVjHo8AQ==", + "dependencies": { + "Azure.Core": "1.47.1", + "Azure.Identity": "1.14.2", + "Microsoft.Bcl.Cryptography": "9.0.4", + "Microsoft.Data.SqlClient.SNI.runtime": "6.0.2", + "Microsoft.Extensions.Caching.Memory": "9.0.4", + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.7.1", + "Microsoft.SqlServer.Server": "1.0.0", + "System.Configuration.ConfigurationManager": "9.0.4", + "System.Security.Cryptography.Pkcs": "9.0.4" + } + }, + "Microsoft.Data.SqlClient.SNI.runtime": { + "type": "Transitive", + "resolved": "6.0.2", + "contentHash": "f+pRODTWX7Y67jXO3T5S2dIPZ9qMJNySjlZT/TKmWVNWe19N8jcWmHaqHnnchaq3gxEKv1SWVY5EFzOD06l41w==" + }, + "Microsoft.Data.Sqlite.Core": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "wPKG/Ym6tSMCo06UOZXzVfeFGzawnOZrTba/R3PfK+RhNuNELZ9I7nFns4WGTtv2kKlrlmmErgJ+kgTXHaNdHg==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.11" + } + }, + "Microsoft.EntityFrameworkCore": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "hHa2amRjMyBLUH/KTML6FgIAhZ0VFYkhCKwWEax0rO6iNeM1P5MflyeQLE5dniSIOZHc3Oqyv5UIyTFO4e1Auw==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Abstractions": "10.0.0", + "Microsoft.EntityFrameworkCore.Analyzers": "10.0.0", + "Microsoft.Extensions.Caching.Memory": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "C+TT9k7f1GQ8agOfV512K9iwrzi76RXVSDiLx+iWC9pz3QhEpSF1Dyk+FpVvd8ULQ+rqymfM8KQ7g48ttQVyMg==" + }, + "Microsoft.EntityFrameworkCore.Analyzers": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "TxHQq0kn0tpYs2ljeRl8jtmWk720B0nteqI6mAZM77HWJpYT9Zj8SkkBBlj8K3Yeq18a6NBjz6YutE+shEk4Ag==" + }, + "Microsoft.EntityFrameworkCore.Relational": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "A3MX1ee7RDxWCUdx/KqP+74fbksz0UIhkVZh56YHvbPkEKsffCXgHU3LGkRDwqR/MrBNWLCWC/IVX79tzM30ZA==", + "dependencies": { + "Microsoft.EntityFrameworkCore": "10.0.0", + "Microsoft.Extensions.Caching.Memory": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0" + } + }, + "Microsoft.EntityFrameworkCore.Sqlite": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "nukHe+yBlhitLUUtkanay7zTbHwtcIh/U5PfmwzZJJTCqui9h2Mt+Gifc9ZjJR7QIuE0zgNQQJaI8+eFxkBaEQ==", + "dependencies": { + "Microsoft.EntityFrameworkCore.Sqlite.Core": "10.0.0", + "Microsoft.Extensions.Caching.Memory": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.DependencyModel": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0", + "SQLitePCLRaw.bundle_e_sqlite3": "2.1.11", + "SQLitePCLRaw.core": "2.1.11" + } + }, + "Microsoft.EntityFrameworkCore.Sqlite.Core": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "VThKv9UqVxFEuuHvjAgMwy6ZFCeKJXOH+ISAR4IMuwlkozv26ptZhr09+6YxWrWwSR/Sinp8BxQ7qePCJFSALQ==", + "dependencies": { + "Microsoft.Data.Sqlite.Core": "10.0.0", + "Microsoft.EntityFrameworkCore.Relational": "10.0.0", + "Microsoft.Extensions.Caching.Memory": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.DependencyModel": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0", + "SQLitePCLRaw.core": "2.1.11" + } + }, + "Microsoft.EntityFrameworkCore.SqlServer": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "9ip+r8Wtu0qNtFLYnQC3bkhQAsINIyo8XWYJHCVY22BFLxIT4rPxZPkbto56SjCnkwxS6nnbFZTU6KuAO2Nu1g==", + "dependencies": { + "Microsoft.Data.SqlClient": "6.1.1", + "Microsoft.EntityFrameworkCore.Relational": "10.0.0", + "Microsoft.Extensions.Caching.Memory": "10.0.0", + "Microsoft.Extensions.Configuration.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging": "10.0.0" + } + }, + "Microsoft.Extensions.ApiDescription.Server": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "NCWCGiwRwje8773yzPQhvucYnnfeR+ZoB1VRIrIMp4uaeUNw7jvEPHij3HIbwCDuNCrNcphA00KSAR9yD9qmbg==" + }, + "Microsoft.Extensions.Caching.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "Zcoy6H9mSoGyvr7UvlGokEZrlZkcPCICPZr8mCsSt9U/N8eeCwCXwKF5bShdA66R0obxBCwP4AxomQHvVkC/uA==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Caching.Memory": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "krK19MKp0BNiR9rpBDW7PKSrTMLVlifS9am3CVc4O1Jq6GWz0o4F+sw5OSL4L3mVd56W8l6JRgghUa2KB51vOw==", + "dependencies": { + "Microsoft.Extensions.Caching.Abstractions": "10.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Logging.Abstractions": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0", + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "d2kDKnCsJvY7mBVhcjPSp9BkJk48DsaHPg5u+Oy4f8XaOqnEedRy/USyvnpHL92wpJ6DrTPy7htppUUzskbCXQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "mBMoXLsr5s1y2zOHWmKsE9veDcx8h1x/c3rz4baEdQKTeDcmQAPNbB54Pi/lhFO3K431eEq6PFbMgLaa6PHFfA==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "f0RBabswJq+gRu5a+hWIobrLWiUYPKMhCD9WO3sYBAdSy3FFH14LMvLVFZc2kPSCimBLxSuitUhsd6tb0TAY6A==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "L3AdmZ1WOK4XXT5YFPEwyt0ep6l8lGIPs7F5OOBZc77Zqeo01Of7XXICy47628sdVl0v/owxYJTe86DTgFwKCA==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "RFYJR7APio/BiqdQunRq6DB+nDB6nc2qhHr77mlvZ0q0BT8PubMXN7XicmfzCbrDE/dzhBnUKBRXLTcqUiZDGg==" + }, + "Microsoft.Extensions.Diagnostics.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "JHYCQG7HmugNYUhOl368g+NMxYE/N/AiclCYRNlgCY9eVyiBkOHMwK4x60RYMxv9EL3+rmj1mqHvdCiPpC+D4Q==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Options": "8.0.0" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "8.0.0" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "AG7HWwVRdCHlaA++1oKDxLsXIBxmDpMPb3VoyOoAghEWnkUvEAdYQUwnV4jJbAaa/nMYNiEh5ByoLauZBEiovg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "8.0.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Diagnostics.Abstractions": "8.0.0", + "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "BStFkd5CcnEtarlcgYDBcFzGYCuuNMzPs02wN3WBsOFoYIEmYoUdAiU+au6opzoqfTYJsMTW00AeqDdnXH2CvA==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "10.0.0", + "Microsoft.Extensions.Logging.Abstractions": "10.0.0", + "Microsoft.Extensions.Options": "10.0.0" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "FU/IfjDfwaMuKr414SSQNTIti/69bHEMb+QKrskRb26oVqpx3lNFXMjs/RC9ZUuhBhcwDM2BwOgoMw+PZ+beqQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0" + } + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "8oCAgXOow5XDrY9HaXX1QmH3ORsyZO/ANVHBlhLyCeWTH5Sg4UuqZeOTWJi6484M+LqSx0RqQXDJtdYy2BNiLQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0", + "Microsoft.Extensions.Primitives": "10.0.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "10.0.0", + "contentHash": "inRnbpCS0nwO/RuoZIAqxQUuyjaknOOnCEZB55KSMMjRhl0RQDttSmLSGsUJN3RQ3ocf5NDLFd2mOQViHqMK5w==" + }, + "Microsoft.Identity.Client": { + "type": "Transitive", + "resolved": "4.73.1", + "contentHash": "NnDLS8QwYqO5ZZecL2oioi1LUqjh5Ewk4bMLzbgiXJbQmZhDLtKwLxL3DpGMlQAJ2G4KgEnvGPKa+OOgffeJbw==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "6.35.0" + } + }, + "Microsoft.Identity.Client.Extensions.Msal": { + "type": "Transitive", + "resolved": "4.73.1", + "contentHash": "xDztAiV2F0wI0W8FLKv5cbaBefyLD6JVaAsvgSN7bjWNCzGYzHbcOEIP5s4TJXUpQzMfUyBsFl1mC6Zmgpz0PQ==", + "dependencies": { + "Microsoft.Identity.Client": "4.73.1", + "System.Security.Cryptography.ProtectedData": "4.5.0" + } + }, + "Microsoft.IdentityModel.Abstractions": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "S7sHg6gLg7oFqNGLwN1qSbJDI+QcRRj8SuJ1jHyCmKSipnF6ZQL+tFV2NzVfGj/xmGT9TykQdQiBN+p5Idl4TA==" + }, + "Microsoft.IdentityModel.JsonWebTokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "3Izi75UCUssvo8LPx3OVnEeZay58qaFicrtSnbtUt7q8qQi0gy46gh4V8VUTkMVMKXV6VMyjBVmeNNgeCUJuIw==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Logging": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "BZNgSq/o8gsKExdYoBKPR65fdsxW0cTF8PsdqB8y011AGUJJW300S/ZIsEUD0+sOmGc003Gwv3FYbjrVjvsLNQ==", + "dependencies": { + "Microsoft.IdentityModel.Abstractions": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "h+fHHBGokepmCX+QZXJk4Ij8OApCb2n2ktoDkNX5CXteXsOxTHMNgjPGpAwdJMFvAL7TtGarUnk3o97NmBq2QQ==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "yT2Hdj8LpPbcT9C9KlLVxXl09C8zjFaVSaApdOwuecMuoV4s6Sof/mnTDz/+F/lILPIBvrWugR9CC7iRVZgbfQ==", + "dependencies": { + "Microsoft.IdentityModel.Protocols": "7.7.1", + "System.IdentityModel.Tokens.Jwt": "7.7.1" + } + }, + "Microsoft.IdentityModel.Tokens": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "fQ0VVCba75lknUHGldi3iTKAYUQqbzp1Un8+d9cm9nON0Gs8NAkXddNg8iaUB0qi/ybtAmNWizTR4avdkCJ9pQ==", + "dependencies": { + "Microsoft.IdentityModel.Logging": "7.7.1" + } + }, + "Microsoft.OpenApi": { + "type": "Transitive", + "resolved": "2.3.0", + "contentHash": "5RZpjyt0JMmoc/aEgY9c1vE5pusdDGvkPl9qKIy9KFbRiIXD+w7gBJxX+unSjzzOcfgRoYxnO4okZyqDAL2WEw==" + }, + "Microsoft.SqlServer.Server": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "N4KeF3cpcm1PUHym1RmakkzfkEv3GRMyofVv40uXsQhCQeglr2OHNcUk2WOG51AKpGO8ynGpo9M/kFXSzghwug==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.11.1", + "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==" + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.11.1", + "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.11.1", + "Newtonsoft.Json": "13.0.1" + } + }, + "Newtonsoft.Json": { + "type": "Transitive", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "Scalar.AspNetCore": { + "type": "Transitive", + "resolved": "2.8.4", + "contentHash": "Ydmi6mtnIMa9cLTHxSdGy/CelCfaI6AvEOZ4nfAd6Tc6tE5uY9L7hU1M87gLRPf78+zCqBZMj0CI/rRa+uj7+g==" + }, + "Serilog": { + "type": "Transitive", + "resolved": "4.0.1", + "contentHash": "pzeDRXdpSLSsgBHpZcmpIDxqMy845Ab4s+dfnBg0sN9h8q/4Wo3vAoe0QCGPze1Q06EVtEPupS+UvLm8iXQmTQ==" + }, + "Serilog.AspNetCore": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "B/X+wAfS7yWLVOTD83B+Ip9yl4MkhioaXj90JSoWi1Ayi8XHepEnsBdrkojg08eodCnmOKmShFUN2GgEc6c0CQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection": "8.0.0", + "Microsoft.Extensions.Logging": "8.0.0", + "Serilog": "3.1.1", + "Serilog.Extensions.Hosting": "8.0.0", + "Serilog.Extensions.Logging": "8.0.0", + "Serilog.Formatting.Compact": "2.0.0", + "Serilog.Settings.Configuration": "8.0.0", + "Serilog.Sinks.Console": "5.0.0", + "Serilog.Sinks.Debug": "2.0.0", + "Serilog.Sinks.File": "5.0.0" + } + }, + "Serilog.Extensions.Hosting": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "db0OcbWeSCvYQkHWu6n0v40N4kKaTAXNjlM3BKvcbwvNzYphQFcBR+36eQ/7hMMwOkJvAyLC2a9/jNdUL5NjtQ==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.0", + "Microsoft.Extensions.Hosting.Abstractions": "8.0.0", + "Microsoft.Extensions.Logging.Abstractions": "8.0.0", + "Serilog": "3.1.1", + "Serilog.Extensions.Logging": "8.0.0" + } + }, + "Serilog.Extensions.Logging": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "YEAMWu1UnWgf1c1KP85l1SgXGfiVo0Rz6x08pCiPOIBt2Qe18tcZLvdBUuV5o1QHvrs8FAry9wTIhgBRtjIlEg==", + "dependencies": { + "Microsoft.Extensions.Logging": "8.0.0", + "Serilog": "3.1.1" + } + }, + "Serilog.Formatting.Compact": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "ob6z3ikzFM3D1xalhFuBIK1IOWf+XrQq+H4KeH4VqBcPpNcmUgZlRQ2h3Q7wvthpdZBBoY86qZOI2LCXNaLlNA==", + "dependencies": { + "Serilog": "3.1.0" + } + }, + "Serilog.Settings.Configuration": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "nR0iL5HwKj5v6ULo3/zpP8NMcq9E2pxYA6XKTSWCbugVs4YqPyvaqaKOY+OMpPivKp7zMEpax2UKHnDodbRB0Q==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "8.0.0", + "Microsoft.Extensions.DependencyModel": "8.0.0", + "Serilog": "3.1.1" + } + }, + "Serilog.Sinks.Console": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "IZ6bn79k+3SRXOBpwSOClUHikSkp2toGPCZ0teUkscv4dpDg9E2R2xVsNkLmwddE4OpNVO3N0xiYsAH556vN8Q==", + "dependencies": { + "Serilog": "3.1.0" + } + }, + "Serilog.Sinks.Debug": { + "type": "Transitive", + "resolved": "2.0.0", + "contentHash": "Y6g3OBJ4JzTyyw16fDqtFcQ41qQAydnEvEqmXjhwhgjsnG/FaJ8GUqF5ldsC/bVkK8KYmqrPhDO+tm4dF6xx4A==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Serilog.Sinks.File": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "uwV5hdhWPwUH1szhO8PJpFiahqXmzPzJT/sOijH/kFgUx+cyoDTMM8MHD0adw9+Iem6itoibbUXHYslzXsLEAg==", + "dependencies": { + "Serilog": "2.10.0" + } + }, + "Spectre.Console": { + "type": "Transitive", + "resolved": "0.49.1", + "contentHash": "USV+pdu49OJ3nCjxNuw1K9Zw/c1HCBbwbjXZp0EOn6wM99tFdAtN34KEBZUMyRuJuXlUMDqhd8Yq9obW2MslYA==" + }, + "Spectre.Console.Cli": { + "type": "Transitive", + "resolved": "0.49.1", + "contentHash": "wBZzyEbKqfPFFUPhV5E7/k4Kwy4UDO42IVzvzk0C4Pkjjw+NSd0EOBkIutYET4vJY4X81pD9ooQO9gfBGXj4+g==", + "dependencies": { + "Spectre.Console": "0.49.1" + } + }, + "SQLitePCLRaw.bundle_e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.11", + "contentHash": "DC4nA7yWnf4UZdgJDF+9Mus4/cb0Y3Sfgi3gDnAoKNAIBwzkskNAbNbyu+u4atT0ruVlZNJfwZmwiEwE5oz9LQ==", + "dependencies": { + "SQLitePCLRaw.lib.e_sqlite3": "2.1.11", + "SQLitePCLRaw.provider.e_sqlite3": "2.1.11" + } + }, + "SQLitePCLRaw.core": { + "type": "Transitive", + "resolved": "2.1.11", + "contentHash": "PK0GLFkfhZzLQeR3PJf71FmhtHox+U3vcY6ZtswoMjrefkB9k6ErNJEnwXqc5KgXDSjige2XXrezqS39gkpQKA==" + }, + "SQLitePCLRaw.lib.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.11", + "contentHash": "Ev2ytaXiOlWZ4b3R67GZBsemTINslLD1DCJr2xiacpn4tbapu0Q4dHEzSvZSMnVWeE5nlObU3VZN2p81q3XOYQ==" + }, + "SQLitePCLRaw.provider.e_sqlite3": { + "type": "Transitive", + "resolved": "2.1.11", + "contentHash": "Y/0ZkR+r0Cg3DQFuCl1RBnv/tmxpIZRU3HUvelPw6MVaKHwYYR8YNvgs0vuNuXCMvlyJ+Fh88U1D4tah1tt6qw==", + "dependencies": { + "SQLitePCLRaw.core": "2.1.11" + } + }, + "Swashbuckle.AspNetCore": { + "type": "Transitive", + "resolved": "10.1.0", + "contentHash": "CvOffaJKStoP0hdfCIX4V/9wuwRkSOJd+PehGo/Y5ALh0WYliLwuMlyh1BbgG7uQtJNe1k5P7QIhIaFfZ/aJAw==", + "dependencies": { + "Microsoft.Extensions.ApiDescription.Server": "10.0.0", + "Swashbuckle.AspNetCore.Swagger": "10.1.0", + "Swashbuckle.AspNetCore.SwaggerGen": "10.1.0", + "Swashbuckle.AspNetCore.SwaggerUI": "10.1.0" + } + }, + "Swashbuckle.AspNetCore.Swagger": { + "type": "Transitive", + "resolved": "10.1.0", + "contentHash": "9Kp35Jhkzw73UXnvgGWVgRjvfGx5h1V4fdL9JcPZMKoTyO/bnKD/1i86n8u2p+rVTDed0cDSH4PKbX4WicZ/gg==", + "dependencies": { + "Microsoft.OpenApi": "2.3.0" + } + }, + "Swashbuckle.AspNetCore.SwaggerGen": { + "type": "Transitive", + "resolved": "10.1.0", + "contentHash": "XV2gyKmjWs5K5QaSq9rNYtO/E7vr/RcyBkMbbCVUlUtI5OY0HKj260Wee9zsJ7scJf7kPCxeseBiUMRp67ZWxA==", + "dependencies": { + "Swashbuckle.AspNetCore.Swagger": "10.1.0" + } + }, + "Swashbuckle.AspNetCore.SwaggerUI": { + "type": "Transitive", + "resolved": "10.1.0", + "contentHash": "ilUsTvGA9hO1ulR7ibdWMWSg3438Iu+pDFcEYUorp+/ClHwaHFdpp/ATfBsFXv2sIRVDbQlEwd5BWBOdMdtKCA==" + }, + "System.ClientModel": { + "type": "Transitive", + "resolved": "1.5.1", + "contentHash": "k2jKSO0X45IqhVOT9iQB4xralNN9foRQsRvXBTyRpAVxyzCJlG895T9qYrQWbcJ6OQXxOouJQ37x5nZH5XKK+A==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "8.0.3", + "System.Memory.Data": "8.0.1" + } + }, + "System.Configuration.ConfigurationManager": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "dvjqKp+2LpGid6phzrdrS/2mmEPxFl3jE1+L7614q4ZChKbLJCpHXg6sBILlCCED1t//EE+un/UdAetzIMpqnw==", + "dependencies": { + "System.Diagnostics.EventLog": "9.0.4", + "System.Security.Cryptography.ProtectedData": "9.0.4" + } + }, + "System.Diagnostics.EventLog": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "getRQEXD8idlpb1KW56XuxImMy0FKp2WJPDf3Qr0kI/QKxxJSftqfDFVo0DZ3HCJRLU73qHSruv5q2l5O47jQQ==" + }, + "System.IdentityModel.Tokens.Jwt": { + "type": "Transitive", + "resolved": "7.7.1", + "contentHash": "rQkO1YbAjLwnDJSMpRhRtrc6XwIcEOcUvoEcge+evurpzSZM3UNK+MZfD3sKyTlYsvknZ6eJjSBfnmXqwOsT9Q==", + "dependencies": { + "Microsoft.IdentityModel.JsonWebTokens": "7.7.1", + "Microsoft.IdentityModel.Tokens": "7.7.1" + } + }, + "System.Memory.Data": { + "type": "Transitive", + "resolved": "8.0.1", + "contentHash": "BVYuec3jV23EMRDeR7Dr1/qhx7369dZzJ9IWy2xylvb4YfXsrUxspWc4UWYid/tj4zZK58uGZqn2WQiaDMhmAg==" + }, + "System.Security.Cryptography.Pkcs": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "cUFTcMlz/Qw9s90b2wnWSCvHdjv51Bau9FQqhsr4TlwSe1OX+7SoXUqphis5G74MLOvMOCghxPPlEqOdCrVVGA==" + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "9.0.4", + "contentHash": "o94k2RKuAce3GeDMlUvIXlhVa1kWpJw95E6C9LwW0KlG0nj5+SgCiIxJ2Eroqb9sLtG1mEMbFttZIBZ13EJPvQ==" + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.3", + "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "1.14.0", + "contentHash": "KcFBmV2150xZHPUebV3YLR5gGl8R4wLuPOoxMiwCf1L4bL8ls0dcwtGFzr6NvQRgg6dWgSqbE52I6SYyeB0VnQ==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.8.1", + "contentHash": "DDM18ur+PeNFhQ4w/vO+uvCUy8hA3OS5+AMf/CFov9Wco7Le49zzj0hovRWwa8f/3vaUfjL5r+IkPvqEHu2IIg==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.8.1", + "contentHash": "Ng4Q/DOwotESPl5CufcdqgP6O2KDpdEcIvNfA3upzfCiBrkj5WsmLhf/XUsCVolzvHA7b1WUlyeTo7j1ulG4gQ==", + "dependencies": { + "xunit.extensibility.core": "[2.8.1]", + "xunit.extensibility.execution": "[2.8.1]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.8.1", + "contentHash": "ilfAsxEhpne9AXXf3W+O65mRgGum94m2xHYm1yeJ1m7eiINM6OOwpaHhoNC/KWEQ2u/WF6/XiEs+Q0TOq7hiGA==", + "dependencies": { + "xunit.abstractions": "2.0.3" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.8.1", + "contentHash": "38UnJW+64Wn8QIabujcNEw0HKvWw2AlYCgU8GNwCCDqyrSuRYb7zwetn7SHoHfbL9e9FAvEiAMXmc2wSUY8sVQ==", + "dependencies": { + "xunit.extensibility.core": "[2.8.1]" + } + }, + "ecommerceapp.consoleclient": { + "type": "Project", + "dependencies": { + "Spectre.Console": "[0.49.1, )" + } + }, + "ecommerceapp.ryanw84": { + "type": "Project", + "dependencies": { + "FluentValidation.AspNetCore": "[11.3.1, )", + "Microsoft.AspNetCore.OpenApi": "[10.0.1, )", + "Microsoft.EntityFrameworkCore": "[10.0.0, )", + "Microsoft.EntityFrameworkCore.Abstractions": "[10.0.0, )", + "Microsoft.EntityFrameworkCore.SqlServer": "[10.0.0, )", + "Microsoft.EntityFrameworkCore.Sqlite": "[10.0.0, )", + "Scalar.AspNetCore": "[2.8.4, )", + "Serilog": "[4.0.1, )", + "Serilog.AspNetCore": "[8.0.1, )", + "Swashbuckle.AspNetCore": "[10.1.0, )" + } + } + } + } +} \ No newline at end of file diff --git a/tests/ECommerceApp.UnitTests/xunit.runner.json b/tests/ECommerceApp.UnitTests/xunit.runner.json new file mode 100644 index 00000000..87a529d3 --- /dev/null +++ b/tests/ECommerceApp.UnitTests/xunit.runner.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "parallelizeAssembly": true, + "parallelizeTestCollections": true, + "maxParallelThreads": -1 +} \ No newline at end of file From d3a8b1ecde26910e504cb7e554f80cbf336e59a6 Mon Sep 17 00:00:00 2001 From: Ryan Weavers Date: Sun, 11 Jan 2026 21:39:42 +0000 Subject: [PATCH 3/3] codacy config updated --- .codacy/cli.sh | 149 ++++++++++++++++++ .codacy/codacy.yaml | 2 + .vscode/settings.json | 5 + .../ECommerceApp.ArchitectureTests.csproj | 2 - 4 files changed, 156 insertions(+), 2 deletions(-) create mode 100755 .codacy/cli.sh create mode 100644 .codacy/codacy.yaml create mode 100644 .vscode/settings.json diff --git a/.codacy/cli.sh b/.codacy/cli.sh new file mode 100755 index 00000000..7057e3bf --- /dev/null +++ b/.codacy/cli.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env bash + + +set -e +o pipefail + +# Set up paths first +bin_name="codacy-cli-v2" + +# Determine OS-specific paths +os_name=$(uname) +arch=$(uname -m) + +case "$arch" in +"x86_64") + arch="amd64" + ;; +"x86") + arch="386" + ;; +"aarch64"|"arm64") + arch="arm64" + ;; +esac + +if [ -z "$CODACY_CLI_V2_TMP_FOLDER" ]; then + if [ "$(uname)" = "Linux" ]; then + CODACY_CLI_V2_TMP_FOLDER="$HOME/.cache/codacy/codacy-cli-v2" + elif [ "$(uname)" = "Darwin" ]; then + CODACY_CLI_V2_TMP_FOLDER="$HOME/Library/Caches/Codacy/codacy-cli-v2" + else + CODACY_CLI_V2_TMP_FOLDER=".codacy-cli-v2" + fi +fi + +version_file="$CODACY_CLI_V2_TMP_FOLDER/version.yaml" + + +get_version_from_yaml() { + if [ -f "$version_file" ]; then + local version=$(grep -o 'version: *"[^"]*"' "$version_file" | cut -d'"' -f2) + if [ -n "$version" ]; then + echo "$version" + return 0 + fi + fi + return 1 +} + +get_latest_version() { + local response + if [ -n "$GH_TOKEN" ]; then + response=$(curl -Lq --header "Authorization: Bearer $GH_TOKEN" "https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest" 2>/dev/null) + else + response=$(curl -Lq "https://api.github.com/repos/codacy/codacy-cli-v2/releases/latest" 2>/dev/null) + fi + + handle_rate_limit "$response" + local version=$(echo "$response" | grep -m 1 tag_name | cut -d'"' -f4) + echo "$version" +} + +handle_rate_limit() { + local response="$1" + if echo "$response" | grep -q "API rate limit exceeded"; then + fatal "Error: GitHub API rate limit exceeded. Please try again later" + fi +} + +download_file() { + local url="$1" + + echo "Downloading from URL: ${url}" + if command -v curl > /dev/null 2>&1; then + curl -# -LS "$url" -O + elif command -v wget > /dev/null 2>&1; then + wget "$url" + else + fatal "Error: Could not find curl or wget, please install one." + fi +} + +download() { + local url="$1" + local output_folder="$2" + + ( cd "$output_folder" && download_file "$url" ) +} + +download_cli() { + # OS name lower case + suffix=$(echo "$os_name" | tr '[:upper:]' '[:lower:]') + + local bin_folder="$1" + local bin_path="$2" + local version="$3" + + if [ ! -f "$bin_path" ]; then + echo "đŸ“Ĩ Downloading CLI version $version..." + + remote_file="codacy-cli-v2_${version}_${suffix}_${arch}.tar.gz" + url="https://github.com/codacy/codacy-cli-v2/releases/download/${version}/${remote_file}" + + download "$url" "$bin_folder" + tar xzfv "${bin_folder}/${remote_file}" -C "${bin_folder}" + fi +} + +# Warn if CODACY_CLI_V2_VERSION is set and update is requested +if [ -n "$CODACY_CLI_V2_VERSION" ] && [ "$1" = "update" ]; then + echo "âš ī¸ Warning: Performing update with forced version $CODACY_CLI_V2_VERSION" + echo " Unset CODACY_CLI_V2_VERSION to use the latest version" +fi + +# Ensure version.yaml exists and is up to date +if [ ! -f "$version_file" ] || [ "$1" = "update" ]; then + echo "â„šī¸ Fetching latest version..." + version=$(get_latest_version) + mkdir -p "$CODACY_CLI_V2_TMP_FOLDER" + echo "version: \"$version\"" > "$version_file" +fi + +# Set the version to use +if [ -n "$CODACY_CLI_V2_VERSION" ]; then + version="$CODACY_CLI_V2_VERSION" +else + version=$(get_version_from_yaml) +fi + + +# Set up version-specific paths +bin_folder="${CODACY_CLI_V2_TMP_FOLDER}/${version}" + +mkdir -p "$bin_folder" +bin_path="$bin_folder"/"$bin_name" + +# Download the tool if not already installed +download_cli "$bin_folder" "$bin_path" "$version" +chmod +x "$bin_path" + +run_command="$bin_path" +if [ -z "$run_command" ]; then + fatal "Codacy cli v2 binary could not be found." +fi + +if [ "$#" -eq 1 ] && [ "$1" = "download" ]; then + echo "Codacy cli v2 download succeeded" +else + eval "$run_command $*" +fi \ No newline at end of file diff --git a/.codacy/codacy.yaml b/.codacy/codacy.yaml new file mode 100644 index 00000000..0ea27a63 --- /dev/null +++ b/.codacy/codacy.yaml @@ -0,0 +1,2 @@ +tools: + - trivy@0.66.0 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..1bcf7b2f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cSpell.words": [ + "buildtransitive" + ] +} \ No newline at end of file diff --git a/tests/ArchitectureTests/ECommerceApp.ArchitectureTests.csproj b/tests/ArchitectureTests/ECommerceApp.ArchitectureTests.csproj index 82478834..0a884ae8 100644 --- a/tests/ArchitectureTests/ECommerceApp.ArchitectureTests.csproj +++ b/tests/ArchitectureTests/ECommerceApp.ArchitectureTests.csproj @@ -1,5 +1,4 @@ - net10.0 false @@ -21,5 +20,4 @@ -