Exercise Files
This commit is contained in:
17
Ch07/07_02_End/Common/App.config
Normal file
17
Ch07/07_02_End/Common/App.config
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<configSections>
|
||||
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
|
||||
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
|
||||
</configSections>
|
||||
<entityFramework>
|
||||
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
|
||||
<parameters>
|
||||
<parameter value="mssqllocaldb" />
|
||||
</parameters>
|
||||
</defaultConnectionFactory>
|
||||
<providers>
|
||||
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
|
||||
</providers>
|
||||
</entityFramework>
|
||||
</configuration>
|
||||
75
Ch07/07_02_End/Common/Common.csproj
Normal file
75
Ch07/07_02_End/Common/Common.csproj
Normal file
@ -0,0 +1,75 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{7709A0D8-F311-4A8E-9E5B-05ED694449AD}</ProjectGuid>
|
||||
<OutputType>Library</OutputType>
|
||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
||||
<RootNamespace>HPlusSports</RootNamespace>
|
||||
<AssemblyName>HPlusSports.Common</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="MediatR, Version=4.1.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MediatR.4.1.0\lib\net45\MediatR.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Models\Category.cs" />
|
||||
<Compile Include="Models\EntityNotFoundException.cs" />
|
||||
<Compile Include="Models\HPlusSportsDbContext.cs" />
|
||||
<Compile Include="Models\HPlusSportsDbContext.SeedData.cs" />
|
||||
<Compile Include="Models\Image.cs" />
|
||||
<Compile Include="Models\Product.cs" />
|
||||
<Compile Include="Models\ProductRating.cs" />
|
||||
<Compile Include="Models\Review.cs" />
|
||||
<Compile Include="Models\ShoppingCart.cs" />
|
||||
<Compile Include="Models\ShoppingCartItem.cs" />
|
||||
<Compile Include="Requests\UpdateProductRequest.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Services\ProductUpdateService.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="TestData.xml" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
19
Ch07/07_02_End/Common/Models/Category.cs
Normal file
19
Ch07/07_02_End/Common/Models/Category.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HPlusSports.Models
|
||||
{
|
||||
public class Category
|
||||
{
|
||||
public long Id { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Key { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
|
||||
public long? ImageId { get; set; }
|
||||
|
||||
public virtual Image Image { get; set; }
|
||||
}
|
||||
}
|
||||
25
Ch07/07_02_End/Common/Models/EntityNotFoundException.cs
Normal file
25
Ch07/07_02_End/Common/Models/EntityNotFoundException.cs
Normal file
@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace HPlusSports
|
||||
{
|
||||
public class EntityNotFoundException : ApplicationException
|
||||
{
|
||||
public object Id { get; }
|
||||
public Type Type { get; }
|
||||
|
||||
public EntityNotFoundException(Type type, object id)
|
||||
: base($"Entity ${id?.ToString()} of type ${type?.Name} not found.")
|
||||
{
|
||||
Type = type;
|
||||
Id = id;
|
||||
}
|
||||
}
|
||||
|
||||
public class EntityNotFoundException<T> : EntityNotFoundException
|
||||
{
|
||||
public EntityNotFoundException(object id) : base(typeof(T), id)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
138
Ch07/07_02_End/Common/Models/HPlusSportsDbContext.SeedData.cs
Normal file
138
Ch07/07_02_End/Common/Models/HPlusSportsDbContext.SeedData.cs
Normal file
@ -0,0 +1,138 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
using HPlusSports.Models;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace HPlusSports
|
||||
{
|
||||
public class HPlusSportsDbContextInitializer
|
||||
: DropCreateDatabaseIfModelChanges<HPlusSportsDbContext>
|
||||
{
|
||||
protected override void Seed(HPlusSportsDbContext context)
|
||||
{
|
||||
new SeedData().Populate(context);
|
||||
}
|
||||
}
|
||||
|
||||
internal class SeedData
|
||||
{
|
||||
static readonly Random Random = new Random(1);
|
||||
|
||||
public const string TestUserId = "demo@hplussports.com";
|
||||
|
||||
static readonly IList<string> UserIds =
|
||||
Enumerable.Range(0, 10)
|
||||
.Select(x => $"user{x}")
|
||||
.Concat(new[] { TestUserId })
|
||||
.ToArray();
|
||||
|
||||
public void Populate(HPlusSportsDbContext context)
|
||||
{
|
||||
var doc = ReadTestData();
|
||||
Populate(context, doc);
|
||||
}
|
||||
|
||||
internal void Populate(HPlusSportsDbContext context, XDocument doc)
|
||||
{
|
||||
var categories = doc.Descendants("Categories").Descendants("Category")
|
||||
.Select(x => new Category
|
||||
{
|
||||
Key = ToKey(x.Element("Name").Value),
|
||||
Name = x.Element("Name").Value,
|
||||
Image = new Image { Url = x.Element("ImageUrl").Value },
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
context.Images.AddRange(categories.Select(x => x.Image));
|
||||
context.SaveChanges();
|
||||
|
||||
context.Categories.AddRange(categories);
|
||||
context.SaveChanges();
|
||||
|
||||
var products = doc.Descendants("Product")
|
||||
.Select(x =>
|
||||
{
|
||||
var product = new Product
|
||||
{
|
||||
CategoryId = categories.First(cat => cat.Name == x.Element("Category").Value).Id,
|
||||
Name = x.Element("Name").Value,
|
||||
Description = StripHtml(x.Element("Description").Value),
|
||||
MSRP = double.Parse(x.Element("MSRP").Value),
|
||||
Price = double.Parse(x.Element("Price").Value),
|
||||
SKU = x.Element("SKU").Value,
|
||||
Summary = x.Element("Summary").Value,
|
||||
LastUpdated = DateTime.UtcNow,
|
||||
LastUpdatedUserId = "admin@hplussports.com",
|
||||
ThumbnailImage = new Image { Url = x.Element("ThumbnailImageUrl").Value },
|
||||
};
|
||||
|
||||
product.Images.Add(new Image { Url = x.Element("ImageUrl").Value });
|
||||
|
||||
return product;
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
context.Products.AddRange(products);
|
||||
context.SaveChanges();
|
||||
|
||||
var reviews = context.Products.SelectMany(GenerateReviews);
|
||||
context.Reviews.AddRange(reviews);
|
||||
context.SaveChanges();
|
||||
}
|
||||
|
||||
IEnumerable<Review> GenerateReviews(Product product)
|
||||
{
|
||||
return Enumerable.Range(0, Random.Next(10))
|
||||
.Select(i =>
|
||||
new Review
|
||||
{
|
||||
SKU = product.SKU,
|
||||
UserId = UserIds[Random.Next(0, UserIds.Count)],
|
||||
Rating = Random.Next(3, 5),
|
||||
});
|
||||
}
|
||||
|
||||
private static string ToKey(string val)
|
||||
{
|
||||
return val
|
||||
.Replace(" ", "-")
|
||||
.Replace("--", "-")
|
||||
.Replace("--", "-")
|
||||
.ToLowerInvariant();
|
||||
}
|
||||
|
||||
private static XDocument ReadTestData()
|
||||
{
|
||||
var set = new XmlReaderSettings
|
||||
{
|
||||
ConformanceLevel = ConformanceLevel.Fragment
|
||||
};
|
||||
|
||||
using (var stream = typeof(SeedData).Assembly.GetManifestResourceStream("HPlusSports.TestData.xml"))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
return XDocument.Parse(reader.ReadToEnd());
|
||||
}
|
||||
}
|
||||
|
||||
private static string StripHtml(string source)
|
||||
{
|
||||
return
|
||||
Regex.Replace(
|
||||
Regex.Replace(
|
||||
source,
|
||||
"<[^>]*>",
|
||||
""
|
||||
),
|
||||
"&#[^;]*;",
|
||||
""
|
||||
)
|
||||
.Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
56
Ch07/07_02_End/Common/Models/HPlusSportsDbContext.cs
Normal file
56
Ch07/07_02_End/Common/Models/HPlusSportsDbContext.cs
Normal file
@ -0,0 +1,56 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Data.Entity;
|
||||
using System.Linq;
|
||||
using HPlusSports.Models;
|
||||
|
||||
namespace HPlusSports
|
||||
{
|
||||
public class HPlusSportsDbContext : DbContext
|
||||
{
|
||||
public DbSet<Category> Categories { get; set; }
|
||||
public DbSet<Image> Images { get; set; }
|
||||
public DbSet<Product> Products { get; set; }
|
||||
public DbSet<Review> Reviews { get; set; }
|
||||
public DbSet<ShoppingCart> ShoppingCarts { get; set; }
|
||||
public DbSet<ShoppingCartItem> ShoppingCartItems { get; set; }
|
||||
|
||||
public HPlusSportsDbContext()
|
||||
: this("HPlusSports")
|
||||
{
|
||||
}
|
||||
|
||||
public HPlusSportsDbContext(string connectionName)
|
||||
: base(connectionName)
|
||||
{
|
||||
}
|
||||
|
||||
public Product FindProductBySku(string sku)
|
||||
=> Products.FirstOrDefault(x => x.SKU == sku);
|
||||
|
||||
public ProductRating GetProductRating(string sku)
|
||||
{
|
||||
var reviews = Reviews.Where(x => x.SKU == sku);
|
||||
|
||||
return new ProductRating
|
||||
{
|
||||
SKU = sku,
|
||||
Rating = reviews.Average(x => (double?)x.Rating),
|
||||
ReviewCount = reviews.Count(),
|
||||
};
|
||||
}
|
||||
|
||||
public IQueryable<ProductRating> GetProductRatings(IEnumerable<string> skus)
|
||||
{
|
||||
return
|
||||
Reviews
|
||||
.Where(x => skus.Distinct().Contains(x.SKU))
|
||||
.GroupBy(x => x.SKU)
|
||||
.Select(reviews => new ProductRating
|
||||
{
|
||||
SKU = reviews.Key,
|
||||
Rating = reviews.Average(x => x.Rating),
|
||||
ReviewCount = reviews.Count(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
13
Ch07/07_02_End/Common/Models/Image.cs
Normal file
13
Ch07/07_02_End/Common/Models/Image.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace HPlusSports.Models
|
||||
{
|
||||
public class Image
|
||||
{
|
||||
public long Id { get; set; }
|
||||
|
||||
public string Url { get; set; }
|
||||
|
||||
public byte[] Content { get; set; }
|
||||
|
||||
public string ContentType { get; set; }
|
||||
}
|
||||
}
|
||||
58
Ch07/07_02_End/Common/Models/Product.cs
Normal file
58
Ch07/07_02_End/Common/Models/Product.cs
Normal file
@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
namespace HPlusSports.Models
|
||||
{
|
||||
public class Product
|
||||
{
|
||||
public long Id { get; set; }
|
||||
|
||||
[Required]
|
||||
public long CategoryId { get; set; }
|
||||
|
||||
[Required]
|
||||
public string SKU { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataType(DataType.Text)]
|
||||
public string Summary { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataType(DataType.MultilineText)]
|
||||
public string Description { get; set; }
|
||||
|
||||
[Range(minimum: 0, maximum: double.MaxValue)]
|
||||
[DataType(DataType.Currency)]
|
||||
public double MSRP { get; set; }
|
||||
|
||||
[Range(minimum: 0, maximum: double.MaxValue)]
|
||||
[DataType(DataType.Currency)]
|
||||
public double Price { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Last Updated By")]
|
||||
public DateTime LastUpdated { get; set; }
|
||||
|
||||
[Required]
|
||||
[Display(Name = "Last Update Timestamp")]
|
||||
public string LastUpdatedUserId { get; set; }
|
||||
|
||||
[NotMapped]
|
||||
public virtual Category Category { get; set; }
|
||||
|
||||
public long? ThumbnailImageId { get; set; }
|
||||
public virtual Image ThumbnailImage { get; set; }
|
||||
|
||||
public virtual ICollection<Image> Images { get; private set; }
|
||||
|
||||
public Product()
|
||||
{
|
||||
Images = new List<Image>();
|
||||
}
|
||||
}
|
||||
}
|
||||
9
Ch07/07_02_End/Common/Models/ProductRating.cs
Normal file
9
Ch07/07_02_End/Common/Models/ProductRating.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace HPlusSports.Models
|
||||
{
|
||||
public class ProductRating
|
||||
{
|
||||
public string SKU { get; set; }
|
||||
public double? Rating { get; set; }
|
||||
public int ReviewCount { get; set; }
|
||||
}
|
||||
}
|
||||
18
Ch07/07_02_End/Common/Models/Review.cs
Normal file
18
Ch07/07_02_End/Common/Models/Review.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HPlusSports.Models
|
||||
{
|
||||
public class Review
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string UserId { get; set; }
|
||||
|
||||
public string SKU { get; set; }
|
||||
|
||||
[Range(minimum: 1, maximum: 5)]
|
||||
public int Rating { get; set; }
|
||||
|
||||
[DataType(DataType.MultilineText)]
|
||||
public string Comments { get; set; }
|
||||
}
|
||||
}
|
||||
46
Ch07/07_02_End/Common/Models/ShoppingCart.cs
Normal file
46
Ch07/07_02_End/Common/Models/ShoppingCart.cs
Normal file
@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
|
||||
namespace HPlusSports.Models
|
||||
{
|
||||
public class ShoppingCart
|
||||
{
|
||||
public long Id { get; set; }
|
||||
public string UserId { get; set; }
|
||||
|
||||
[DataType(DataType.Currency)]
|
||||
public double Tax { get; set; }
|
||||
|
||||
[DataType(DataType.Currency)]
|
||||
public double Shipping { get; set; }
|
||||
|
||||
[DataType(DataType.Currency)]
|
||||
public double Total
|
||||
{
|
||||
get { return Subtotal + Tax + Shipping; }
|
||||
}
|
||||
|
||||
public string Coupon { get; set; }
|
||||
|
||||
public virtual ICollection<ShoppingCartItem> Items { get; private set; }
|
||||
|
||||
[DataType(DataType.Currency)]
|
||||
public double Subtotal
|
||||
{
|
||||
get { return Items.Sum(x => x.Total); }
|
||||
}
|
||||
|
||||
public ShoppingCart()
|
||||
{
|
||||
Items = new List<ShoppingCartItem>();
|
||||
}
|
||||
|
||||
public void Recalculate()
|
||||
{
|
||||
Shipping = Subtotal * 0.1;
|
||||
Tax = Subtotal * 0.07;
|
||||
}
|
||||
}
|
||||
}
|
||||
37
Ch07/07_02_End/Common/Models/ShoppingCartItem.cs
Normal file
37
Ch07/07_02_End/Common/Models/ShoppingCartItem.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HPlusSports.Models
|
||||
{
|
||||
public class ShoppingCartItem
|
||||
{
|
||||
public long Id { get; set; }
|
||||
|
||||
[Required]
|
||||
public string SKU { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataType(DataType.Currency)]
|
||||
[Range(minimum: 0, maximum: double.MaxValue)]
|
||||
public double MSRP { get; set; }
|
||||
|
||||
[DataType(DataType.Currency)]
|
||||
[Range(minimum: 0, maximum: double.MaxValue)]
|
||||
public double Price { get; set; }
|
||||
|
||||
[Range(minimum: 0, maximum: int.MaxValue)]
|
||||
public int Quantity { get; set; }
|
||||
|
||||
[DataType(DataType.Currency)]
|
||||
public double Total
|
||||
{
|
||||
get { return Price * Quantity; }
|
||||
}
|
||||
|
||||
public ShoppingCartItem()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
37
Ch07/07_02_End/Common/Models/UpdateProductRequest.cs
Normal file
37
Ch07/07_02_End/Common/Models/UpdateProductRequest.cs
Normal file
@ -0,0 +1,37 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HPlusSports.Models
|
||||
{
|
||||
public class UpdateProductRequest
|
||||
{
|
||||
[Required]
|
||||
public long Id { get; set; }
|
||||
|
||||
[Required]
|
||||
public long CategoryId { get; set; }
|
||||
|
||||
[Required]
|
||||
public string SKU { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataType(DataType.Text)]
|
||||
public string Summary { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataType(DataType.MultilineText)]
|
||||
public string Description { get; set; }
|
||||
|
||||
[Range(minimum: 0, maximum: double.MaxValue)]
|
||||
[DataType(DataType.Currency)]
|
||||
public double MSRP { get; set; }
|
||||
|
||||
[Range(minimum: 0, maximum: double.MaxValue)]
|
||||
[DataType(DataType.Currency)]
|
||||
public double Price { get; set; }
|
||||
|
||||
public string LastUpdatedUserId { get; set; }
|
||||
}
|
||||
}
|
||||
38
Ch07/07_02_End/Common/Properties/AssemblyInfo.cs
Normal file
38
Ch07/07_02_End/Common/Properties/AssemblyInfo.cs
Normal file
@ -0,0 +1,38 @@
|
||||
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("Common")]
|
||||
[assembly: AssemblyDescription("")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("")]
|
||||
[assembly: AssemblyProduct("Common")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
||||
[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("7709a0d8-f311-4a8e-9e5b-05ed694449ad")]
|
||||
|
||||
// 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")]
|
||||
|
||||
[assembly: InternalsVisibleTo("HPlusSports.Tests")]
|
||||
43
Ch07/07_02_End/Common/Requests/UpdateProductRequest.cs
Normal file
43
Ch07/07_02_End/Common/Requests/UpdateProductRequest.cs
Normal file
@ -0,0 +1,43 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace HPlusSports.Requests
|
||||
{
|
||||
public class UpdateProductResponse
|
||||
{
|
||||
public bool Success { get; set; }
|
||||
public string Message { get; set; }
|
||||
}
|
||||
|
||||
public class UpdateProductRequest : MediatR.IRequest<UpdateProductResponse>
|
||||
{
|
||||
[Required]
|
||||
public long Id { get; set; }
|
||||
|
||||
[Required]
|
||||
public long CategoryId { get; set; }
|
||||
|
||||
[Required]
|
||||
public string SKU { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Name { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataType(DataType.Text)]
|
||||
public string Summary { get; set; }
|
||||
|
||||
[Required]
|
||||
[DataType(DataType.MultilineText)]
|
||||
public string Description { get; set; }
|
||||
|
||||
[Range(minimum: 0, maximum: double.MaxValue)]
|
||||
[DataType(DataType.Currency)]
|
||||
public double MSRP { get; set; }
|
||||
|
||||
[Range(minimum: 0, maximum: double.MaxValue)]
|
||||
[DataType(DataType.Currency)]
|
||||
public double Price { get; set; }
|
||||
|
||||
public string LastUpdatedUserId { get; set; }
|
||||
}
|
||||
}
|
||||
80
Ch07/07_02_End/Common/Services/ProductUpdateService.cs
Normal file
80
Ch07/07_02_End/Common/Services/ProductUpdateService.cs
Normal file
@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using HPlusSports.Requests;
|
||||
|
||||
namespace HPlusSports.Services
|
||||
{
|
||||
public class ProductUpdateService
|
||||
: MediatR.IRequestHandler<UpdateProductRequest, UpdateProductResponse>
|
||||
{
|
||||
private readonly HPlusSportsDbContext _context;
|
||||
|
||||
public ProductUpdateService(HPlusSportsDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public Task<UpdateProductResponse> Handle(UpdateProductRequest request, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(Update(request));
|
||||
}
|
||||
|
||||
public UpdateProductResponse Update(UpdateProductRequest request)
|
||||
{
|
||||
var existing = _context.Products.Find(request.Id);
|
||||
|
||||
if (existing == null)
|
||||
{
|
||||
return new UpdateProductResponse
|
||||
{
|
||||
Success = false,
|
||||
Message = $"Couldn't update product #\"{request.Id}\": product not found!",
|
||||
};
|
||||
}
|
||||
|
||||
var hasPriceChanged = existing.Price != request.Price;
|
||||
|
||||
existing.CategoryId = request.CategoryId;
|
||||
existing.Description = request.Description;
|
||||
existing.MSRP = request.MSRP;
|
||||
existing.Name = request.Name;
|
||||
existing.Price = request.Price;
|
||||
existing.SKU = request.SKU;
|
||||
existing.Summary = request.Summary;
|
||||
existing.LastUpdated = DateTime.UtcNow;
|
||||
existing.LastUpdatedUserId = request.LastUpdatedUserId;
|
||||
|
||||
_context.SaveChanges();
|
||||
|
||||
if (hasPriceChanged)
|
||||
{
|
||||
var cartsToUpdate =
|
||||
_context.ShoppingCarts
|
||||
.Include("Items")
|
||||
.Where(cart => cart.Items.Any(x => x.SKU == request.SKU));
|
||||
|
||||
foreach (var cart in cartsToUpdate)
|
||||
{
|
||||
foreach (var cartItem in cart.Items.Where(x => x.SKU == request.SKU))
|
||||
{
|
||||
cartItem.Price = request.Price;
|
||||
}
|
||||
|
||||
cart.Recalculate();
|
||||
}
|
||||
}
|
||||
|
||||
_context.SaveChanges();
|
||||
|
||||
return new UpdateProductResponse
|
||||
{
|
||||
Success = true,
|
||||
Message = $"Successfully updated \"{request.Name}\"",
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
Ch07/07_02_End/Common/TestData.xml
Normal file
BIN
Ch07/07_02_End/Common/TestData.xml
Normal file
Binary file not shown.
5
Ch07/07_02_End/Common/packages.config
Normal file
5
Ch07/07_02_End/Common/packages.config
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="EntityFramework" version="6.2.0" targetFramework="net461" />
|
||||
<package id="MediatR" version="4.1.0" targetFramework="net461" />
|
||||
</packages>
|
||||
Reference in New Issue
Block a user