From a7443708773b0b5ccbdd75ce37750bf040ec1c0a Mon Sep 17 00:00:00 2001 From: Federico Arambarri Date: Tue, 15 Oct 2024 10:11:24 -0300 Subject: [PATCH 1/5] upgrade chatty-io --- .../AdventureWorksProductContext.cs | 42 ----- .../ChattyIO.DataAccess.csproj | 71 -------- .../ChattyIO.DataAccess/Models/Product.cs | 32 ---- .../Models/ProductCategory.cs | 24 --- .../Models/ProductListPriceHistory.cs | 22 --- .../Models/ProductSubcategory.cs | 27 --- .../ChattyIO.DataAccess/ProductionMapping.cs | 75 -------- .../Properties/AssemblyInfo.cs | 36 ---- ChattyIO/ChattyIO.DataAccess/packages.config | 4 - .../ChattyIO.WebApi/App_Start/WebApiConfig.cs | 24 --- .../ChattyIO.WebApi/ChattyIO.WebApi.csproj | 144 --------------- .../Controllers/ChattyProductController.cs | 52 ------ .../Controllers/ChunkyProductController.cs | 33 ---- ChattyIO/ChattyIO.WebApi/Global.asax | 1 - ChattyIO/ChattyIO.WebApi/Global.asax.cs | 16 -- .../Properties/AssemblyInfo.cs | 38 ---- ChattyIO/ChattyIO.WebApi/Web.Debug.config | 30 --- ChattyIO/ChattyIO.WebApi/Web.Release.config | 31 ---- ChattyIO/ChattyIO.WebApi/Web.config | 33 ---- ChattyIO/ChattyIO.WebApi/packages.config | 9 - ChattyIO/ChattyIO.sln | 35 ---- ChattyIO/ChattyIO/ChattyIO.sln | 25 +++ ChattyIO/ChattyIO/ChattyIO/ChattyIO.csproj | 16 ++ .../Controllers/ChattyProductController.cs | 65 +++++++ .../Controllers/ChunkyProductController.cs | 44 +++++ .../ChattyIO/DTOs/ProductCategoryDTO.cs | 12 ++ ChattyIO/ChattyIO/ChattyIO/DTOs/ProductDTO.cs | 9 + .../ChattyIO/DTOs/SalesOrderDetailDTO.cs | 9 + .../Model/AdventureWorksProductContext.cs | 51 ++++++ ChattyIO/ChattyIO/ChattyIO/Model/Product.cs | 57 ++++++ .../ChattyIO/Model/ProductCategory.cs | 25 +++ .../ChattyIO/Model/SalesOrderDetail.cs | 34 ++++ .../ChattyIO/Model/SalesOrderHeader.cs | 58 ++++++ ChattyIO/ChattyIO/ChattyIO/Program.cs | 56 ++++++ .../ChattyIO/appsettings.Development.json | 8 + ChattyIO/ChattyIO/ChattyIO/appsettings.json | 10 + ChattyIO/ReadMe.md | 107 ++++++++--- ChattyIO/bicep/main.bicep | 171 ++++++++++++++++++ 38 files changed, 733 insertions(+), 803 deletions(-) delete mode 100644 ChattyIO/ChattyIO.DataAccess/AdventureWorksProductContext.cs delete mode 100644 ChattyIO/ChattyIO.DataAccess/ChattyIO.DataAccess.csproj delete mode 100644 ChattyIO/ChattyIO.DataAccess/Models/Product.cs delete mode 100644 ChattyIO/ChattyIO.DataAccess/Models/ProductCategory.cs delete mode 100644 ChattyIO/ChattyIO.DataAccess/Models/ProductListPriceHistory.cs delete mode 100644 ChattyIO/ChattyIO.DataAccess/Models/ProductSubcategory.cs delete mode 100644 ChattyIO/ChattyIO.DataAccess/ProductionMapping.cs delete mode 100644 ChattyIO/ChattyIO.DataAccess/Properties/AssemblyInfo.cs delete mode 100644 ChattyIO/ChattyIO.DataAccess/packages.config delete mode 100644 ChattyIO/ChattyIO.WebApi/App_Start/WebApiConfig.cs delete mode 100644 ChattyIO/ChattyIO.WebApi/ChattyIO.WebApi.csproj delete mode 100644 ChattyIO/ChattyIO.WebApi/Controllers/ChattyProductController.cs delete mode 100644 ChattyIO/ChattyIO.WebApi/Controllers/ChunkyProductController.cs delete mode 100644 ChattyIO/ChattyIO.WebApi/Global.asax delete mode 100644 ChattyIO/ChattyIO.WebApi/Global.asax.cs delete mode 100644 ChattyIO/ChattyIO.WebApi/Properties/AssemblyInfo.cs delete mode 100644 ChattyIO/ChattyIO.WebApi/Web.Debug.config delete mode 100644 ChattyIO/ChattyIO.WebApi/Web.Release.config delete mode 100644 ChattyIO/ChattyIO.WebApi/Web.config delete mode 100644 ChattyIO/ChattyIO.WebApi/packages.config delete mode 100644 ChattyIO/ChattyIO.sln create mode 100644 ChattyIO/ChattyIO/ChattyIO.sln create mode 100644 ChattyIO/ChattyIO/ChattyIO/ChattyIO.csproj create mode 100644 ChattyIO/ChattyIO/ChattyIO/Controllers/ChattyProductController.cs create mode 100644 ChattyIO/ChattyIO/ChattyIO/Controllers/ChunkyProductController.cs create mode 100644 ChattyIO/ChattyIO/ChattyIO/DTOs/ProductCategoryDTO.cs create mode 100644 ChattyIO/ChattyIO/ChattyIO/DTOs/ProductDTO.cs create mode 100644 ChattyIO/ChattyIO/ChattyIO/DTOs/SalesOrderDetailDTO.cs create mode 100644 ChattyIO/ChattyIO/ChattyIO/Model/AdventureWorksProductContext.cs create mode 100644 ChattyIO/ChattyIO/ChattyIO/Model/Product.cs create mode 100644 ChattyIO/ChattyIO/ChattyIO/Model/ProductCategory.cs create mode 100644 ChattyIO/ChattyIO/ChattyIO/Model/SalesOrderDetail.cs create mode 100644 ChattyIO/ChattyIO/ChattyIO/Model/SalesOrderHeader.cs create mode 100644 ChattyIO/ChattyIO/ChattyIO/Program.cs create mode 100644 ChattyIO/ChattyIO/ChattyIO/appsettings.Development.json create mode 100644 ChattyIO/ChattyIO/ChattyIO/appsettings.json create mode 100644 ChattyIO/bicep/main.bicep diff --git a/ChattyIO/ChattyIO.DataAccess/AdventureWorksProductContext.cs b/ChattyIO/ChattyIO.DataAccess/AdventureWorksProductContext.cs deleted file mode 100644 index 5f3862d..0000000 --- a/ChattyIO/ChattyIO.DataAccess/AdventureWorksProductContext.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Data.Entity; -using ChattyIO.DataAccess.Models; - -namespace ChattyIO.DataAccess -{ - public class AdventureWorksProductContext : DbContext - { - static AdventureWorksProductContext() - { - Database.SetInitializer(null); - } - - public static AdventureWorksProductContext GetEagerContext() - { - var context = new AdventureWorksProductContext(); - context.Configuration.LazyLoadingEnabled = false; - context.Configuration.ProxyCreationEnabled = false; - return context; - } - - public AdventureWorksProductContext() - : base("Name=AdventureWorksProductContext") - { - } - - protected override void OnModelCreating(DbModelBuilder modelBuilder) - { - modelBuilder.Configurations.Add(new ProductConfiguration()); - modelBuilder.Configurations.Add(new ProductCategoryConfiguration()); - modelBuilder.Configurations.Add(new ProductListPriceHistoryConfiguration()); - modelBuilder.Configurations.Add(new ProductSubcategoryConfiguration()); - } - - public DbSet Products { get; set; } - public DbSet ProductCategories { get; set; } - public DbSet ProductListPriceHistory { get; set; } - public DbSet ProductSubcategories { get; set; } - } -} diff --git a/ChattyIO/ChattyIO.DataAccess/ChattyIO.DataAccess.csproj b/ChattyIO/ChattyIO.DataAccess/ChattyIO.DataAccess.csproj deleted file mode 100644 index 2852d0d..0000000 --- a/ChattyIO/ChattyIO.DataAccess/ChattyIO.DataAccess.csproj +++ /dev/null @@ -1,71 +0,0 @@ - - - - - Debug - AnyCPU - {71798002-E345-4C51-A177-571FF9D71AB7} - Library - Properties - ChattyIO.DataAccess - ChattyIO.DataAccess - v4.5.1 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - False - ..\..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll - - - False - ..\..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ChattyIO/ChattyIO.DataAccess/Models/Product.cs b/ChattyIO/ChattyIO.DataAccess/Models/Product.cs deleted file mode 100644 index 697f35a..0000000 --- a/ChattyIO/ChattyIO.DataAccess/Models/Product.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Collections.Generic; - -namespace ChattyIO.DataAccess.Models -{ - public class Product - { - public Product() - { - ProductListPriceHistory = new List(); - } - - // ProductID (Primary key). Primary key for Product records. - public int ProductId { get; set; } - - // Name. Name of the product. - public string Name { get; set; } - - // ProductNumber. Unique product identification number. - public string ProductNumber { get; set; } - - // ListPrice. Selling price. - public decimal ListPrice { get; set; } - - // ProductSubcategoryID. Product is a member of this product subcategory. Foreign key to ProductSubCategory.ProductSubCategoryID. - public int? ProductSubcategoryId { get; set; } - - public virtual ICollection ProductListPriceHistory { get; set; } - } -} \ No newline at end of file diff --git a/ChattyIO/ChattyIO.DataAccess/Models/ProductCategory.cs b/ChattyIO/ChattyIO.DataAccess/Models/ProductCategory.cs deleted file mode 100644 index ff944d1..0000000 --- a/ChattyIO/ChattyIO.DataAccess/Models/ProductCategory.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Collections.Generic; - -namespace ChattyIO.DataAccess.Models -{ - public class ProductCategory - { - public ProductCategory() - { - ProductSubcategory = new List(); - } - - // ProductCategoryID (Primary key). Primary key for ProductCategory records. - public int ProductCategoryId { get; set; } - - // Name. Category description. - public string Name { get; set; } - - // Reverse navigation - public virtual ICollection ProductSubcategory { get; set; } - } -} diff --git a/ChattyIO/ChattyIO.DataAccess/Models/ProductListPriceHistory.cs b/ChattyIO/ChattyIO.DataAccess/Models/ProductListPriceHistory.cs deleted file mode 100644 index 3b9ed63..0000000 --- a/ChattyIO/ChattyIO.DataAccess/Models/ProductListPriceHistory.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; - -namespace ChattyIO.DataAccess.Models -{ - public class ProductListPriceHistory - { - // ProductID (Primary key). Product identification number. Foreign key to Product.ProductID - public int ProductId { get; set; } - - // StartDate (Primary key). List price start date. - public DateTime StartDate { get; set; } - - // EndDate. List price end date - public DateTime? EndDate { get; set; } - - // ListPrice. Product list price. - public decimal ListPrice { get; set; } - } -} diff --git a/ChattyIO/ChattyIO.DataAccess/Models/ProductSubcategory.cs b/ChattyIO/ChattyIO.DataAccess/Models/ProductSubcategory.cs deleted file mode 100644 index 56c2e2f..0000000 --- a/ChattyIO/ChattyIO.DataAccess/Models/ProductSubcategory.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Collections.Generic; - -namespace ChattyIO.DataAccess.Models -{ - public class ProductSubcategory - { - public ProductSubcategory() - { - Product = new List(); - } - - // ProductSubcategoryID (Primary key). Primary key for ProductSubcategory records. - public int ProductSubcategoryId { get; set; } - - // ProductCategoryID. Product category identification number. Foreign key to ProductCategory.ProductCategoryID. - public int ProductCategoryId { get; set; } - - // Name. Subcategory description. - public string Name { get; set; } - - // Reverse navigation - public virtual ICollection Product { get; set; } - } -} \ No newline at end of file diff --git a/ChattyIO/ChattyIO.DataAccess/ProductionMapping.cs b/ChattyIO/ChattyIO.DataAccess/ProductionMapping.cs deleted file mode 100644 index 17b31ac..0000000 --- a/ChattyIO/ChattyIO.DataAccess/ProductionMapping.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.ComponentModel.DataAnnotations.Schema; -using System.Data.Entity.ModelConfiguration; -using ChattyIO.DataAccess.Models; - -namespace ChattyIO.DataAccess -{ - //Product - internal class ProductConfiguration : EntityTypeConfiguration - { - public ProductConfiguration(string schema = "Production") - { - ToTable(schema + ".Product"); - HasKey(x => x.ProductId); - - Property(x => x.ProductId) - .HasColumnName("ProductID") - .IsRequired() - .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); - Property(x => x.Name).HasColumnName("Name").IsRequired().HasMaxLength(50); - } - } - - // ProductCategory - internal class ProductCategoryConfiguration : EntityTypeConfiguration - { - public ProductCategoryConfiguration(string schema = "Production") - { - ToTable(schema + ".ProductCategory"); - HasKey(x => x.ProductCategoryId); - - Property(x => x.ProductCategoryId) - .HasColumnName("ProductCategoryID") - .IsRequired() - .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); - Property(x => x.Name).HasColumnName("Name").IsRequired().HasMaxLength(50); - - } - } - - // ProductListPriceHistory - internal class ProductListPriceHistoryConfiguration : - EntityTypeConfiguration - { - public ProductListPriceHistoryConfiguration(string schema = "Production") - { - ToTable(schema + ".ProductListPriceHistory"); - HasKey(x => new { x.ProductId, x.StartDate }); - - Property(x => x.ProductId) - .HasColumnName("ProductID") - .IsRequired() - .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None); - Property(x => x.StartDate).HasColumnName("StartDate").IsRequired(); - Property(x => x.EndDate).HasColumnName("EndDate").IsOptional(); - Property(x => x.ListPrice).HasColumnName("ListPrice").IsRequired().HasPrecision(19, 4); - } - } - - // ProductSubcategory - internal class ProductSubcategoryConfiguration : EntityTypeConfiguration - { - public ProductSubcategoryConfiguration(string schema = "Production") - { - ToTable(schema + ".ProductSubcategory"); - HasKey(x => x.ProductSubcategoryId); - - Property(x => x.ProductSubcategoryId).HasColumnName("ProductSubcategoryID").IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); - Property(x => x.ProductCategoryId).HasColumnName("ProductCategoryID").IsRequired(); - Property(x => x.Name).HasColumnName("Name").IsRequired().HasMaxLength(50); - } - } -} diff --git a/ChattyIO/ChattyIO.DataAccess/Properties/AssemblyInfo.cs b/ChattyIO/ChattyIO.DataAccess/Properties/AssemblyInfo.cs deleted file mode 100644 index 642877d..0000000 --- a/ChattyIO/ChattyIO.DataAccess/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ChattyIO.DataAccess")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("ChattyIO.DataAccess")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("88bc3835-ef44-4a23-a978-08d6ffd9f5de")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ChattyIO/ChattyIO.DataAccess/packages.config b/ChattyIO/ChattyIO.DataAccess/packages.config deleted file mode 100644 index 6138937..0000000 --- a/ChattyIO/ChattyIO.DataAccess/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/ChattyIO/ChattyIO.WebApi/App_Start/WebApiConfig.cs b/ChattyIO/ChattyIO.WebApi/App_Start/WebApiConfig.cs deleted file mode 100644 index 0daa9c6..0000000 --- a/ChattyIO/ChattyIO.WebApi/App_Start/WebApiConfig.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Web.Http; - -namespace ChattyIO.WebApi -{ - public static class WebApiConfig - { - public static void Register(HttpConfiguration config) - { - // Web API configuration and services - - // Web API routes - config.MapHttpAttributeRoutes(); - - config.Routes.MapHttpRoute( - name: "DefaultApi", - routeTemplate: "api/{controller}/{id}", - defaults: new {controller="Home", action = "Index", id = RouteParameter.Optional } - ); - } - } -} diff --git a/ChattyIO/ChattyIO.WebApi/ChattyIO.WebApi.csproj b/ChattyIO/ChattyIO.WebApi/ChattyIO.WebApi.csproj deleted file mode 100644 index 7ed4130..0000000 --- a/ChattyIO/ChattyIO.WebApi/ChattyIO.WebApi.csproj +++ /dev/null @@ -1,144 +0,0 @@ - - - - - Debug - AnyCPU - - - 2.0 - {F8C34AFB-5694-4028-B665-4075EA420C7B} - {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties - ChattyIO.WebApi - ChattyIO.WebApi - v4.5.1 - true - - - - - - - true - full - false - bin\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\ - TRACE - prompt - 4 - - - - False - ..\..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.dll - - - False - ..\..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll - - - - False - ..\..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll - - - - False - ..\..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll - - - - - - - - - - - - False - ..\..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll - - - False - ..\..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll - - - - - - - - - - - - - - - - - - - Global.asax - - - - - - - Web.config - - - Web.config - - - - - {71798002-e345-4c51-a177-571ff9d71ab7} - ChattyIO.DataAccess - - - - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - - - - - True - True - 6940 - / - http://localhost:34183/ - False - False - - - False - - - - - - \ No newline at end of file diff --git a/ChattyIO/ChattyIO.WebApi/Controllers/ChattyProductController.cs b/ChattyIO/ChattyIO.WebApi/Controllers/ChattyProductController.cs deleted file mode 100644 index e01d8e6..0000000 --- a/ChattyIO/ChattyIO.WebApi/Controllers/ChattyProductController.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Data.Entity; -using System.Linq; -using System.Threading.Tasks; -using System.Web.Http; -using ChattyIO.DataAccess; - -namespace ChattyIO.WebApi.Controllers -{ - // We are using the db context directly here since the purpose of this is to illustrate perf anti-patterns. - // Consider using the Repository pattern instead in a real app. - - public class ChattyProductController : ApiController - { - [HttpGet] - [Route("chattyproduct/products/{subcategoryId}")] - public async Task GetProductsInSubCategoryAsync(int subcategoryId) - { - using (var context = AdventureWorksProductContext.GetEagerContext()) - { - var productSubcategory = await context.ProductSubcategories - .Where(psc => psc.ProductSubcategoryId == subcategoryId) - .FirstOrDefaultAsync(); - - if (productSubcategory == null) - { - // The subcategory was not found. - return NotFound(); - } - - productSubcategory.Product = await context.Products - .Where(p => subcategoryId == p.ProductSubcategoryId) - .ToListAsync(); - - foreach (var prod in productSubcategory.Product) - { - int productId = prod.ProductId; - - var productListPriceHistory = await context.ProductListPriceHistory - .Where(pl => pl.ProductId == productId) - .ToListAsync(); - - prod.ProductListPriceHistory = productListPriceHistory; - } - - return Ok(productSubcategory); - } - } - } -} diff --git a/ChattyIO/ChattyIO.WebApi/Controllers/ChunkyProductController.cs b/ChattyIO/ChattyIO.WebApi/Controllers/ChunkyProductController.cs deleted file mode 100644 index aadc23e..0000000 --- a/ChattyIO/ChattyIO.WebApi/Controllers/ChunkyProductController.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Data.Entity; -using System.Linq; -using System.Threading.Tasks; -using System.Web.Http; -using ChattyIO.DataAccess; - -namespace ChattyIO.WebApi.Controllers -{ - public class ChunkyProductController : ApiController - { - [HttpGet] - [Route("chunkyproduct/products/{subCategoryId}")] - public async Task GetProductCategoryDetailsAsync(int subCategoryId) - { - using (var context = AdventureWorksProductContext.GetEagerContext()) - { - var subCategory = await context.ProductSubcategories - .Where(psc => psc.ProductSubcategoryId == subCategoryId) - .Include("Product.ProductListPriceHistory") - .FirstOrDefaultAsync(); - - if (subCategory == null) - return NotFound(); - - return Ok(subCategory); - } - } - } -} - diff --git a/ChattyIO/ChattyIO.WebApi/Global.asax b/ChattyIO/ChattyIO.WebApi/Global.asax deleted file mode 100644 index 6a1f0a5..0000000 --- a/ChattyIO/ChattyIO.WebApi/Global.asax +++ /dev/null @@ -1 +0,0 @@ -<%@ Application Codebehind="Global.asax.cs" Inherits="ChattyIO.WebApi.WebApiApplication" Language="C#" %> diff --git a/ChattyIO/ChattyIO.WebApi/Global.asax.cs b/ChattyIO/ChattyIO.WebApi/Global.asax.cs deleted file mode 100644 index 016a7c6..0000000 --- a/ChattyIO/ChattyIO.WebApi/Global.asax.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Web; -using System.Web.Http; - -namespace ChattyIO.WebApi -{ - public class WebApiApplication : HttpApplication - { - protected void Application_Start() - { - GlobalConfiguration.Configure(WebApiConfig.Register); - } - } -} diff --git a/ChattyIO/ChattyIO.WebApi/Properties/AssemblyInfo.cs b/ChattyIO/ChattyIO.WebApi/Properties/AssemblyInfo.cs deleted file mode 100644 index 7939fd9..0000000 --- a/ChattyIO/ChattyIO.WebApi/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("ChattyIO.WebApi")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("ChattyIO.WebApi")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("78404761-9ba0-45bd-9e03-ef3ae7e094b0")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Revision and Build Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/ChattyIO/ChattyIO.WebApi/Web.Debug.config b/ChattyIO/ChattyIO.WebApi/Web.Debug.config deleted file mode 100644 index 2e302f9..0000000 --- a/ChattyIO/ChattyIO.WebApi/Web.Debug.config +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/ChattyIO/ChattyIO.WebApi/Web.Release.config b/ChattyIO/ChattyIO.WebApi/Web.Release.config deleted file mode 100644 index c358444..0000000 --- a/ChattyIO/ChattyIO.WebApi/Web.Release.config +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/ChattyIO/ChattyIO.WebApi/Web.config b/ChattyIO/ChattyIO.WebApi/Web.config deleted file mode 100644 index af299a4..0000000 --- a/ChattyIO/ChattyIO.WebApi/Web.config +++ /dev/null @@ -1,33 +0,0 @@ - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ChattyIO/ChattyIO.WebApi/packages.config b/ChattyIO/ChattyIO.WebApi/packages.config deleted file mode 100644 index 6033a55..0000000 --- a/ChattyIO/ChattyIO.WebApi/packages.config +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/ChattyIO/ChattyIO.sln b/ChattyIO/ChattyIO.sln deleted file mode 100644 index 3747a96..0000000 --- a/ChattyIO/ChattyIO.sln +++ /dev/null @@ -1,35 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChattyIO.DataAccess", "ChattyIO.DataAccess\ChattyIO.DataAccess.csproj", "{71798002-E345-4C51-A177-571FF9D71AB7}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChattyIO.WebApi", "ChattyIO.WebApi\ChattyIO.WebApi.csproj", "{F8C34AFB-5694-4028-B665-4075EA420C7B}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DB9FCE38-B05C-4DE7-801B-9CB31CEDFD70}" - ProjectSection(SolutionItems) = preProject - docs\ChattyIO.md = docs\ChattyIO.md - ..\LICENSE.txt = ..\LICENSE.txt - ReadMe.md = ReadMe.md - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {71798002-E345-4C51-A177-571FF9D71AB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {71798002-E345-4C51-A177-571FF9D71AB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {71798002-E345-4C51-A177-571FF9D71AB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {71798002-E345-4C51-A177-571FF9D71AB7}.Release|Any CPU.Build.0 = Release|Any CPU - {F8C34AFB-5694-4028-B665-4075EA420C7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F8C34AFB-5694-4028-B665-4075EA420C7B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F8C34AFB-5694-4028-B665-4075EA420C7B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F8C34AFB-5694-4028-B665-4075EA420C7B}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/ChattyIO/ChattyIO/ChattyIO.sln b/ChattyIO/ChattyIO/ChattyIO.sln new file mode 100644 index 0000000..76e2b5a --- /dev/null +++ b/ChattyIO/ChattyIO/ChattyIO.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.11.35327.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ChattyIO", "ChattyIO\ChattyIO.csproj", "{BCFE4366-C1C2-46A9-B5AB-1559CD38880A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BCFE4366-C1C2-46A9-B5AB-1559CD38880A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BCFE4366-C1C2-46A9-B5AB-1559CD38880A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BCFE4366-C1C2-46A9-B5AB-1559CD38880A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BCFE4366-C1C2-46A9-B5AB-1559CD38880A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {466FBB78-F7B8-4917-B122-67D59CA4AC2E} + EndGlobalSection +EndGlobal diff --git a/ChattyIO/ChattyIO/ChattyIO/ChattyIO.csproj b/ChattyIO/ChattyIO/ChattyIO/ChattyIO.csproj new file mode 100644 index 0000000..e213fa0 --- /dev/null +++ b/ChattyIO/ChattyIO/ChattyIO/ChattyIO.csproj @@ -0,0 +1,16 @@ + + + + net8.0 + enable + enable + + + + + + + + + + diff --git a/ChattyIO/ChattyIO/ChattyIO/Controllers/ChattyProductController.cs b/ChattyIO/ChattyIO/ChattyIO/Controllers/ChattyProductController.cs new file mode 100644 index 0000000..39ac2a5 --- /dev/null +++ b/ChattyIO/ChattyIO/ChattyIO/Controllers/ChattyProductController.cs @@ -0,0 +1,65 @@ +using ChattyIO.DTOs; +using ChattyIO.Model; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace ChattyIO.Controllers +{ + [ApiController] + [Route("[controller]")] + public class ChattyProductController(ILogger logger, AdventureWorksProductContext context) : ControllerBase + { + + [HttpGet("products/{subcategoryId}")] + public async Task GetProductsInSubCategoryAsync(int subcategoryId) + { + var productSubcategory = await context.ProductCategories + .Where(psc => psc.ProductCategoryID == subcategoryId) + .FirstOrDefaultAsync(); + + if (productSubcategory == null) + { + // The subcategory was not found. + return NotFound(); + } + + productSubcategory.Products = await context.Products + .Where(p => subcategoryId == p.ProductCategoryID) + .ToListAsync(); + + foreach (var prod in productSubcategory.Products) + { + int productId = prod.ProductID; + + var salesOrderDetail = await context.SalesOrderDetails + .Where(sod => sod.ProductID == productId) + .ToListAsync(); + + prod.SalesOrderDetails = salesOrderDetail; + } + + var categories = new ProductCategoryDTO + { + ProductCategoryID = productSubcategory.ProductCategoryID, + ParentProductCategoryID = productSubcategory.ParentProductCategoryID, + Name = productSubcategory.Name, + Rowguid = productSubcategory.Rowguid, + ModifiedDate = productSubcategory.ModifiedDate, + Products = productSubcategory.Products.Select(p => new ProductDTO + { + ProductID = p.ProductID, + Name = p.Name, + SalesOrderDetails = p.SalesOrderDetails.Select(od => new SalesOrderDetailDTO + { + SalesOrderDetailID = od.SalesOrderDetailID, + OrderQty = od.OrderQty, + ProductID = od.ProductID + }).ToList() + }) + .ToList() + }; + + return Ok(categories); + } + } +} diff --git a/ChattyIO/ChattyIO/ChattyIO/Controllers/ChunkyProductController.cs b/ChattyIO/ChattyIO/ChattyIO/Controllers/ChunkyProductController.cs new file mode 100644 index 0000000..acd07b7 --- /dev/null +++ b/ChattyIO/ChattyIO/ChattyIO/Controllers/ChunkyProductController.cs @@ -0,0 +1,44 @@ +using ChattyIO.DTOs; +using ChattyIO.Model; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace ChattyIO.Controllers +{ + [ApiController] + [Route("[controller]")] + public class ChunkyProductController(ILogger logger, AdventureWorksProductContext context) : ControllerBase + { + + [HttpGet("products/{subcategoryId}")] + public async Task GetProductCategoryDetailsAsync(int subcategoryId) + { + var categories = await context.ProductCategories + .Include(x => x.Products) // Include related Products + .ThenInclude(p => p.SalesOrderDetails) // Then include SalesOrderDetails for each Product + .Where(x => x.ProductCategoryID == subcategoryId) + .Select(x => new ProductCategoryDTO + { + ProductCategoryID = x.ProductCategoryID, + ParentProductCategoryID = x.ParentProductCategoryID, + Name = x.Name, + Rowguid = x.Rowguid, + ModifiedDate = x.ModifiedDate, + Products = x.Products.Select(p => new ProductDTO + { + ProductID = p.ProductID, + Name = p.Name, + SalesOrderDetails = p.SalesOrderDetails.Select(od => new SalesOrderDetailDTO + { + SalesOrderDetailID = od.SalesOrderDetailID, + OrderQty = od.OrderQty, + ProductID = od.ProductID + }).ToList() + }).ToList() + }) + .FirstOrDefaultAsync(); + + return Ok(categories); + } + } +} diff --git a/ChattyIO/ChattyIO/ChattyIO/DTOs/ProductCategoryDTO.cs b/ChattyIO/ChattyIO/ChattyIO/DTOs/ProductCategoryDTO.cs new file mode 100644 index 0000000..7037fdb --- /dev/null +++ b/ChattyIO/ChattyIO/ChattyIO/DTOs/ProductCategoryDTO.cs @@ -0,0 +1,12 @@ +namespace ChattyIO.DTOs +{ + public class ProductCategoryDTO + { + public int ProductCategoryID { get; set; } + public int? ParentProductCategoryID { get; set; } + public string Name { get; set; } + public Guid Rowguid { get; set; } + public DateTime ModifiedDate { get; set; } + public ICollection Products { get; set; } + } +} diff --git a/ChattyIO/ChattyIO/ChattyIO/DTOs/ProductDTO.cs b/ChattyIO/ChattyIO/ChattyIO/DTOs/ProductDTO.cs new file mode 100644 index 0000000..41e448c --- /dev/null +++ b/ChattyIO/ChattyIO/ChattyIO/DTOs/ProductDTO.cs @@ -0,0 +1,9 @@ +namespace ChattyIO.DTOs +{ + public class ProductDTO + { + public int ProductID { get; set; } + public string Name { get; set; } + public ICollection SalesOrderDetails { get; set; } + } +} diff --git a/ChattyIO/ChattyIO/ChattyIO/DTOs/SalesOrderDetailDTO.cs b/ChattyIO/ChattyIO/ChattyIO/DTOs/SalesOrderDetailDTO.cs new file mode 100644 index 0000000..e8344a5 --- /dev/null +++ b/ChattyIO/ChattyIO/ChattyIO/DTOs/SalesOrderDetailDTO.cs @@ -0,0 +1,9 @@ +namespace ChattyIO.DTOs +{ + public class SalesOrderDetailDTO + { + public int SalesOrderDetailID { get; set; } + public int OrderQty { get; set; } + public int ProductID { get; set; } + } +} diff --git a/ChattyIO/ChattyIO/ChattyIO/Model/AdventureWorksProductContext.cs b/ChattyIO/ChattyIO/ChattyIO/Model/AdventureWorksProductContext.cs new file mode 100644 index 0000000..1fc8465 --- /dev/null +++ b/ChattyIO/ChattyIO/ChattyIO/Model/AdventureWorksProductContext.cs @@ -0,0 +1,51 @@ +using Microsoft.EntityFrameworkCore; + +namespace ChattyIO.Model +{ + public class AdventureWorksProductContext : DbContext + { + public AdventureWorksProductContext() + { + } + + public AdventureWorksProductContext(DbContextOptions options) + : base(options) + { + } + + // DbSet properties for each table in your database + public DbSet ProductCategories { get; set; } + public DbSet Products { get; set; } + public DbSet SalesOrderDetails { get; set; } + public DbSet SalesOrderHeaders { get; set; } + + // Configure model relationships using Fluent API + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity() + .ToTable("ProductCategory", "SalesLT"); + + // Configuring SalesOrderDetail primary key (composite key) + modelBuilder.Entity() + .ToTable("SalesOrderDetail", "SalesLT") + .HasKey(sod => new { sod.SalesOrderID, sod.SalesOrderDetailID }); + + // Configuring relationships between Product and ProductCategory + modelBuilder.Entity() + .ToTable("Product", "SalesLT") + .HasOne(p => p.ProductCategory) + .WithMany(pc => pc.Products) + .HasForeignKey(p => p.ProductCategoryID); + + // Configuring relationships between Product and SalesOrderDetail + modelBuilder.Entity() + .ToTable("SalesOrderDetail", "SalesLT") + .HasOne(sod => sod.Product) + .WithMany(p => p.SalesOrderDetails) + .HasForeignKey(sod => sod.ProductID); + + base.OnModelCreating(modelBuilder); + } + + } +} diff --git a/ChattyIO/ChattyIO/ChattyIO/Model/Product.cs b/ChattyIO/ChattyIO/ChattyIO/Model/Product.cs new file mode 100644 index 0000000..eb3d146 --- /dev/null +++ b/ChattyIO/ChattyIO/ChattyIO/Model/Product.cs @@ -0,0 +1,57 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; + +namespace ChattyIO.Model +{ + public class Product + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int ProductID { get; set; } + + [Required] + public string Name { get; set; } + + [Required] + [MaxLength(25)] + public string ProductNumber { get; set; } + + [MaxLength(15)] + public string Color { get; set; } + + public decimal StandardCost { get; set; } + + public decimal ListPrice { get; set; } + + [MaxLength(5)] + public string Size { get; set; } + + public decimal? Weight { get; set; } + + public int? ProductCategoryID { get; set; } + + public int? ProductModelID { get; set; } + + public DateTime SellStartDate { get; set; } + + public DateTime? SellEndDate { get; set; } + + public DateTime? DiscontinuedDate { get; set; } + + public byte[] ThumbNailPhoto { get; set; } + + [MaxLength(50)] + public string ThumbnailPhotoFileName { get; set; } + + public Guid Rowguid { get; set; } + + public DateTime ModifiedDate { get; set; } + + // Navigation property for related SalesOrderDetails + public ICollection SalesOrderDetails { get; set; } + + // Navigation property to ProductCategory + public ProductCategory ProductCategory { get; set; } + } + +} diff --git a/ChattyIO/ChattyIO/ChattyIO/Model/ProductCategory.cs b/ChattyIO/ChattyIO/ChattyIO/Model/ProductCategory.cs new file mode 100644 index 0000000..cb8d141 --- /dev/null +++ b/ChattyIO/ChattyIO/ChattyIO/Model/ProductCategory.cs @@ -0,0 +1,25 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; + +namespace ChattyIO.Model +{ + public class ProductCategory + { + [Key] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int ProductCategoryID { get; set; } + + public int? ParentProductCategoryID { get; set; } + + [Required] + public string Name { get; set; } + + public Guid Rowguid { get; set; } + + public DateTime ModifiedDate { get; set; } + + // Navigation property for related Products + public ICollection Products { get; set; } + } + +} diff --git a/ChattyIO/ChattyIO/ChattyIO/Model/SalesOrderDetail.cs b/ChattyIO/ChattyIO/ChattyIO/Model/SalesOrderDetail.cs new file mode 100644 index 0000000..5f8f14a --- /dev/null +++ b/ChattyIO/ChattyIO/ChattyIO/Model/SalesOrderDetail.cs @@ -0,0 +1,34 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; + +namespace ChattyIO.Model +{ + public class SalesOrderDetail + { + [Key, Column(Order = 0)] + public int SalesOrderID { get; set; } + + [Key, Column(Order = 1)] + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] + public int SalesOrderDetailID { get; set; } + + public short OrderQty { get; set; } + + public int ProductID { get; set; } + + public decimal UnitPrice { get; set; } + + public decimal UnitPriceDiscount { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Computed)] + public decimal LineTotal { get; set; } + + public Guid Rowguid { get; set; } + + public DateTime ModifiedDate { get; set; } + + // Navigation property to Product + public Product Product { get; set; } + } + +} diff --git a/ChattyIO/ChattyIO/ChattyIO/Model/SalesOrderHeader.cs b/ChattyIO/ChattyIO/ChattyIO/Model/SalesOrderHeader.cs new file mode 100644 index 0000000..ee5d53f --- /dev/null +++ b/ChattyIO/ChattyIO/ChattyIO/Model/SalesOrderHeader.cs @@ -0,0 +1,58 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; + +namespace ChattyIO.Model +{ + public class SalesOrderHeader + { + [Key] + public int SalesOrderID { get; set; } + + public byte RevisionNumber { get; set; } + + public DateTime OrderDate { get; set; } + + public DateTime DueDate { get; set; } + + public DateTime? ShipDate { get; set; } + + public byte Status { get; set; } + + public bool OnlineOrderFlag { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Computed)] + public string SalesOrderNumber { get; set; } + + public string PurchaseOrderNumber { get; set; } + + public string AccountNumber { get; set; } + + public int CustomerID { get; set; } + + public int? ShipToAddressID { get; set; } + + public int? BillToAddressID { get; set; } + + [MaxLength(50)] + public string ShipMethod { get; set; } + + [MaxLength(15)] + public string CreditCardApprovalCode { get; set; } + + public decimal SubTotal { get; set; } + + public decimal TaxAmt { get; set; } + + public decimal Freight { get; set; } + + [DatabaseGenerated(DatabaseGeneratedOption.Computed)] + public decimal TotalDue { get; set; } + + public string Comment { get; set; } + + public Guid Rowguid { get; set; } + + public DateTime ModifiedDate { get; set; } + } + +} diff --git a/ChattyIO/ChattyIO/ChattyIO/Program.cs b/ChattyIO/ChattyIO/ChattyIO/Program.cs new file mode 100644 index 0000000..b0fb5c2 --- /dev/null +++ b/ChattyIO/ChattyIO/ChattyIO/Program.cs @@ -0,0 +1,56 @@ +using ChattyIO.Model; +using Microsoft.AspNetCore.Diagnostics; +using Microsoft.EntityFrameworkCore; +using System.Net; + +var builder = WebApplication.CreateBuilder(args); + +// Add services to the container. + +builder.Services.AddControllers(); +// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle +builder.Services.AddEndpointsApiExplorer(); +builder.Services.AddSwaggerGen(); + +builder.Services.AddDbContext(Option => Option.UseSqlServer(builder.Configuration["AdventureWorksProductDB"])); + +var loggerFactory = LoggerFactory.Create(builder => +{ + builder.AddConsole(); +}); + +var _logger = loggerFactory.CreateLogger(); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. +if (app.Environment.IsDevelopment()) +{ + app.UseSwagger(); + app.UseSwaggerUI(); +} + +app.UseExceptionHandler(appError => +{ + appError.Run(async context => + { + context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; + context.Response.ContentType = "application/json"; + + var contextFeature = context.Features.Get(); + if (contextFeature != null) + { + _logger.LogError($"Something went wrong: {contextFeature.Error}"); + + await context.Response.WriteAsync("Internal Server Error."); + } + }); +}); + +app.UseHttpsRedirection(); + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); diff --git a/ChattyIO/ChattyIO/ChattyIO/appsettings.Development.json b/ChattyIO/ChattyIO/ChattyIO/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/ChattyIO/ChattyIO/ChattyIO/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/ChattyIO/ChattyIO/ChattyIO/appsettings.json b/ChattyIO/ChattyIO/ChattyIO/appsettings.json new file mode 100644 index 0000000..ad218d9 --- /dev/null +++ b/ChattyIO/ChattyIO/ChattyIO/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "AdventureWorksProductDB": "Server=tcp:sqlserver-na4xnouzzsx3w.database.windows.net,1433;Database=busyDatabase-na4xnouzzsx3w;Authentication=ActiveDirectoryDefault; Encrypt=True;TrustServerCertificate=false;Connection Timeout=30;" +} diff --git a/ChattyIO/ReadMe.md b/ChattyIO/ReadMe.md index 02ad6d6..cca4c7f 100644 --- a/ChattyIO/ReadMe.md +++ b/ChattyIO/ReadMe.md @@ -1,14 +1,12 @@ # ChattyIO Sample Code -The BusyFrontEnd sample code comprises the following items: +It is a sample implementation to ilustrate [Chatty I/O antipattern](https://learn.microsoft.com/azure/architecture/antipatterns/chatty-io/) -* ChattyIO solution file +The ChattyIO sample code comprises the following items: -* ChattyIO WebAPI project (*Note that this project is an Azure web application and not a cloud service*) +- ChattyIO Application -* ChattyIO.DataAccess class library - -* [Detailed Documentation][docs] +- Azure SQL database with [AdventureWorksLT sample](https://learn.microsoft.com/sql/samples/adventureworks-install-configure?view=sql-server-ver16&tabs=ssms#deploy-to-azure-sql-database) The ChattyIO WebAPI project contains two controllers: @@ -16,34 +14,95 @@ The ChattyIO WebAPI project contains two controllers: * `ChunkyProductController` -The `GetProductsInSubCategoryAsync` action of the `ChattyProductController` retrieves the details of all products held, including price histories, in the specified subcategory in the AdventureWorks2012 database. The code connects to the database and fetches the details of the subcategory, the products in the subcategory, and the price history of each product as separate database requests before formatting the data and returning the result. +The `GetProductsInSubCategoryAsync` action of the `ChattyProductController` retrieves the details of all products held, including sales order details, in the specified subcategory in the AdventureWorksLT database. The code connects to the database and fetches the details of the subcategory, the products in the subcategory, and the saless order details of each product as separate database requests before formatting the data and returning the result. The `GetProductCategoryDetailsAsync` action of the `ChunkyProductController` performs a similar task except that it fetches the data from the database in a single request. -## Configuring the project +## :rocket: Deployment guide + +Install the prerequisites and follow the steps to deploy and run the examples. + +### Prerequisites + +- Permission to create a new resource group and resources in an [Azure subscription](https://azure.com/free) +- Unix-like shell. Also available in: + - [Azure Cloud Shell](https://shell.azure.com/) + - [Windows Subsystem for Linux (WSL)](https://learn.microsoft.com/windows/wsl/install) +- [Git](https://git-scm.com/downloads) +- [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) +- [Azure CLI](https://learn.microsoft.com/cli/azure/install-azure-cli) +- Optionally, an IDE, like [Visual Studio](https://visualstudio.microsoft.com/downloads/) or [Visual Studio Code](https://code.visualstudio.com/). + +### Steps + +1. Clone this repository to your workstation and navigate to the working directory. + + ```bash + git clone https://github.com/mspnp/performance-optimization + cd ChattyIO + ``` + +1. Log into Azure and create an empty resource group. + + ```bash + az login + az account set -s + + export USER= + export USER_OBJECTID= + export USER_TENANTID= + + LOCATION=eastus + RESOURCEGROUP=rg-chatty-io-${LOCATION} + + az group create --name ${RESOURCEGROUP} --location ${LOCATION} + + ``` + +1. Deploy the supporting Azure resources. + It will create a database that only allows Microsoft Entra ID users, including the AdventureWorksLT sample + + ```bash + az deployment group create --resource-group ${RESOURCEGROUP} \ + -f ./bicep/main.bicep \ + -p user=${USER} \ + userObjectId=${USER_OBJECTID} \ + userTenantId=${USER_TENANTID} + ``` + +1. Configure database connection string + + On appsettings.json you need to complete with your server and database name. + + ```bash + "Server=tcp:.database.windows.net,1433;Database=;Authentication=ActiveDirectoryDefault; Encrypt=True;TrustServerCertificate=false;Connection Timeout=30;", + ``` + +1. Authenticate with a Microsoft Entra identity + + It uses [Active Directory Default](https://learn.microsoft.com/sql/connect/ado-net/sql/azure-active-directory-authentication?view=sql-server-ver16#setting-microsoft-entra-authentication) which requires authentication via AZ CLI or Visual Studio. + +1. Enable your computer to reach the Azure Database: -This project uses the [AdventureWorks2012][AdventureWorks2012] database stored by using Azure SQL Database. Create the database by using the Azure Management Portal and add the connection string to the `AdventureWorksProductContext` connection string in the web.config file for the ChattyIO WebAPI project. Note that the new Azure portal provides a simplified version of the database (AdventureWorksLT). -The AdventureWorksLT database uses a different schema from that expected by this sample -application which might not function correctly unless the full -[AdventureWorks2012][AdventureWorks2012] database is installed. + - Go to the Database Server. + - In the Network section, allow your IP address. -## Deploying the project to Azure +1. Run proyect locally -In Visual Studio Solution Explorer, right-click the ChattyIO.WebApi project and then click *Publish*. Publish the project to an Azure website. + Execute the API and then you will be able to call both endpoints -## Load testing the project +## :broom: Clean up resources -You can use [Visual Studio Online to load test](http://www.visualstudio.com/en-us/get-started/load-test-your-app-vs.aspx) the application. -For details of the load testing strategy for this sample, see [Load Testing][Load Testing]. +Most of the Azure resources deployed in the prior steps will incur ongoing charges unless removed. -## Dependencies +```bash +az group delete -n ${RESOURCEGROUP} -y +``` -This project requires: +## Contributions -* Azure SDK 2.5 +Please see our [Contributor guide](./CONTRIBUTING.md). -* An instance of the [AdventureWorks2012] database +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact with any additional questions or comments. -[docs]: docs/ChattyIO.md -[AdventureWorks2012]: https://msftdbprodsamples.codeplex.com/releases/view/37304 -[Load Testing]: docs/LoadTesting.md +With :heart: from Azure Patterns & Practices, [Azure Architecture Center](https://azure.com/architecture). \ No newline at end of file diff --git a/ChattyIO/bicep/main.bicep b/ChattyIO/bicep/main.bicep new file mode 100644 index 0000000..a97447c --- /dev/null +++ b/ChattyIO/bicep/main.bicep @@ -0,0 +1,171 @@ +@description('Location for all resources.') +param location string = resourceGroup().location + +@description('The administrator username of the SQL logical server.') +param administratorLogin string = 'myadminname' + +@description('The administrator password of the SQL logical server.') +@secure() +param administratorLoginPassword string = newGuid() + +@description('The Microsoft Entra ID user to be database admin') +param user string + +@description('The object id of the previous user') +param userObjectId string + +@description('The tenant id of the previous user') +param userTenantId string + +// --- Variables +var uniqueName = uniqueString(resourceGroup().id) + +@description('The name of the SQL logical server.') +var serverName = 'sqlserver-${uniqueName}' +@description('The name of the SQL Database.') +var sqlDBName = 'chattyIoDatabase-${uniqueName}' +var logAnalyticsWorkspaceName = 'chattyIoDatabase-${uniqueName}' + + +resource sqlServer 'Microsoft.Sql/servers@2023-08-01-preview' = { + name: serverName + location: location + properties: { + administratorLogin: administratorLogin + administratorLoginPassword: administratorLoginPassword + minimalTlsVersion: '1.2' + version: '12.0' + publicNetworkAccess: 'Enabled' + } + resource allowAzureServicesRule 'firewallRules' = { + name: 'AllowAllWindowsAzureIps' + properties: { + startIpAddress: '0.0.0.0' + endIpAddress: '0.0.0.0' + } + } + identity: { + type: 'SystemAssigned' + } + resource activeDirectoryAdmin 'administrators@2023-08-01-preview' = { + name: 'ActiveDirectory' + properties: { + administratorType: 'ActiveDirectory' + login: user + sid: userObjectId + tenantId: userTenantId + } + } + resource sqlADOnlyAuth 'azureADOnlyAuthentications@2023-08-01-preview' = { + name: 'Default' + properties: { + azureADOnlyAuthentication: true + } + dependsOn: [ + activeDirectoryAdmin + ] + } +} + +resource diagnosticSettingsSqlServer 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { + scope: sqlServer + name: '${sqlServer.name}-diag' + properties: { + workspaceId: logAnalyticsWorkspace.id + } +} + +resource auditingServerSettings 'Microsoft.Sql/servers/auditingSettings@2021-11-01-preview' = { + parent: sqlServer + name: 'default' + properties: { + state: 'Enabled' + isAzureMonitorTargetEnabled: true + auditActionsAndGroups: [ + 'SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP' + 'FAILED_DATABASE_AUTHENTICATION_GROUP' + 'BATCH_COMPLETED_GROUP' + ] + } +} + +resource sqlVulnerabilityAssessment 'Microsoft.Sql/servers/sqlVulnerabilityAssessments@2022-11-01-preview' = { + name: 'default' + parent: sqlServer + properties: { + state: 'Enabled' + } + dependsOn: [ + auditingServerSettings + ] +} + +resource sqlDB 'Microsoft.Sql/servers/databases@2023-08-01-preview' = { + parent: sqlServer + name: sqlDBName + location: location + sku: { + name: 'Basic' + tier: 'Basic' + capacity: 5 + } + properties: { + zoneRedundant: false + readScale: 'Disabled' + requestedBackupStorageRedundancy: 'Local' + collation: 'SQL_Latin1_General_CP1_CI_AS' + sampleName: 'AdventureWorksLT' + } + dependsOn: [ + sqlServer::sqlADOnlyAuth + sqlServer::activeDirectoryAdmin + auditingServerSettings + sqlVulnerabilityAssessment + ] +} + +resource auditingDbSettings 'Microsoft.Sql/servers/databases/auditingSettings@2023-08-01-preview' = { + parent: sqlDB + name: 'default' + properties: { + retentionDays: 0 + auditActionsAndGroups: [ + 'SUCCESSFUL_DATABASE_AUTHENTICATION_GROUP' + 'FAILED_DATABASE_AUTHENTICATION_GROUP' + 'BATCH_COMPLETED_GROUP' + ] + isAzureMonitorTargetEnabled: true + isManagedIdentityInUse: false + state: 'Enabled' + storageAccountSubscriptionId: '00000000-0000-0000-0000-000000000000' + } +} + +resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { + name: logAnalyticsWorkspaceName + location: location + properties: { + sku: { + name: 'PerGB2018' // Example SKU, adjust as needed + } + retentionInDays: 30 // Adjust retention period as needed + } +} + +resource diagnosticSettingsSqlDb 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = { + scope: sqlDB + name: '${sqlDB.name}-diag' + properties: { + workspaceId: logAnalyticsWorkspace.id + logs: [ + { + category: 'SQLSecurityAuditEvents' + enabled: true + retentionPolicy: { + days: 0 + enabled: false + } + } + ] + } +} From 60492fe0518796ad9abe657b90cffe275e3e36bd Mon Sep 17 00:00:00 2001 From: Federico Arambarri Date: Tue, 15 Oct 2024 14:28:14 -0300 Subject: [PATCH 2/5] Changing connection string --- ChattyIO/ChattyIO/ChattyIO/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChattyIO/ChattyIO/ChattyIO/appsettings.json b/ChattyIO/ChattyIO/ChattyIO/appsettings.json index ad218d9..8176580 100644 --- a/ChattyIO/ChattyIO/ChattyIO/appsettings.json +++ b/ChattyIO/ChattyIO/ChattyIO/appsettings.json @@ -6,5 +6,5 @@ } }, "AllowedHosts": "*", - "AdventureWorksProductDB": "Server=tcp:sqlserver-na4xnouzzsx3w.database.windows.net,1433;Database=busyDatabase-na4xnouzzsx3w;Authentication=ActiveDirectoryDefault; Encrypt=True;TrustServerCertificate=false;Connection Timeout=30;" + "AdventureWorksProductDB": "Server=tcp:.database.windows.net,1433;Database=;Authentication=ActiveDirectoryDefault; Encrypt=True;TrustServerCertificate=false;Connection Timeout=30;" } From 2bd5bfb3bacecc3c04d62ae3cdd715bd3f87a5f6 Mon Sep 17 00:00:00 2001 From: Federico Arambarri Date: Wed, 16 Oct 2024 08:22:30 -0300 Subject: [PATCH 3/5] script improvement --- ChattyIO/bicep/main.bicep | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/ChattyIO/bicep/main.bicep b/ChattyIO/bicep/main.bicep index a97447c..6033b50 100644 --- a/ChattyIO/bicep/main.bicep +++ b/ChattyIO/bicep/main.bicep @@ -23,8 +23,8 @@ var uniqueName = uniqueString(resourceGroup().id) @description('The name of the SQL logical server.') var serverName = 'sqlserver-${uniqueName}' @description('The name of the SQL Database.') -var sqlDBName = 'chattyIoDatabase-${uniqueName}' -var logAnalyticsWorkspaceName = 'chattyIoDatabase-${uniqueName}' +var sqlDBName = 'extraneosFeatching-${uniqueName}' +var logAnalyticsWorkspaceName = 'extraneosFeatching-${uniqueName}' resource sqlServer 'Microsoft.Sql/servers@2023-08-01-preview' = { @@ -100,6 +100,25 @@ resource sqlVulnerabilityAssessment 'Microsoft.Sql/servers/sqlVulnerabilityAsses ] } +resource solutions_SQLAuditing_githubmetrics 'Microsoft.OperationsManagement/solutions@2015-11-01-preview' = { + name: 'SolutionSQLAuditing${logAnalyticsWorkspace.name}' + location: location + plan: { + name: 'SQLAuditing${sqlDB.name}' + promotionCode: '' + product: 'SQLAuditing' + publisher: 'Microsoft' + } + properties: { + workspaceResourceId: logAnalyticsWorkspace.id + containedResources: [ + '${resourceId('Microsoft.OperationalInsights/workspaces', logAnalyticsWorkspace.name)}/views/SQLSecurityInsights' + '${resourceId('Microsoft.OperationalInsights/workspaces', logAnalyticsWorkspace.name)}/views/SQLAccessToSensitiveData' + ] + referencedResources: [] + } +} + resource sqlDB 'Microsoft.Sql/servers/databases@2023-08-01-preview' = { parent: sqlServer name: sqlDBName From ec46b109735186d14b5ed536bc2576b54c55e20e Mon Sep 17 00:00:00 2001 From: Federico Arambarri <62260324+v-fearam@users.noreply.github.com> Date: Thu, 17 Oct 2024 11:08:47 -0300 Subject: [PATCH 4/5] Update main.bicep --- ChattyIO/bicep/main.bicep | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ChattyIO/bicep/main.bicep b/ChattyIO/bicep/main.bicep index 6033b50..5fbb013 100644 --- a/ChattyIO/bicep/main.bicep +++ b/ChattyIO/bicep/main.bicep @@ -23,8 +23,8 @@ var uniqueName = uniqueString(resourceGroup().id) @description('The name of the SQL logical server.') var serverName = 'sqlserver-${uniqueName}' @description('The name of the SQL Database.') -var sqlDBName = 'extraneosFeatching-${uniqueName}' -var logAnalyticsWorkspaceName = 'extraneosFeatching-${uniqueName}' +var sqlDBName = 'chattyio-${uniqueName}' +var logAnalyticsWorkspaceName = 'chattyio-${uniqueName}' resource sqlServer 'Microsoft.Sql/servers@2023-08-01-preview' = { From 40aa82841dac03c63e922be91aacfefc4190f3e6 Mon Sep 17 00:00:00 2001 From: Federico Arambarri Date: Wed, 23 Oct 2024 07:35:27 -0300 Subject: [PATCH 5/5] Improve Readme --- ChattyIO/ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChattyIO/ReadMe.md b/ChattyIO/ReadMe.md index cca4c7f..d77a699 100644 --- a/ChattyIO/ReadMe.md +++ b/ChattyIO/ReadMe.md @@ -14,7 +14,7 @@ The ChattyIO WebAPI project contains two controllers: * `ChunkyProductController` -The `GetProductsInSubCategoryAsync` action of the `ChattyProductController` retrieves the details of all products held, including sales order details, in the specified subcategory in the AdventureWorksLT database. The code connects to the database and fetches the details of the subcategory, the products in the subcategory, and the saless order details of each product as separate database requests before formatting the data and returning the result. +The `GetProductsInSubCategoryAsync` action of the `ChattyProductController` retrieves detailed information about all products, including their sales order details, within a specified subcategory in the AdventureWorksLT database. The action connects to the database, retrieves the subcategory information, fetches the associated products, and obtains the sales order details for each product. These steps are performed as separate database requests before the data is formatted and returned as the result. The `GetProductCategoryDetailsAsync` action of the `ChunkyProductController` performs a similar task except that it fetches the data from the database in a single request.