diff --git a/ExtraneousFetching/ExtraneousFetching.DataAccess/AdventureWorksContext.cs b/ExtraneousFetching/ExtraneousFetching.DataAccess/AdventureWorksContext.cs deleted file mode 100644 index 39c5769..0000000 --- a/ExtraneousFetching/ExtraneousFetching.DataAccess/AdventureWorksContext.cs +++ /dev/null @@ -1,30 +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.Data.Entity.Infrastructure.Interception; -using ExtraneousFetching.DataAccess.Mapping; - -namespace ExtraneousFetching.DataAccess -{ - public class AdventureWorksContext : DbContext - { - static AdventureWorksContext() - { - Database.SetInitializer(null); - DbInterception.Add(new ConnectionInterceptor()); - } - - protected override void OnModelCreating(DbModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - modelBuilder.Configurations.Add(new ProductConfiguration()); - modelBuilder.Configurations.Add(new SalesOrderHeaderConfiguration()); - } - - public DbSet Products { get; set; } - - public DbSet SalesOrderHeaders { get; set; } - } -} \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.DataAccess/ConnectionInterceptor.cs b/ExtraneousFetching/ExtraneousFetching.DataAccess/ConnectionInterceptor.cs deleted file mode 100644 index 6d977f0..0000000 --- a/ExtraneousFetching/ExtraneousFetching.DataAccess/ConnectionInterceptor.cs +++ /dev/null @@ -1,116 +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; -using System.Data.Common; -using System.Data.Entity.Infrastructure.Interception; -using System.Data.SqlClient; - -namespace ExtraneousFetching.DataAccess -{ - public class ConnectionInterceptor : IDbConnectionInterceptor - { - public void Opened(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - ((SqlConnection)connection).StatisticsEnabled = true; - } - - public void Closing(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - var stats = ((SqlConnection)connection).RetrieveStatistics(); - var bytesReceived = stats["BytesReceived"]; - - // TODO: Observe the database response size using the monitoring system of your choice. - } - - #region unused - public void BeginningTransaction(DbConnection connection, BeginTransactionInterceptionContext interceptionContext) - { - } - - public void BeganTransaction(DbConnection connection, BeginTransactionInterceptionContext interceptionContext) - { - } - - public void Closed(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - } - - public void ConnectionStringGetting(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - } - - public void ConnectionStringGot(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - } - - public void ConnectionStringSetting(DbConnection connection, DbConnectionPropertyInterceptionContext interceptionContext) - { - } - - public void ConnectionStringSet(DbConnection connection, DbConnectionPropertyInterceptionContext interceptionContext) - { - } - - public void ConnectionTimeoutGetting(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - } - - public void ConnectionTimeoutGot(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - } - - public void DatabaseGetting(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - } - - public void DatabaseGot(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - } - - public void DataSourceGetting(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - } - - public void DataSourceGot(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - } - - public void Disposing(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - } - - public void Disposed(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - } - - public void EnlistingTransaction(DbConnection connection, EnlistTransactionInterceptionContext interceptionContext) - { - } - - public void EnlistedTransaction(DbConnection connection, EnlistTransactionInterceptionContext interceptionContext) - { - } - - public void Opening(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - } - - public void ServerVersionGetting(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - } - - public void ServerVersionGot(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - } - - public void StateGetting(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - } - - public void StateGot(DbConnection connection, DbConnectionInterceptionContext interceptionContext) - { - } -#endregion - } -} diff --git a/ExtraneousFetching/ExtraneousFetching.DataAccess/ExtraneousFetching.DataAccess.csproj b/ExtraneousFetching/ExtraneousFetching.DataAccess/ExtraneousFetching.DataAccess.csproj deleted file mode 100644 index 017fcf8..0000000 --- a/ExtraneousFetching/ExtraneousFetching.DataAccess/ExtraneousFetching.DataAccess.csproj +++ /dev/null @@ -1,70 +0,0 @@ - - - - - Debug - AnyCPU - {35AA1C25-E5CE-456B-A88C-6E8A69BECB2E} - Library - Properties - ExtraneousFetching.DataAccess - ExtraneousFetching.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/ExtraneousFetching/ExtraneousFetching.DataAccess/Mapping/ProductionMapping.cs b/ExtraneousFetching/ExtraneousFetching.DataAccess/Mapping/ProductionMapping.cs deleted file mode 100644 index a02161e..0000000 --- a/ExtraneousFetching/ExtraneousFetching.DataAccess/Mapping/ProductionMapping.cs +++ /dev/null @@ -1,44 +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; - -namespace ExtraneousFetching.DataAccess.Mapping -{ - //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); - Property(x => x.ProductNumber).HasColumnName("ProductNumber").IsRequired().HasMaxLength(25); - Property(x => x.MakeFlag).HasColumnName("MakeFlag").IsRequired(); - Property(x => x.FinishedGoodsFlag).HasColumnName("FinishedGoodsFlag").IsRequired(); - Property(x => x.Color).HasColumnName("Color").IsOptional().HasMaxLength(15); - Property(x => x.SafetyStockLevel).HasColumnName("SafetyStockLevel").IsRequired(); - Property(x => x.ReorderPoint).HasColumnName("ReorderPoint").IsRequired(); - Property(x => x.StandardCost).HasColumnName("StandardCost").IsRequired().HasPrecision(19, 4); - Property(x => x.ListPrice).HasColumnName("ListPrice").IsRequired().HasPrecision(19, 4); - Property(x => x.Size).HasColumnName("Size").IsOptional().HasMaxLength(5); - Property(x => x.SizeUnitMeasureCode).HasColumnName("SizeUnitMeasureCode").IsOptional().IsFixedLength().HasMaxLength(3); - Property(x => x.WeightUnitMeasureCode).HasColumnName("WeightUnitMeasureCode").IsOptional().IsFixedLength().HasMaxLength(3); - Property(x => x.Weight).HasColumnName("Weight").IsOptional().HasPrecision(8, 2); - Property(x => x.DaysToManufacture).HasColumnName("DaysToManufacture").IsRequired(); - Property(x => x.ProductLine).HasColumnName("ProductLine").IsOptional().IsFixedLength().HasMaxLength(2); - Property(x => x.Class).HasColumnName("Class").IsOptional().IsFixedLength().HasMaxLength(2); - Property(x => x.Style).HasColumnName("Style").IsOptional().IsFixedLength().HasMaxLength(2); - Property(x => x.ProductSubcategoryId).HasColumnName("ProductSubcategoryID").IsOptional(); - Property(x => x.ProductModelId).HasColumnName("ProductModelID").IsOptional(); - Property(x => x.SellStartDate).HasColumnName("SellStartDate").IsRequired(); - Property(x => x.SellEndDate).HasColumnName("SellEndDate").IsOptional(); - Property(x => x.DiscontinuedDate).HasColumnName("DiscontinuedDate").IsOptional(); - Property(x => x.Rowguid).HasColumnName("rowguid").IsRequired(); - Property(x => x.ModifiedDate).HasColumnName("ModifiedDate").IsRequired(); - } - } -} \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.DataAccess/Mapping/SalesMapping.cs b/ExtraneousFetching/ExtraneousFetching.DataAccess/Mapping/SalesMapping.cs deleted file mode 100644 index 77f3a87..0000000 --- a/ExtraneousFetching/ExtraneousFetching.DataAccess/Mapping/SalesMapping.cs +++ /dev/null @@ -1,45 +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; - -namespace ExtraneousFetching.DataAccess.Mapping -{ - // SalesOrderHeader - internal class SalesOrderHeaderConfiguration : EntityTypeConfiguration - { - public SalesOrderHeaderConfiguration(string schema = "Sales") - { - ToTable(schema + ".SalesOrderHeader"); - HasKey(x => x.SalesOrderId); - - Property(x => x.SalesOrderId).HasColumnName("SalesOrderID").IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); - Property(x => x.RevisionNumber).HasColumnName("RevisionNumber").IsRequired(); - Property(x => x.OrderDate).HasColumnName("OrderDate").IsRequired(); - Property(x => x.DueDate).HasColumnName("DueDate").IsRequired(); - Property(x => x.ShipDate).HasColumnName("ShipDate").IsOptional(); - Property(x => x.Status).HasColumnName("Status").IsRequired(); - Property(x => x.OnlineOrderFlag).HasColumnName("OnlineOrderFlag").IsRequired(); - Property(x => x.SalesOrderNumber).HasColumnName("SalesOrderNumber").IsRequired().HasMaxLength(25).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed); - Property(x => x.PurchaseOrderNumber).HasColumnName("PurchaseOrderNumber").IsOptional().HasMaxLength(25); - Property(x => x.AccountNumber).HasColumnName("AccountNumber").IsOptional().HasMaxLength(15); - Property(x => x.CustomerId).HasColumnName("CustomerID").IsRequired(); - Property(x => x.SalesPersonId).HasColumnName("SalesPersonID").IsOptional(); - Property(x => x.TerritoryId).HasColumnName("TerritoryID").IsOptional(); - Property(x => x.BillToAddressId).HasColumnName("BillToAddressID").IsRequired(); - Property(x => x.ShipToAddressId).HasColumnName("ShipToAddressID").IsRequired(); - Property(x => x.ShipMethodId).HasColumnName("ShipMethodID").IsRequired(); - Property(x => x.CreditCardId).HasColumnName("CreditCardID").IsOptional(); - Property(x => x.CreditCardApprovalCode).HasColumnName("CreditCardApprovalCode").IsOptional().IsUnicode(false).HasMaxLength(15); - Property(x => x.CurrencyRateId).HasColumnName("CurrencyRateID").IsOptional(); - Property(x => x.SubTotal).HasColumnName("SubTotal").IsRequired().HasPrecision(19, 4); - Property(x => x.TaxAmt).HasColumnName("TaxAmt").IsRequired().HasPrecision(19, 4); - Property(x => x.Freight).HasColumnName("Freight").IsRequired().HasPrecision(19, 4); - Property(x => x.TotalDue).HasColumnName("TotalDue").IsRequired().HasPrecision(19, 4).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Computed); - Property(x => x.Comment).HasColumnName("Comment").IsOptional().HasMaxLength(128); - Property(x => x.Rowguid).HasColumnName("rowguid").IsRequired(); - Property(x => x.ModifiedDate).HasColumnName("ModifiedDate").IsRequired(); - } - } -} \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.DataAccess/Product.cs b/ExtraneousFetching/ExtraneousFetching.DataAccess/Product.cs deleted file mode 100644 index 1e16c33..0000000 --- a/ExtraneousFetching/ExtraneousFetching.DataAccess/Product.cs +++ /dev/null @@ -1,60 +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 ExtraneousFetching.DataAccess -{ - public class Product - { - public int ProductId { get; set; } // ProductID (Primary key) - - public string Name { get; set; } // Name - - public string ProductNumber { get; set; } // ProductNumber - - public bool MakeFlag { get; set; } // MakeFlag - - public bool FinishedGoodsFlag { get; set; } // FinishedGoodsFlag - - public string Color { get; set; } // Color - - public short SafetyStockLevel { get; set; } // SafetyStockLevel - - public short ReorderPoint { get; set; } // ReorderPoint - - public decimal StandardCost { get; set; } // StandardCost - - public decimal ListPrice { get; set; } // ListPrice - - public string Size { get; set; } // Size - - public string SizeUnitMeasureCode { get; set; } // SizeUnitMeasureCode - - public string WeightUnitMeasureCode { get; set; } // WeightUnitMeasureCode - - public decimal? Weight { get; set; } // Weight - - public int DaysToManufacture { get; set; } // DaysToManufacture - - public string ProductLine { get; set; } // ProductLine - - public string Class { get; set; } // Class - - public string Style { get; set; } // Style - - public int? ProductSubcategoryId { get; set; } // ProductSubcategoryID - - public int? ProductModelId { get; set; } // ProductModelID - - public DateTime SellStartDate { get; set; } // SellStartDate - - public DateTime? SellEndDate { get; set; } // SellEndDate - - public DateTime? DiscontinuedDate { get; set; } // DiscontinuedDate - - public Guid Rowguid { get; set; } // rowguid - - public DateTime ModifiedDate { get; set; } // ModifiedDate - } -} \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.DataAccess/Properties/AssemblyInfo.cs b/ExtraneousFetching/ExtraneousFetching.DataAccess/Properties/AssemblyInfo.cs deleted file mode 100644 index ee6715e..0000000 --- a/ExtraneousFetching/ExtraneousFetching.DataAccess/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.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("ExtraneousFetching.DataAccess")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("ExtraneousFetching.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("a1e4fb9c-d6b3-4dc6-b3f3-cc52f456daa8")] - -// 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 Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.DataAccess/SalesOrderHeader.cs b/ExtraneousFetching/ExtraneousFetching.DataAccess/SalesOrderHeader.cs deleted file mode 100644 index 4b51b64..0000000 --- a/ExtraneousFetching/ExtraneousFetching.DataAccess/SalesOrderHeader.cs +++ /dev/null @@ -1,62 +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 ExtraneousFetching.DataAccess -{ - public class SalesOrderHeader - { - public int SalesOrderId { get; set; } // SalesOrderID (Primary key) - - public byte RevisionNumber { get; set; } // RevisionNumber - - public DateTime OrderDate { get; set; } // OrderDate - - public DateTime DueDate { get; set; } // DueDate - - public DateTime? ShipDate { get; set; } // ShipDate - - public byte Status { get; set; } // Status - - public bool OnlineOrderFlag { get; set; } // OnlineOrderFlag - - public string SalesOrderNumber { get; internal set; } // SalesOrderNumber - - public string PurchaseOrderNumber { get; set; } // PurchaseOrderNumber - - public string AccountNumber { get; set; } // AccountNumber - - public int CustomerId { get; set; } // CustomerID - - public int? SalesPersonId { get; set; } // SalesPersonID - - public int? TerritoryId { get; set; } // TerritoryID - - public int BillToAddressId { get; set; } // BillToAddressID - - public int ShipToAddressId { get; set; } // ShipToAddressID - - public int ShipMethodId { get; set; } // ShipMethodID - - public int? CreditCardId { get; set; } // CreditCardID - - public string CreditCardApprovalCode { get; set; } // CreditCardApprovalCode - - public int? CurrencyRateId { get; set; } // CurrencyRateID - - public decimal SubTotal { get; set; } // SubTotal - - public decimal TaxAmt { get; set; } // TaxAmt - - public decimal Freight { get; set; } // Freight - - public decimal TotalDue { get; internal set; } // TotalDue - - public string Comment { get; set; } // Comment - - public Guid Rowguid { get; set; } // rowguid - - public DateTime ModifiedDate { get; set; } // ModifiedDate - } -} \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.DataAccess/packages.config b/ExtraneousFetching/ExtraneousFetching.DataAccess/packages.config deleted file mode 100644 index 6138937..0000000 --- a/ExtraneousFetching/ExtraneousFetching.DataAccess/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.WebApi/App_Start/ResponseHandler.cs b/ExtraneousFetching/ExtraneousFetching.WebApi/App_Start/ResponseHandler.cs deleted file mode 100644 index 8803694..0000000 --- a/ExtraneousFetching/ExtraneousFetching.WebApi/App_Start/ResponseHandler.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.Net.Http; -using System.Threading; -using System.Threading.Tasks; - -namespace ExtraneousFetching.WebApi -{ - public class ResponseHandler : DelegatingHandler - { - protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - var response = await base.SendAsync(request, cancellationToken); - if (response.Content != null) - { - await response.Content.LoadIntoBufferAsync(); - var contentLength = response.Content.Headers.ContentLength; - - // TODO: Observe the http response size using the monitoring system of your choice. - - } - - return response; - } - } -} \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.WebApi/App_Start/WebApiConfig.cs b/ExtraneousFetching/ExtraneousFetching.WebApi/App_Start/WebApiConfig.cs deleted file mode 100644 index b104f60..0000000 --- a/ExtraneousFetching/ExtraneousFetching.WebApi/App_Start/WebApiConfig.cs +++ /dev/null @@ -1,25 +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 ExtraneousFetching.WebApi -{ - public static class WebApiConfig - { - public static void Register(HttpConfiguration config) - { - // Web API configuration and services - config.MessageHandlers.Add(new ResponseHandler()); - - // Web API routes - config.MapHttpAttributeRoutes(); - - config.Routes.MapHttpRoute( - name: "DefaultApi", - routeTemplate: "api/{controller}/{id}", - defaults: new { id = RouteParameter.Optional } - ); - } - } -} \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.WebApi/Controllers/UnnecessaryFieldsController.cs b/ExtraneousFetching/ExtraneousFetching.WebApi/Controllers/UnnecessaryFieldsController.cs deleted file mode 100644 index 1e7e41a..0000000 --- a/ExtraneousFetching/ExtraneousFetching.WebApi/Controllers/UnnecessaryFieldsController.cs +++ /dev/null @@ -1,46 +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 ExtraneousFetching.DataAccess; -using ExtraneousFetching.WebApi.Models; - -namespace ExtraneousFetching.WebApi.Controllers -{ - public class UnnecessaryFieldsController : ApiController - { - [HttpGet] - [Route("api/allfields")] - public async Task GetAllFieldsAsync() - { - using (var context = new AdventureWorksContext()) - { - // execute the query - var products = await context.Products.ToListAsync(); - - // project fields from the query results - var result = products.Select(p => new ProductInfo { Id = p.ProductId, Name = p.Name }); - - return Ok(result); - } - } - - [HttpGet] - [Route("api/requiredfields")] - public async Task GetRequiredFieldsAsync() - { - using (var context = new AdventureWorksContext()) - { - // project fields as part of the query itself - var result = await context.Products - .Select(p => new ProductInfo {Id = p.ProductId, Name = p.Name}) - .ToListAsync(); - - return Ok(result); - } - } - } -} \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.WebApi/Controllers/UnnecessaryRowsController.cs b/ExtraneousFetching/ExtraneousFetching.WebApi/Controllers/UnnecessaryRowsController.cs deleted file mode 100644 index 6012645..0000000 --- a/ExtraneousFetching/ExtraneousFetching.WebApi/Controllers/UnnecessaryRowsController.cs +++ /dev/null @@ -1,43 +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 ExtraneousFetching.DataAccess; - -namespace ExtraneousFetching.WebApi.Controllers -{ - public class UnnecessaryRowsController : ApiController - { - [HttpGet] - [Route("api/aggregateonclient")] - public async Task AggregateOnClientAsync() - { - using (var context = new AdventureWorksContext()) - { - // fetch all order totals from the database - var orderAmounts = await context.SalesOrderHeaders.Select(soh => soh.TotalDue).ToListAsync(); - - // sum the order totals here in the controller - var total = orderAmounts.Sum(); - - return Ok(total); - } - } - - [HttpGet] - [Route("api/aggregateondatabase")] - public async Task AggregateOnDatabaseAsync() - { - using (var context = new AdventureWorksContext()) - { - // fetch the sum of all order totals, as computed on the database server - var total = await context.SalesOrderHeaders.SumAsync(soh => soh.TotalDue); - - return Ok(total); - } - } - } -} \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.WebApi/ExtraneousFetching.WebApi.csproj b/ExtraneousFetching/ExtraneousFetching.WebApi/ExtraneousFetching.WebApi.csproj deleted file mode 100644 index d2b7561..0000000 --- a/ExtraneousFetching/ExtraneousFetching.WebApi/ExtraneousFetching.WebApi.csproj +++ /dev/null @@ -1,152 +0,0 @@ - - - - - Debug - AnyCPU - - - 2.0 - {6EF1565F-1384-4C18-8556-8913388D9281} - {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} - Library - Properties - ExtraneousFetching.WebApi - ExtraneousFetching.WebApi - v4.5.1 - true - - - - - dc39d717 - /subscriptions/a3e494d0-a5b0-4b41-b7a6-7f0952471687/resourcegroups/Default-ApplicationInsights-CentralUS/providers/microsoft.insights/components/ExtraneousFetching.WebApi - - - - 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 - - - - - - - Designer - - - - Web.config - - - Web.config - - - - - {35aa1c25-e5ce-456b-a88c-6e8a69becb2e} - ExtraneousFetching.DataAccess - - - - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - - - - - - - - - True - True - 2830 - / - http://localhost:16357/ - False - False - - - False - - - - - - \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.WebApi/Global.asax b/ExtraneousFetching/ExtraneousFetching.WebApi/Global.asax deleted file mode 100644 index 76770fc..0000000 --- a/ExtraneousFetching/ExtraneousFetching.WebApi/Global.asax +++ /dev/null @@ -1 +0,0 @@ -<%@ Application Codebehind="Global.asax.cs" Inherits="ExtraneousFetching.WebApi.WebApiApplication" Language="C#" %> diff --git a/ExtraneousFetching/ExtraneousFetching.WebApi/Global.asax.cs b/ExtraneousFetching/ExtraneousFetching.WebApi/Global.asax.cs deleted file mode 100644 index b86f83a..0000000 --- a/ExtraneousFetching/ExtraneousFetching.WebApi/Global.asax.cs +++ /dev/null @@ -1,15 +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 ExtraneousFetching.WebApi -{ - public class WebApiApplication : System.Web.HttpApplication - { - protected void Application_Start() - { - GlobalConfiguration.Configure(WebApiConfig.Register); - } - } -} \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.WebApi/Models/ProductInfo.cs b/ExtraneousFetching/ExtraneousFetching.WebApi/Models/ProductInfo.cs deleted file mode 100644 index 9f7d1b5..0000000 --- a/ExtraneousFetching/ExtraneousFetching.WebApi/Models/ProductInfo.cs +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -namespace ExtraneousFetching.WebApi.Models -{ - public class ProductInfo - { - public int Id { get; set; } - - public string Name { get; set; } - } -} \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.WebApi/Properties/AssemblyInfo.cs b/ExtraneousFetching/ExtraneousFetching.WebApi/Properties/AssemblyInfo.cs deleted file mode 100644 index 482d929..0000000 --- a/ExtraneousFetching/ExtraneousFetching.WebApi/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,37 +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.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("ExtraneousFetching.WebApi")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("ExtraneousFetching.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("eacd5942-4d2c-416c-bde7-efbc99fe2e85")] - -// 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")] \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.WebApi/Web.Debug.config b/ExtraneousFetching/ExtraneousFetching.WebApi/Web.Debug.config deleted file mode 100644 index 83c5c76..0000000 --- a/ExtraneousFetching/ExtraneousFetching.WebApi/Web.Debug.config +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.WebApi/Web.Release.config b/ExtraneousFetching/ExtraneousFetching.WebApi/Web.Release.config deleted file mode 100644 index 9b49033..0000000 --- a/ExtraneousFetching/ExtraneousFetching.WebApi/Web.Release.config +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.WebApi/Web.config b/ExtraneousFetching/ExtraneousFetching.WebApi/Web.config deleted file mode 100644 index c2a8b51..0000000 --- a/ExtraneousFetching/ExtraneousFetching.WebApi/Web.config +++ /dev/null @@ -1,41 +0,0 @@ - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.WebApi/packages.config b/ExtraneousFetching/ExtraneousFetching.WebApi/packages.config deleted file mode 100644 index 6033a55..0000000 --- a/ExtraneousFetching/ExtraneousFetching.WebApi/packages.config +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/ExtraneousFetching/ExtraneousFetching.sln b/ExtraneousFetching/ExtraneousFetching.sln deleted file mode 100644 index baeb827..0000000 --- a/ExtraneousFetching/ExtraneousFetching.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}") = "ExtraneousFetching.DataAccess", "ExtraneousFetching.DataAccess\ExtraneousFetching.DataAccess.csproj", "{35AA1C25-E5CE-456B-A88C-6E8A69BECB2E}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExtraneousFetching.WebApi", "ExtraneousFetching.WebApi\ExtraneousFetching.WebApi.csproj", "{6EF1565F-1384-4C18-8556-8913388D9281}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8186102C-9E9B-4D12-A2B4-C01ACA98CEF1}" - ProjectSection(SolutionItems) = preProject - docs\ExtraneousFetching.md = docs\ExtraneousFetching.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 - {35AA1C25-E5CE-456B-A88C-6E8A69BECB2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {35AA1C25-E5CE-456B-A88C-6E8A69BECB2E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {35AA1C25-E5CE-456B-A88C-6E8A69BECB2E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {35AA1C25-E5CE-456B-A88C-6E8A69BECB2E}.Release|Any CPU.Build.0 = Release|Any CPU - {6EF1565F-1384-4C18-8556-8913388D9281}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6EF1565F-1384-4C18-8556-8913388D9281}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6EF1565F-1384-4C18-8556-8913388D9281}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6EF1565F-1384-4C18-8556-8913388D9281}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching.sln b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching.sln new file mode 100644 index 0000000..92f9301 --- /dev/null +++ b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching.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}") = "ExtraneousFetching", "ExtraneousFetching\ExtraneousFetching.csproj", "{DBB56ECB-2890-4527-95A6-9F9AAC1AD2D1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DBB56ECB-2890-4527-95A6-9F9AAC1AD2D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DBB56ECB-2890-4527-95A6-9F9AAC1AD2D1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DBB56ECB-2890-4527-95A6-9F9AAC1AD2D1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DBB56ECB-2890-4527-95A6-9F9AAC1AD2D1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A38EBBF4-AD60-40AA-A23A-F2D146303F7A} + EndGlobalSection +EndGlobal diff --git a/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Controllers/UnnecessaryFieldsController.cs b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Controllers/UnnecessaryFieldsController.cs new file mode 100644 index 0000000..8fae0ae --- /dev/null +++ b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Controllers/UnnecessaryFieldsController.cs @@ -0,0 +1,36 @@ +using ExtraneousFetching.Model; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace ExtraneousFetching.Controllers +{ + [ApiController] + [Route("[controller]")] + public class UnnecessaryFieldsController(AdventureWorksProductContext context) : ControllerBase + { + [HttpGet] + [Route("api/allfields")] + public async Task GetAllFieldsAsync() + { + // execute the query + var products = await context.Products.ToListAsync(); + + // project fields from the query results + var result = products.Select(p => new DTOs.ProductInfo { Id = p.ProductID, Name = p.Name }); + + return Ok(result); + } + + [HttpGet] + [Route("api/requiredfields")] + public async Task GetRequiredFieldsAsync() + { + // project fields as part of the query itself + var result = await context.Products + .Select(p => new DTOs.ProductInfo { Id = p.ProductID, Name = p.Name }) + .ToListAsync(); + + return Ok(result); + } + } +} diff --git a/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Controllers/UnnecessaryRowsController.cs b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Controllers/UnnecessaryRowsController.cs new file mode 100644 index 0000000..518c11d --- /dev/null +++ b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Controllers/UnnecessaryRowsController.cs @@ -0,0 +1,34 @@ +using ExtraneousFetching.Model; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace ExtraneousFetching.Controllers +{ + [ApiController] + [Route("[controller]")] + public class UnnecessaryRowsController(AdventureWorksProductContext context) : Controller + { + [HttpGet] + [Route("api/aggregateonclient")] + public async Task AggregateOnClientAsync() + { + // fetch all order totals from the database + var orderAmounts = await context.SalesOrderHeaders.Select(soh => soh.TotalDue).ToListAsync(); + + // sum the order totals here in the controller + var total = orderAmounts.Sum(); + + return Ok(total); + } + + [HttpGet] + [Route("api/aggregateondatabase")] + public async Task AggregateOnDatabaseAsync() + { + // fetch the sum of all order totals, as computed on the database server + var total = await context.SalesOrderHeaders.SumAsync(soh => soh.TotalDue); + + return Ok(total); + } + } +} diff --git a/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/DTOs/ProductInfo.cs b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/DTOs/ProductInfo.cs new file mode 100644 index 0000000..f4e6583 --- /dev/null +++ b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/DTOs/ProductInfo.cs @@ -0,0 +1,9 @@ +namespace ExtraneousFetching.DTOs +{ + public class ProductInfo + { + public int? Id { get; set; } + + public string? Name { get; set; } + } +} diff --git a/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching.csproj b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching.csproj new file mode 100644 index 0000000..e88fad6 --- /dev/null +++ b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching.csproj @@ -0,0 +1,16 @@ + + + + net8.0 + enable + enable + + + + + + + + + + diff --git a/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Model/AdventureWorksProductContext.cs b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Model/AdventureWorksProductContext.cs new file mode 100644 index 0000000..60c531d --- /dev/null +++ b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Model/AdventureWorksProductContext.cs @@ -0,0 +1,54 @@ +using Microsoft.EntityFrameworkCore; + +namespace ExtraneousFetching.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"); + + modelBuilder.Entity() + .ToTable("SalesOrderHeader", "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/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Model/Product.cs b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Model/Product.cs new file mode 100644 index 0000000..86c8bdf --- /dev/null +++ b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Model/Product.cs @@ -0,0 +1,56 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; + +namespace ExtraneousFetching.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/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Model/ProductCategory.cs b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Model/ProductCategory.cs new file mode 100644 index 0000000..55f0f82 --- /dev/null +++ b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Model/ProductCategory.cs @@ -0,0 +1,24 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; + +namespace ExtraneousFetching.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/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Model/SalesOrderDetail.cs b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Model/SalesOrderDetail.cs new file mode 100644 index 0000000..5664c2f --- /dev/null +++ b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Model/SalesOrderDetail.cs @@ -0,0 +1,33 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; + +namespace ExtraneousFetching.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/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Model/SalesOrderHeader.cs b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Model/SalesOrderHeader.cs new file mode 100644 index 0000000..d13cb65 --- /dev/null +++ b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Model/SalesOrderHeader.cs @@ -0,0 +1,57 @@ +using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations; + +namespace ExtraneousFetching.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/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Program.cs b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Program.cs new file mode 100644 index 0000000..1b57cc1 --- /dev/null +++ b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/Program.cs @@ -0,0 +1,56 @@ +using ExtraneousFetching.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/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/appsettings.Development.json b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/appsettings.json b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/appsettings.json new file mode 100644 index 0000000..8176580 --- /dev/null +++ b/ExtraneousFetching/ExtraneousFetching/ExtraneousFetching/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "AdventureWorksProductDB": "Server=tcp:.database.windows.net,1433;Database=;Authentication=ActiveDirectoryDefault; Encrypt=True;TrustServerCertificate=false;Connection Timeout=30;" +} diff --git a/ExtraneousFetching/ReadMe.md b/ExtraneousFetching/ReadMe.md index 19932a9..10a2d65 100644 --- a/ExtraneousFetching/ReadMe.md +++ b/ExtraneousFetching/ReadMe.md @@ -1,136 +1,173 @@ # ExtraneousFetching Sample Code -The ExtraneousFetching sample code comprises the following items: - -* ExtraneousFetching solution file +It is a sample implementation to ilustrate [Extraneous Fetching antipattern](https://learn.microsoft.com/azure/architecture/antipatterns/extraneous-fetching/) -* ExtraneousFetching WebAPI project (*Note that this project is an Azure web application and not a cloud service*) +The ExtraneousFetching sample code comprises the following items: -* ExtraneousFetching.DataAccess class library +- ExtraneousFetching Application -* [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 WebRole WebAPI project contains two controllers: +The Api project contains two controllers: -* `UnnecessaryFieldsController` +- `UnnecessaryFieldsController` -* `UnnecessaryRowsController` +- `UnnecessaryRowsController` -The `GetAllFieldsAsync` action of the `UnnecessaryFieldsController` retrieves a -collection of product IDs and product names from the AdventureWorks2012 database and -returns the result. The action fetches all the details of every product from the -database before returning only the data in the product ID and product name fields: +The `GetAllFieldsAsync` action of the `UnnecessaryFieldsController` retrieves a collection of product IDs and product names from the AdventureWorksLT database and returns the result. The action fetches all the details of every product from the database before returning only the data in the product ID and product name fields: **C#** -``` C# -public async Task GetAllFieldsAsync() +```C# +[HttpGet] +[Route("api/allfields")] +public async Task GetAllFieldsAsync() { - using (var context = new AdventureWorksContext()) - { - // execute the query - var products = await context.Products.ToListAsync(); + // execute the query + var products = await context.Products.ToListAsync(); - // project fields from the query results - var result = products.Select(p => new ProductInfo { Id = p.ProductId, Name = p.Name }); + // project fields from the query results + var result = products.Select(p => new DTOs.ProductInfo { Id = p.ProductID, Name = p.Name }); - return Ok(result); - } + return Ok(result); } ``` -The `GetRequiredFieldsAsync` action of the `UnnecessaryFieldsController` performs the -same task but only fetches the product ID and product name from the database: +The `GetRequiredFieldsAsync` action of the `UnnecessaryFieldsController` performs the same task but only fetches the product ID and product name from the database: **C#** -``` C# -public async Task GetRequiredFieldsAsync() -{ - using (var context = new AdventureWorksContext()) - { - // project fields as part of the query itself - var result = await context.Products - .Select(p => new ProductInfo {Id = p.ProductId, Name = p.Name}) - .ToListAsync(); - - return Ok(result); - } -} +```C# + [HttpGet] + [Route("api/requiredfields")] + public async Task GetRequiredFieldsAsync() + { + // project fields as part of the query itself + var result = await context.Products + .Select(p => new DTOs.ProductInfo { Id = p.ProductID, Name = p.Name }) + .ToListAsync(); + + return Ok(result); + } ``` -The `AggregateOnClientAsync` action of the `UnnecessaryRowsController` calculates the -total value of sales made by a salesperson recorded in the database. To do this, it -retrieves the details of every sale from the `SalesOrderHeader` table and then -iterates through the results to perform the calculation: +The `AggregateOnClientAsync` action of the `UnnecessaryRowsController` calculates the total value of sales made by a salesperson recorded in the database. To do this, it retrieves the details of every sale from the `SalesOrderHeader` table and then iterates through the results to perform the calculation: **C#** -``` C# -public async Task AggregateOnClientAsync() -{ - using (var context = new AdventureWorksContext()) - { - // fetch all order totals from the database - var orderAmounts = await context.SalesOrderHeaders.Select(soh => soh.TotalDue).ToListAsync(); +```C# + [HttpGet] + [Route("api/aggregateonclient")] + public async Task AggregateOnClientAsync() + { + // fetch all order totals from the database + var orderAmounts = await context.SalesOrderHeaders.Select(soh => soh.TotalDue).ToListAsync(); + // sum the order totals here in the controller + var total = orderAmounts.Sum(); + return Ok(total); + } +``` - // sum the order totals here in the controller - var total = orderAmounts.Sum(); +The `AggregateOnDatabaseAsync` action of the `UnnecessaryRowsController` also calculates the total value of sales but uses the database to perform the aggregation by using the `Sum` function: - return Ok(total); - } -} +**C#** + +```C# + [HttpGet] + [Route("api/aggregateondatabase")] + public async Task AggregateOnDatabaseAsync() + { + // fetch the sum of all order totals, as computed on the database server + var total = await context.SalesOrderHeaders.SumAsync(soh => soh.TotalDue); + return Ok(total); + } ``` -The `AggregateOnDatabaseAsync` action of the `UnnecessaryRowsController` also -calculates the total value of sales but uses the database to perform the aggregation -by using the `Sum` function: +## :rocket: Deployment guide -**C#** +Install the prerequisites and follow the steps to deploy and run the examples. -``` C# -public async Task AggregateOnDatabaseAsync() -{ - using (var context = new AdventureWorksContext()) - { - // fetch the sum of all order totals, as computed on the database server - var total = await context.SalesOrderHeaders.SumAsync(soh => soh.TotalDue); +### Prerequisites - return Ok(total); - } -} -``` +- 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. -## Configuring the project + ```bash + git clone https://github.com/mspnp/performance-optimization + cd ExtraneousFetching + ``` -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 `AdventureWorksContext` connection string in the -web.config file for the ExtraneousFetching 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. +1. Log into Azure and create an empty resource group. -## Deploying the project to Azure + ```bash + az login + az account set -s -In Visual Studio Solution Explorer, right-click the ExtraneousFetching.WebApi project -and then click *Publish*. Publish the project to an Azure website. + export USER= + export USER_OBJECTID= + export USER_TENANTID= -## Load testing the project + LOCATION=eastus + RESOURCEGROUP=rg-extraneous-fetching-${LOCATION} -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]. + az group create --name ${RESOURCEGROUP} --location ${LOCATION} -## Dependencies + ``` + +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: + + - Go to the Database Server. + - In the Network section, allow your IP address. + +1. Run proyect locally + + Execute the API and then you will be able to call both endpoints + +## :broom: Clean up resources + +Most of the Azure resources deployed in the prior steps will incur ongoing charges unless removed. + +```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/ExtraneousFetching.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). diff --git a/ExtraneousFetching/bicep/main.bicep b/ExtraneousFetching/bicep/main.bicep new file mode 100644 index 0000000..6033b50 --- /dev/null +++ b/ExtraneousFetching/bicep/main.bicep @@ -0,0 +1,190 @@ +@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 = 'extraneosFeatching-${uniqueName}' +var logAnalyticsWorkspaceName = 'extraneosFeatching-${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 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 + 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 + } + } + ] + } +}