Browse Source

Merge branch 'master' into pr/119

main
Eric Fleming 7 years ago
parent
commit
6b12999df1
  1. 6
      README.md
  2. 4
      docker-compose.yml
  3. BIN
      docs/Architecting Modern Web Applications with ASP.NET Core and Azure (v2.0).epub
  4. BIN
      docs/Architecting Modern Web Applications with ASP.NET Core and Azure (v2.0).mobi
  5. BIN
      docs/Architecting Modern Web Applications with ASP.NET Core and Azure.epub
  6. BIN
      docs/Architecting Modern Web Applications with ASP.NET Core and Azure.mobi
  7. 21
      eShopOnWeb.sln
  8. 16
      src/ApplicationCore/ApplicationCore.csproj
  9. 1
      src/ApplicationCore/Entities/BuyerAggregate/Buyer.cs
  10. 4
      src/ApplicationCore/Entities/BuyerAggregate/PaymentMethod.cs
  11. 4
      src/ApplicationCore/Entities/CatalogBrand.cs
  12. 1
      src/ApplicationCore/Entities/OrderAggregate/Order.cs
  13. 4
      src/ApplicationCore/Entities/OrderAggregate/OrderItem.cs
  14. 5
      src/ApplicationCore/Interfaces/IAsyncRepository.cs
  15. 3
      src/ApplicationCore/Interfaces/IOrderRepository.cs
  16. 1
      src/ApplicationCore/Interfaces/IRepository.cs
  17. 6
      src/ApplicationCore/Interfaces/ISpecification.cs
  18. 12
      src/ApplicationCore/Services/BasketService.cs
  19. 1
      src/ApplicationCore/Services/UriComposer.cs
  20. 20
      src/ApplicationCore/Specifications/BaseSpecification.cs
  21. 14
      src/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs
  22. 3
      src/Infrastructure/Data/CatalogContextSeed.cs
  23. 52
      src/Infrastructure/Data/EfRepository.cs
  24. 1
      src/Infrastructure/Data/Migrations/20171018175735_Initial.cs
  25. 3
      src/Infrastructure/Data/Migrations/CatalogContextModelSnapshot.cs
  26. 11
      src/Infrastructure/Data/OrderRepository.cs
  27. 50
      src/Infrastructure/Data/SpecificationEvaluator.cs
  28. 1
      src/Infrastructure/Identity/Migrations/20170822214310_InitialIdentityModel.cs
  29. 2
      src/Infrastructure/Identity/Migrations/AppIdentityDbContextModelSnapshot.cs
  30. 32
      src/Infrastructure/Infrastructure.csproj
  31. 3
      src/Web/.bowerrc
  32. 3
      src/Web/Areas/Identity/Pages/_ViewStart.cshtml
  33. 2
      src/Web/Constants.cs
  34. 16
      src/Web/Controllers/AccountController.cs
  35. 5
      src/Web/Controllers/Api/BaseApiController.cs
  36. 111
      src/Web/Controllers/BasketController.cs
  37. 29
      src/Web/Controllers/CatalogController.cs
  38. 13
      src/Web/Controllers/ManageController.cs
  39. 23
      src/Web/Controllers/OrderController.cs
  40. 16
      src/Web/Data/ApplicationDbContext.cs
  41. 236
      src/Web/Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs
  42. 220
      src/Web/Data/Migrations/00000000000000_CreateIdentitySchema.cs
  43. 234
      src/Web/Data/Migrations/ApplicationDbContextModelSnapshot.cs
  44. 4
      src/Web/Dockerfile
  45. 40
      src/Web/HealthChecks/ApiHealthCheck.cs
  46. 36
      src/Web/HealthChecks/HomePageHealthCheck.cs
  47. 2
      src/Web/Interfaces/IBasketViewModelService.cs
  48. 11
      src/Web/Models/ErrorViewModel.cs
  49. 2
      src/Web/Pages/Basket/BasketItemViewModel.cs
  50. 2
      src/Web/Pages/Basket/BasketViewModel.cs
  51. 16
      src/Web/Pages/Basket/Checkout.cshtml
  52. 83
      src/Web/Pages/Basket/Checkout.cshtml.cs
  53. 58
      src/Web/Pages/Basket/Index.cshtml
  54. 92
      src/Web/Pages/Basket/Index.cshtml.cs
  55. 26
      src/Web/Pages/Error.cshtml
  56. 19
      src/Web/Pages/Error.cshtml.cs
  57. 27
      src/Web/Pages/Index.cshtml
  58. 26
      src/Web/Pages/Index.cshtml.cs
  59. 8
      src/Web/Pages/Privacy.cshtml
  60. 11
      src/Web/Pages/Privacy.cshtml.cs
  61. 9
      src/Web/Pages/Shared/Components/BasketComponent/Basket.cs
  62. 13
      src/Web/Pages/Shared/Components/BasketComponent/Default.cshtml
  63. 8
      src/Web/Pages/Shared/_pagination.cshtml
  64. 10
      src/Web/Pages/Shared/_product.cshtml
  65. 9
      src/Web/Pages/_ViewImports.cshtml
  66. 3
      src/Web/Pages/_ViewStart.cshtml
  67. 13
      src/Web/Program.cs
  68. 16
      src/Web/Properties/launchSettings.json
  69. 10
      src/Web/Services/BasketViewModelService.cs
  70. 26
      src/Web/Services/CatalogService.cs
  71. 17
      src/Web/SlugifyParameterTransformer.cs
  72. 154
      src/Web/Startup.cs
  73. 3
      src/Web/ViewModels/Account/RegisterViewModel.cs
  74. 4
      src/Web/ViewModels/Manage/TwoFactorAuthenticationViewModel.cs
  75. 6
      src/Web/ViewModels/OrderItemViewModel.cs
  76. 1
      src/Web/Views/Account/LoginWith2fa.cshtml
  77. 4
      src/Web/Views/Account/Register.cshtml
  78. 1
      src/Web/Views/Account/Signin.cshtml
  79. 16
      src/Web/Views/Basket/Checkout.cshtml
  80. 12
      src/Web/Views/Manage/MyAccount.cshtml
  81. 19
      src/Web/Views/Order/Detail.cshtml
  82. 3
      src/Web/Views/Order/MyOrders.cshtml
  83. 25
      src/Web/Views/Shared/_CookieConsentPartial.cshtml
  84. 117
      src/Web/Views/Shared/_Layout.cshtml
  85. 107
      src/Web/Views/Shared/_LoginPartial.cshtml
  86. 4
      src/Web/Views/_ViewImports.cshtml
  87. 107
      src/Web/Web.csproj
  88. 3
      src/Web/appsettings.Development.json
  89. 3
      src/Web/appsettings.json
  90. 10
      src/Web/bower.json
  91. 24
      src/Web/bundleconfig.json
  92. 42
      src/Web/compilerconfig.json
  93. 40
      src/Web/wwwroot/lib/jquery-validation/.bower.json
  94. 3
      src/WebRazorPages/.bowerrc
  95. 3
      src/WebRazorPages/Areas/Identity/Pages/_ViewStart.cshtml
  96. 16
      src/WebRazorPages/Data/ApplicationDbContext.cs
  97. 236
      src/WebRazorPages/Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs
  98. 220
      src/WebRazorPages/Data/Migrations/00000000000000_CreateIdentitySchema.cs
  99. 234
      src/WebRazorPages/Data/Migrations/ApplicationDbContextModelSnapshot.cs
  100. 4
      src/WebRazorPages/Dockerfile

6
README.md

@ -56,9 +56,9 @@ You can also run the samples in Docker (see below).
}
```
1. Ensure your connection strings in `appsettings.json` point to a local SQL Server instance.
2. Ensure your connection strings in `appsettings.json` point to a local SQL Server instance.
2. Open a command prompt in the Web folder and execute the following commands:
3. Open a command prompt in the Web folder and execute the following commands:
```
dotnet restore
@ -68,7 +68,7 @@ dotnet ef database update -c appidentitydbcontext -p ../Infrastructure/Infrastru
These commands will create two separate databases, one for the store's catalog data and shopping cart information, and one for the app's user credentials and identity data.
3. Run the application.
4. Run the application.
The first time you run the application, it will seed both databases with data such that you should see products in the store, and you should be able to log in using the demouser@microsoft.com account.
Note: If you need to create migrations, you can use these commands:

4
docker-compose.yml

@ -9,7 +9,7 @@ services:
environment:
- ASPNETCORE_ENVIRONMENT=Development
ports:
- "5107:5107"
- "5107:80"
eshopwebmvc:
image: eshopwebmvc
@ -19,7 +19,7 @@ services:
environment:
- ASPNETCORE_ENVIRONMENT=Development
ports:
- "5106:5106"
- "5106:80"
networks:
default:

BIN
docs/Architecting Modern Web Applications with ASP.NET Core and Azure (v2.0).epub

Binary file not shown.

BIN
docs/Architecting Modern Web Applications with ASP.NET Core and Azure (v2.0).mobi

Binary file not shown.

BIN
docs/Architecting Modern Web Applications with ASP.NET Core and Azure.epub

Binary file not shown.

BIN
docs/Architecting Modern Web Applications with ASP.NET Core and Azure.mobi

Binary file not shown.

21
eShopOnWeb.sln

@ -7,8 +7,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{419A6ACE-041
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Infrastructure", "src\Infrastructure\Infrastructure.csproj", "{7C461394-ABDC-43CD-A798-71249C58BA67}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web", "src\Web\Web.csproj", "{9CB6566E-E86A-4C07-BB8D-E0B95BCD4BD2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApplicationCore", "src\ApplicationCore\ApplicationCore.csproj", "{7FED7440-2311-4D1E-958B-3E887C585CD2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{15EA4737-125B-4E6E-A806-E13B7EBCDCCF}"
@ -19,13 +17,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "tests\I
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunctionalTests", "tests\FunctionalTests\FunctionalTests.csproj", "{7EFB5482-F942-4C3D-94B0-9B70596E6D0A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebRazorPages", "src\WebRazorPages\WebRazorPages.csproj", "{3CA62E98-218E-4A74-BF79-1098900BD421}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0BD72BEA-EF42-4B72-8B69-12A39EC76FBA}"
ProjectSection(SolutionItems) = preProject
docker-compose.yml = docker-compose.yml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Web", "src\Web\Web.csproj", "{227CF035-29B0-448D-97E4-944F9EA850E5}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -36,10 +34,6 @@ Global
{7C461394-ABDC-43CD-A798-71249C58BA67}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C461394-ABDC-43CD-A798-71249C58BA67}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C461394-ABDC-43CD-A798-71249C58BA67}.Release|Any CPU.Build.0 = Release|Any CPU
{9CB6566E-E86A-4C07-BB8D-E0B95BCD4BD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9CB6566E-E86A-4C07-BB8D-E0B95BCD4BD2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9CB6566E-E86A-4C07-BB8D-E0B95BCD4BD2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9CB6566E-E86A-4C07-BB8D-E0B95BCD4BD2}.Release|Any CPU.Build.0 = Release|Any CPU
{7FED7440-2311-4D1E-958B-3E887C585CD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7FED7440-2311-4D1E-958B-3E887C585CD2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7FED7440-2311-4D1E-958B-3E887C585CD2}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -56,22 +50,21 @@ Global
{7EFB5482-F942-4C3D-94B0-9B70596E6D0A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7EFB5482-F942-4C3D-94B0-9B70596E6D0A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7EFB5482-F942-4C3D-94B0-9B70596E6D0A}.Release|Any CPU.Build.0 = Release|Any CPU
{3CA62E98-218E-4A74-BF79-1098900BD421}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3CA62E98-218E-4A74-BF79-1098900BD421}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3CA62E98-218E-4A74-BF79-1098900BD421}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3CA62E98-218E-4A74-BF79-1098900BD421}.Release|Any CPU.Build.0 = Release|Any CPU
{227CF035-29B0-448D-97E4-944F9EA850E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{227CF035-29B0-448D-97E4-944F9EA850E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{227CF035-29B0-448D-97E4-944F9EA850E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{227CF035-29B0-448D-97E4-944F9EA850E5}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{7C461394-ABDC-43CD-A798-71249C58BA67} = {419A6ACE-0419-4315-A6FB-B0E63D39432E}
{9CB6566E-E86A-4C07-BB8D-E0B95BCD4BD2} = {419A6ACE-0419-4315-A6FB-B0E63D39432E}
{7FED7440-2311-4D1E-958B-3E887C585CD2} = {419A6ACE-0419-4315-A6FB-B0E63D39432E}
{EF6877E6-59CB-43A7-8C2C-E70DD70CC5B6} = {15EA4737-125B-4E6E-A806-E13B7EBCDCCF}
{0F576306-7E2D-49B7-87B1-EB5D94CFD5FC} = {15EA4737-125B-4E6E-A806-E13B7EBCDCCF}
{7EFB5482-F942-4C3D-94B0-9B70596E6D0A} = {15EA4737-125B-4E6E-A806-E13B7EBCDCCF}
{3CA62E98-218E-4A74-BF79-1098900BD421} = {419A6ACE-0419-4315-A6FB-B0E63D39432E}
{227CF035-29B0-448D-97E4-944F9EA850E5} = {419A6ACE-0419-4315-A6FB-B0E63D39432E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {49813262-5DA3-4D61-ABD3-493C74CE8C2B}

16
src/ApplicationCore/ApplicationCore.csproj

@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Microsoft.eShopWeb.ApplicationCore</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Microsoft.eShopWeb.ApplicationCore</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Ardalis.GuardClauses" Version="1.2.3" />
<PackageReference Include="System.Security.Claims" Version="4.3.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Ardalis.GuardClauses" Version="1.2.3" />
<PackageReference Include="System.Security.Claims" Version="4.3.0" />
</ItemGroup>
</Project>

1
src/ApplicationCore/Entities/BuyerAggregate/Buyer.cs

@ -1,6 +1,5 @@
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Ardalis.GuardClauses;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using System.Collections.Generic;
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate

4
src/ApplicationCore/Entities/BuyerAggregate/PaymentMethod.cs

@ -1,6 +1,4 @@
using Microsoft.eShopWeb.ApplicationCore.Entities;
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate
namespace Microsoft.eShopWeb.ApplicationCore.Entities.BuyerAggregate
{
public class PaymentMethod : BaseEntity
{

4
src/ApplicationCore/Entities/CatalogBrand.cs

@ -1,6 +1,4 @@
using System.Collections.Generic;
namespace Microsoft.eShopWeb.ApplicationCore.Entities
namespace Microsoft.eShopWeb.ApplicationCore.Entities
{
public class CatalogBrand : BaseEntity
{

1
src/ApplicationCore/Entities/OrderAggregate/Order.cs

@ -1,6 +1,5 @@
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Ardalis.GuardClauses;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using System;
using System.Collections.Generic;

4
src/ApplicationCore/Entities/OrderAggregate/OrderItem.cs

@ -1,6 +1,4 @@
using Microsoft.eShopWeb.ApplicationCore.Entities;
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate
namespace Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate
{
public class OrderItem : BaseEntity

5
src/ApplicationCore/Interfaces/IAsyncRepository.cs

@ -7,10 +7,11 @@ namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
public interface IAsyncRepository<T> where T : BaseEntity
{
Task<T> GetByIdAsync(int id);
Task<List<T>> ListAllAsync();
Task<List<T>> ListAsync(ISpecification<T> spec);
Task<IReadOnlyList<T>> ListAllAsync();
Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec);
Task<T> AddAsync(T entity);
Task UpdateAsync(T entity);
Task DeleteAsync(T entity);
Task<int> CountAsync(ISpecification<T> spec);
}
}

3
src/ApplicationCore/Interfaces/IOrderRepository.cs

@ -4,9 +4,8 @@ using System.Threading.Tasks;
namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
{
public interface IOrderRepository : IRepository<Order>, IAsyncRepository<Order>
public interface IOrderRepository : IAsyncRepository<Order>
{
Order GetByIdWithItems(int id);
Task<Order> GetByIdWithItemsAsync(int id);
}
}

1
src/ApplicationCore/Interfaces/IRepository.cs

@ -12,5 +12,6 @@ namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
T Add(T entity);
void Update(T entity);
void Delete(T entity);
int Count(ISpecification<T> spec);
}
}

6
src/ApplicationCore/Interfaces/ISpecification.cs

@ -9,5 +9,11 @@ namespace Microsoft.eShopWeb.ApplicationCore.Interfaces
Expression<Func<T, bool>> Criteria { get; }
List<Expression<Func<T, object>>> Includes { get; }
List<string> IncludeStrings { get; }
Expression<Func<T, object>> OrderBy { get; }
Expression<Func<T, object>> OrderByDescending { get; }
int Take { get; }
int Skip { get; }
bool isPagingEnabled { get;}
}
}

12
src/ApplicationCore/Services/BasketService.cs

@ -12,6 +12,7 @@ namespace Microsoft.eShopWeb.ApplicationCore.Services
public class BasketService : IBasketService
{
private readonly IAsyncRepository<Basket> _basketRepository;
private readonly IAsyncRepository<BasketItem> _basketItemRepository;
private readonly IUriComposer _uriComposer;
private readonly IAppLogger<BasketService> _logger;
private readonly IRepository<CatalogItem> _itemRepository;
@ -19,12 +20,14 @@ namespace Microsoft.eShopWeb.ApplicationCore.Services
public BasketService(IAsyncRepository<Basket> basketRepository,
IRepository<CatalogItem> itemRepository,
IUriComposer uriComposer,
IAppLogger<BasketService> logger)
IAppLogger<BasketService> logger,
IAsyncRepository<BasketItem> basketItemRepository)
{
_basketRepository = basketRepository;
_uriComposer = uriComposer;
this._logger = logger;
_logger = logger;
_itemRepository = itemRepository;
_basketItemRepository = basketItemRepository;
}
public async Task AddItemToBasket(int basketId, int catalogItemId, decimal price, int quantity)
@ -40,6 +43,11 @@ namespace Microsoft.eShopWeb.ApplicationCore.Services
{
var basket = await _basketRepository.GetByIdAsync(basketId);
foreach (var item in basket.Items.ToList())
{
await _basketItemRepository.DeleteAsync(item);
}
await _basketRepository.DeleteAsync(basket);
}

1
src/ApplicationCore/Services/UriComposer.cs

@ -1,5 +1,4 @@
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb;
namespace Microsoft.eShopWeb.ApplicationCore.Services
{

20
src/ApplicationCore/Specifications/BaseSpecification.cs

@ -14,6 +14,12 @@ namespace Microsoft.eShopWeb.ApplicationCore.Specifications
public Expression<Func<T, bool>> Criteria { get; }
public List<Expression<Func<T, object>>> Includes { get; } = new List<Expression<Func<T, object>>>();
public List<string> IncludeStrings { get; } = new List<string>();
public Expression<Func<T, object>> OrderBy { get; private set; }
public Expression<Func<T, object>> OrderByDescending { get; private set; }
public int Take { get; private set; }
public int Skip { get; private set; }
public bool isPagingEnabled { get; private set; } = false;
protected virtual void AddInclude(Expression<Func<T, object>> includeExpression)
{
@ -23,5 +29,19 @@ namespace Microsoft.eShopWeb.ApplicationCore.Specifications
{
IncludeStrings.Add(includeString);
}
protected virtual void ApplyPaging(int skip, int take)
{
Skip = skip;
Take = take;
isPagingEnabled = true;
}
protected virtual void ApplyOrderBy(Expression<Func<T, object>> orderByExpression)
{
OrderBy = orderByExpression;
}
protected virtual void ApplyOrderByDescending(Expression<Func<T, object>> orderByDescendingExpression)
{
OrderByDescending = orderByDescendingExpression;
}
}
}

14
src/ApplicationCore/Specifications/CatalogFilterPaginatedSpecification.cs

@ -0,0 +1,14 @@
using Microsoft.eShopWeb.ApplicationCore.Entities;
namespace Microsoft.eShopWeb.ApplicationCore.Specifications
{
public class CatalogFilterPaginatedSpecification : BaseSpecification<CatalogItem>
{
public CatalogFilterPaginatedSpecification(int skip, int take, int? brandId, int? typeId)
: base(i => (!brandId.HasValue || i.CatalogBrandId == brandId) &&
(!typeId.HasValue || i.CatalogTypeId == typeId))
{
ApplyPaging(skip, take);
}
}
}

3
src/Infrastructure/Data/CatalogContextSeed.cs

@ -1,5 +1,4 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;

52
src/Infrastructure/Data/EfRepository.cs

@ -31,7 +31,6 @@ namespace Microsoft.eShopWeb.Infrastructure.Data
return List(spec).FirstOrDefault();
}
public virtual async Task<T> GetByIdAsync(int id)
{
return await _dbContext.Set<T>().FindAsync(id);
@ -42,44 +41,28 @@ namespace Microsoft.eShopWeb.Infrastructure.Data
return _dbContext.Set<T>().AsEnumerable();
}
public async Task<List<T>> ListAllAsync()
public async Task<IReadOnlyList<T>> ListAllAsync()
{
return await _dbContext.Set<T>().ToListAsync();
}
public IEnumerable<T> List(ISpecification<T> spec)
{
// fetch a Queryable that includes all expression-based includes
var queryableResultWithIncludes = spec.Includes
.Aggregate(_dbContext.Set<T>().AsQueryable(),
(current, include) => current.Include(include));
// modify the IQueryable to include any string-based include statements
var secondaryResult = spec.IncludeStrings
.Aggregate(queryableResultWithIncludes,
(current, include) => current.Include(include));
// return the result of the query using the specification's criteria expression
return secondaryResult
.Where(spec.Criteria)
.AsEnumerable();
return ApplySpecification(spec).AsEnumerable();
}
public async Task<List<T>> ListAsync(ISpecification<T> spec)
public async Task<IReadOnlyList<T>> ListAsync(ISpecification<T> spec)
{
// fetch a Queryable that includes all expression-based includes
var queryableResultWithIncludes = spec.Includes
.Aggregate(_dbContext.Set<T>().AsQueryable(),
(current, include) => current.Include(include));
// modify the IQueryable to include any string-based include statements
var secondaryResult = spec.IncludeStrings
.Aggregate(queryableResultWithIncludes,
(current, include) => current.Include(include));
// return the result of the query using the specification's criteria expression
return await secondaryResult
.Where(spec.Criteria)
.ToListAsync();
return await ApplySpecification(spec).ToListAsync();
}
public int Count(ISpecification<T> spec)
{
return ApplySpecification(spec).Count();
}
public async Task<int> CountAsync(ISpecification<T> spec)
{
return await ApplySpecification(spec).CountAsync();
}
public T Add(T entity)
@ -103,6 +86,7 @@ namespace Microsoft.eShopWeb.Infrastructure.Data
_dbContext.Entry(entity).State = EntityState.Modified;
_dbContext.SaveChanges();
}
public async Task UpdateAsync(T entity)
{
_dbContext.Entry(entity).State = EntityState.Modified;
@ -114,10 +98,16 @@ namespace Microsoft.eShopWeb.Infrastructure.Data
_dbContext.Set<T>().Remove(entity);
_dbContext.SaveChanges();
}
public async Task DeleteAsync(T entity)
{
_dbContext.Set<T>().Remove(entity);
await _dbContext.SaveChangesAsync();
}
private IQueryable<T> ApplySpecification(ISpecification<T> spec)
{
return SpecificationEvaluator<T>.GetQuery(_dbContext.Set<T>().AsQueryable(), spec);
}
}
}

1
src/Infrastructure/Data/Migrations/20171018175735_Initial.cs

@ -1,7 +1,6 @@
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace Microsoft.eShopWeb.Infrastructure.Data.Migrations
{

3
src/Infrastructure/Data/Migrations/CatalogContextModelSnapshot.cs

@ -1,5 +1,4 @@
// <auto-generated />
using System;
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;

11
src/Infrastructure/Data/OrderRepository.cs

@ -1,7 +1,6 @@
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Infrastructure.Data
@ -12,20 +11,12 @@ namespace Microsoft.eShopWeb.Infrastructure.Data
{
}
public Order GetByIdWithItems(int id)
{
return _dbContext.Orders
.Include(o => o.OrderItems)
.Include($"{nameof(Order.OrderItems)}.{nameof(OrderItem.ItemOrdered)}")
.FirstOrDefault();
}
public Task<Order> GetByIdWithItemsAsync(int id)
{
return _dbContext.Orders
.Include(o => o.OrderItems)
.Include($"{nameof(Order.OrderItems)}.{nameof(OrderItem.ItemOrdered)}")
.FirstOrDefaultAsync();
.FirstOrDefaultAsync(x => x.Id == id);
}
}
}

50
src/Infrastructure/Data/SpecificationEvaluator.cs

@ -0,0 +1,50 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Microsoft.eShopWeb.Infrastructure.Data
{
public class SpecificationEvaluator<T> where T : BaseEntity
{
public static IQueryable<T> GetQuery(IQueryable<T> inputQuery, ISpecification<T> specification)
{
var query = inputQuery;
// modify the IQueryable using the specification's criteria expression
if (specification.Criteria != null)
{
query = query.Where(specification.Criteria);
}
// Includes all expression-based includes
query = specification.Includes.Aggregate(query,
(current, include) => current.Include(include));
// Include any string-based include statements
query = specification.IncludeStrings.Aggregate(query,
(current, include) => current.Include(include));
// Apply ordering if expressions are set
if (specification.OrderBy != null)
{
query = query.OrderBy(specification.OrderBy);
}
else if (specification.OrderByDescending != null)
{
query = query.OrderByDescending(specification.OrderByDescending);
}
// Apply paging if enabled
if (specification.isPagingEnabled)
{
query = query.Skip(specification.Skip)
.Take(specification.Take);
}
return query;
}
}
}

1
src/Infrastructure/Identity/Migrations/20170822214310_InitialIdentityModel.cs

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Metadata;

2
src/Infrastructure/Identity/Migrations/AppIdentityDbContextModelSnapshot.cs

@ -2,8 +2,6 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.eShopWeb.Infrastructure.Identity;
namespace Microsoft.eShopWeb.Infrastructure.Identity.Migrations
{

32
src/Infrastructure/Infrastructure.csproj

@ -1,20 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Microsoft.eShopWeb.Infrastructure</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>Microsoft.eShopWeb.Infrastructure</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.0" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ApplicationCore\ApplicationCore.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Data\Migrations\" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.1.3" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="2.1.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.1.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.2" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ApplicationCore\ApplicationCore.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Data\Migrations\" />
</ItemGroup>
</Project>

3
src/Web/.bowerrc

@ -1,3 +0,0 @@
{
"directory": "wwwroot/lib"
}

3
src/Web/Areas/Identity/Pages/_ViewStart.cshtml

@ -0,0 +1,3 @@
@{
Layout = "/Views/Shared/_Layout.cshtml";
}

2
src/Web/Constants.cs

@ -3,5 +3,7 @@
public static class Constants
{
public const string BASKET_COOKIENAME = "eShop";
public const int ITEMS_PER_PAGE = 10;
public const string DEFAULT_USERNAME = "Guest";
}
}

16
src/Web/Controllers/AccountController.cs

@ -1,17 +1,18 @@
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.eShopWeb.Web.ViewModels.Account;
using System;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Web.Controllers
{
[ApiExplorerSettings(IgnoreApi = true)]
[Route("[controller]/[action]")]
[Authorize]
[Authorize] // Controllers that mainly require Authorization still use Controller/View; other pages use Pages
public class AccountController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
@ -149,10 +150,11 @@ namespace Microsoft.eShopWeb.Web.Controllers
{
await _signInManager.SignOutAsync();
return RedirectToAction(nameof(CatalogController.Index), "Catalog");
return RedirectToPage("/Index");
}
[AllowAnonymous]
[HttpGet]
public IActionResult Register()
{
return View();
@ -184,7 +186,7 @@ namespace Microsoft.eShopWeb.Web.Controllers
{
if (userId == null || code == null)
{
return RedirectToAction(nameof(CatalogController.Index), "Catalog");
return RedirectToPage("/Index");
}
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
@ -215,7 +217,7 @@ namespace Microsoft.eShopWeb.Web.Controllers
}
else
{
return RedirectToAction(nameof(CatalogController.Index), "Catalog");
return RedirectToPage("/Index");
}
}

5
src/Web/Controllers/Api/BaseApiController.cs

@ -1,10 +1,9 @@
using Microsoft.eShopWeb.Web.Services;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
namespace Microsoft.eShopWeb.Web.Controllers.Api
{
[Route("api/[controller]/[action]")]
[ApiController]
public class BaseApiController : Controller
{ }
}

111
src/Web/Controllers/BasketController.cs

@ -1,111 +0,0 @@
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.AspNetCore.Http;
using Microsoft.eShopWeb.Web.ViewModels;
using Microsoft.AspNetCore.Identity;
using Microsoft.eShopWeb.Infrastructure.Identity;
using System;
using System.Collections.Generic;
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
using Microsoft.AspNetCore.Authorization;
using Microsoft.eShopWeb.Web.Interfaces;
namespace Microsoft.eShopWeb.Web.Controllers
{
[Route("[controller]/[action]")]
public class BasketController : Controller
{
private readonly IBasketService _basketService;
private readonly IUriComposer _uriComposer;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IAppLogger<BasketController> _logger;
private readonly IOrderService _orderService;
private readonly IBasketViewModelService _basketViewModelService;
public BasketController(IBasketService basketService,
IBasketViewModelService basketViewModelService,
IOrderService orderService,
IUriComposer uriComposer,
SignInManager<ApplicationUser> signInManager,
IAppLogger<BasketController> logger)
{
_basketService = basketService;
_uriComposer = uriComposer;
_signInManager = signInManager;
_logger = logger;
_orderService = orderService;
_basketViewModelService = basketViewModelService;
}
[HttpGet]
public async Task<IActionResult> Index()
{
var basketModel = await GetBasketViewModelAsync();
return View(basketModel);
}
[HttpPost]
public async Task<IActionResult> Index(Dictionary<string, int> items)
{
var basketViewModel = await GetBasketViewModelAsync();
await _basketService.SetQuantities(basketViewModel.Id, items);
return View(await GetBasketViewModelAsync());
}
// POST: /Basket/AddToBasket
[HttpPost]
public async Task<IActionResult> AddToBasket(CatalogItemViewModel productDetails)
{
if (productDetails?.Id == null)
{
return RedirectToAction("Index", "Catalog");
}
var basketViewModel = await GetBasketViewModelAsync();
await _basketService.AddItemToBasket(basketViewModel.Id, productDetails.Id, productDetails.Price, 1);
return RedirectToAction("Index");
}
[HttpPost]
[Authorize]
public async Task<IActionResult> Checkout(Dictionary<string, int> items)
{
var basketViewModel = await GetBasketViewModelAsync();
await _basketService.SetQuantities(basketViewModel.Id, items);
await _orderService.CreateOrderAsync(basketViewModel.Id, new Address("123 Main St.", "Kent", "OH", "United States", "44240"));
await _basketService.DeleteBasketAsync(basketViewModel.Id);
return View("Checkout");
}
private async Task<BasketViewModel> GetBasketViewModelAsync()
{
if (_signInManager.IsSignedIn(HttpContext.User))
{
return await _basketViewModelService.GetOrCreateBasketForUser(User.Identity.Name);
}
string anonymousId = GetOrSetBasketCookie();
return await _basketViewModelService.GetOrCreateBasketForUser(anonymousId);
}
private string GetOrSetBasketCookie()
{
if (Request.Cookies.ContainsKey(Constants.BASKET_COOKIENAME))
{
return Request.Cookies[Constants.BASKET_COOKIENAME];
}
string anonymousId = Guid.NewGuid().ToString();
var cookieOptions = new CookieOptions();
cookieOptions.Expires = DateTime.Today.AddYears(10);
Response.Cookies.Append(Constants.BASKET_COOKIENAME, anonymousId, cookieOptions);
return anonymousId;
}
}
}

29
src/Web/Controllers/CatalogController.cs

@ -1,29 +0,0 @@
using Microsoft.eShopWeb.Web.Services;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Web.Controllers
{
[Route("")]
public class CatalogController : Controller
{
private readonly ICatalogService _catalogService;
public CatalogController(ICatalogService catalogService) => _catalogService = catalogService;
[HttpGet]
[HttpPost]
public async Task<IActionResult> Index(int? brandFilterApplied, int? typesFilterApplied, int? page)
{
var itemsPage = 10;
var catalogModel = await _catalogService.GetCatalogItems(page ?? 0, itemsPage, brandFilterApplied, typesFilterApplied);
return View(catalogModel);
}
[HttpGet("Error")]
public IActionResult Error()
{
return View();
}
}
}

13
src/Web/Controllers/ManageController.cs

@ -1,11 +1,11 @@
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopWeb.Web.ViewModels.Manage;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.eShopWeb.Web.Services;
using Microsoft.eShopWeb.Web.ViewModels.Manage;
using System;
using System.Linq;
using System.Text;
@ -14,7 +14,8 @@ using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Web.Controllers
{
[Authorize]
[ApiExplorerSettings(IgnoreApi = true)]
[Authorize] // Controllers that mainly require Authorization still use Controller/View; other pages use Pages
[Route("[controller]/[action]")]
public class ManageController : Controller
{
@ -44,7 +45,7 @@ namespace Microsoft.eShopWeb.Web.Controllers
public string StatusMessage { get; set; }
[HttpGet]
public async Task<IActionResult> Index()
public async Task<IActionResult> MyAccount()
{
var user = await _userManager.GetUserAsync(User);
if (user == null)

23
src/Web/Controllers/OrderController.cs

@ -1,26 +1,27 @@
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.eShopWeb.Web.ViewModels;
using System;
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using System.Linq;
using Microsoft.eShopWeb.ApplicationCore.Specifications;
using Microsoft.eShopWeb.Web.ViewModels;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Web.Controllers
{
[Authorize]
[ApiExplorerSettings(IgnoreApi = true)]
[Authorize] // Controllers that mainly require Authorization still use Controller/View; other pages use Pages
[Route("[controller]/[action]")]
public class OrderController : Controller
{
private readonly IOrderRepository _orderRepository;
public OrderController(IOrderRepository orderRepository) {
public OrderController(IOrderRepository orderRepository)
{
_orderRepository = orderRepository;
}
public async Task<IActionResult> Index()
[HttpGet()]
public async Task<IActionResult> MyOrders()
{
var orders = await _orderRepository.ListAsync(new CustomerOrdersWithItemsSpecification(User.Identity.Name));

16
src/Web/Data/ApplicationDbContext.cs

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace Web2.Data
{
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
}

236
src/Web/Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs

@ -0,0 +1,236 @@
// <auto-generated />
using System;
using Web2.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Web2.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("00000000000000_CreateIdentitySchema")]
partial class CreateIdentitySchema
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.0-preview1")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<string>("SecurityStamp");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128);
b.Property<string>("ProviderKey")
.HasMaxLength(128);
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider")
.HasMaxLength(128);
b.Property<string>("Name")
.HasMaxLength(128);
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

220
src/Web/Data/Migrations/00000000000000_CreateIdentitySchema.cs

@ -0,0 +1,220 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
namespace Web2.Data.Migrations
{
public partial class CreateIdentitySchema : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Name = table.Column<string>(maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(maxLength: 256, nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<string>(nullable: false),
UserName = table.Column<string>(maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
Email = table.Column<string>(maxLength: 256, nullable: true),
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(nullable: false),
PasswordHash = table.Column<string>(nullable: true),
SecurityStamp = table.Column<string>(nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true),
PhoneNumber = table.Column<string>(nullable: true),
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
TwoFactorEnabled = table.Column<bool>(nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
LockoutEnabled = table.Column<bool>(nullable: false),
AccessFailedCount = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
RoleId = table.Column<string>(nullable: false),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
UserId = table.Column<string>(nullable: false),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserLogins",
columns: table => new
{
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
ProviderKey = table.Column<string>(maxLength: 128, nullable: false),
ProviderDisplayName = table.Column<string>(nullable: true),
UserId = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
table.ForeignKey(
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
RoleId = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
Name = table.Column<string>(maxLength: 128, nullable: false),
Value = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
table.ForeignKey(
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
table: "AspNetRoles",
column: "NormalizedName",
unique: true,
filter: "[NormalizedName] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserClaims_UserId",
table: "AspNetUserClaims",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserLogins_UserId",
table: "AspNetUserLogins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_RoleId",
table: "AspNetUserRoles",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "EmailIndex",
table: "AspNetUsers",
column: "NormalizedEmail");
migrationBuilder.CreateIndex(
name: "UserNameIndex",
table: "AspNetUsers",
column: "NormalizedUserName",
unique: true,
filter: "[NormalizedUserName] IS NOT NULL");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AspNetRoleClaims");
migrationBuilder.DropTable(
name: "AspNetUserClaims");
migrationBuilder.DropTable(
name: "AspNetUserLogins");
migrationBuilder.DropTable(
name: "AspNetUserRoles");
migrationBuilder.DropTable(
name: "AspNetUserTokens");
migrationBuilder.DropTable(
name: "AspNetRoles");
migrationBuilder.DropTable(
name: "AspNetUsers");
}
}
}

234
src/Web/Data/Migrations/ApplicationDbContextModelSnapshot.cs

@ -0,0 +1,234 @@
// <auto-generated />
using System;
using Web2.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace Web2.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.0-preview1")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<string>("SecurityStamp");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128);
b.Property<string>("ProviderKey")
.HasMaxLength(128);
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider")
.HasMaxLength(128);
b.Property<string>("Name")
.HasMaxLength(128);
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

4
src/Web/Dockerfile

@ -7,7 +7,7 @@
#
# RUN COMMAND
# docker run --name eshopweb --rm -it -p 8000:5106 web
FROM microsoft/dotnet:2.1-sdk AS build
FROM microsoft/dotnet:2.2-sdk AS build
WORKDIR /app
COPY *.sln .
@ -17,7 +17,7 @@ RUN dotnet restore
RUN dotnet publish -c Release -o out
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS runtime
FROM microsoft/dotnet:2.2-aspnetcore-runtime AS runtime
WORKDIR /app
COPY --from=build /app/src/Web/out ./

40
src/Web/HealthChecks/ApiHealthCheck.cs

@ -0,0 +1,40 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Web.HealthChecks
{
public class ApiHealthCheck : IHealthCheck
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly LinkGenerator _linkGenerator;
public ApiHealthCheck(IHttpContextAccessor httpContextAccessor, LinkGenerator linkGenerator)
{
_httpContextAccessor = httpContextAccessor;
_linkGenerator = linkGenerator;
}
public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default(CancellationToken))
{
var request = _httpContextAccessor.HttpContext.Request;
string apiLink = _linkGenerator.GetPathByAction("List", "Catalog");
string myUrl = request.Scheme + "://" + request.Host.ToString() + apiLink;
var client = new HttpClient();
var response = await client.GetAsync(myUrl);
var pageContents = await response.Content.ReadAsStringAsync();
if (pageContents.Contains(".NET Bot Black Sweatshirt"))
{
return HealthCheckResult.Healthy("The check indicates a healthy result.");
}
return HealthCheckResult.Unhealthy("The check indicates an unhealthy result.");
}
}
}

36
src/Web/HealthChecks/HomePageHealthCheck.cs

@ -0,0 +1,36 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Web.HealthChecks
{
public class HomePageHealthCheck : IHealthCheck
{
private readonly IHttpContextAccessor _httpContextAccessor;
public HomePageHealthCheck(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public async Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default(CancellationToken))
{
var request = _httpContextAccessor.HttpContext.Request;
string myUrl = request.Scheme + "://" + request.Host.ToString();
var client = new HttpClient();
var response = await client.GetAsync(myUrl);
var pageContents = await response.Content.ReadAsStringAsync();
if (pageContents.Contains(".NET Bot Black Sweatshirt"))
{
return HealthCheckResult.Healthy("The check indicates a healthy result.");
}
return HealthCheckResult.Unhealthy("The check indicates an unhealthy result.");
}
}
}

2
src/Web/Interfaces/IBasketService.cs → src/Web/Interfaces/IBasketViewModelService.cs

@ -1,4 +1,4 @@
using Microsoft.eShopWeb.Web.ViewModels;
using Microsoft.eShopWeb.Web.Pages.Basket;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Web.Interfaces

11
src/Web/Models/ErrorViewModel.cs

@ -0,0 +1,11 @@
using System;
namespace Web2.Models
{
public class ErrorViewModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}

2
src/Web/ViewModels/BasketItemViewModel.cs → src/Web/Pages/Basket/BasketItemViewModel.cs

@ -1,4 +1,4 @@
namespace Microsoft.eShopWeb.Web.ViewModels
namespace Microsoft.eShopWeb.Web.Pages.Basket
{
public class BasketItemViewModel
{

2
src/Web/ViewModels/BasketViewModel.cs → src/Web/Pages/Basket/BasketViewModel.cs

@ -2,7 +2,7 @@
using System.Collections.Generic;
using System.Linq;
namespace Microsoft.eShopWeb.Web.ViewModels
namespace Microsoft.eShopWeb.Web.Pages.Basket
{
public class BasketViewModel
{

16
src/Web/Pages/Basket/Checkout.cshtml

@ -0,0 +1,16 @@
@page
@model CheckoutModel
@{
ViewData["Title"] = "Checkout Complete";
}
<section class="esh-catalog-hero">
<div class="container">
<img class="esh-catalog-title" src="~/images/main_banner_text.png" />
</div>
</section>
<div class="container">
<h1>Thanks for your Order!</h1>
<a asp-page="/Index">Continue Shopping...</a>
</div>

83
src/Web/Pages/Basket/Checkout.cshtml.cs

@ -0,0 +1,83 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.eShopWeb.Web.Interfaces;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Web.Pages.Basket
{
public class CheckoutModel : PageModel
{
private readonly IBasketService _basketService;
private readonly IUriComposer _uriComposer;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly IOrderService _orderService;
private string _username = null;
private readonly IBasketViewModelService _basketViewModelService;
public CheckoutModel(IBasketService basketService,
IBasketViewModelService basketViewModelService,
IUriComposer uriComposer,
SignInManager<ApplicationUser> signInManager,
IOrderService orderService)
{
_basketService = basketService;
_uriComposer = uriComposer;
_signInManager = signInManager;
_orderService = orderService;
_basketViewModelService = basketViewModelService;
}
public BasketViewModel BasketModel { get; set; } = new BasketViewModel();
public void OnGet()
{
}
public async Task<IActionResult> OnPost(Dictionary<string, int> items)
{
await SetBasketModelAsync();
await _basketService.SetQuantities(BasketModel.Id, items);
await _orderService.CreateOrderAsync(BasketModel.Id, new Address("123 Main St.", "Kent", "OH", "United States", "44240"));
await _basketService.DeleteBasketAsync(BasketModel.Id);
return RedirectToPage();
}
private async Task SetBasketModelAsync()
{
if (_signInManager.IsSignedIn(HttpContext.User))
{
BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(User.Identity.Name);
}
else
{
GetOrSetBasketCookieAndUserName();
BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(_username);
}
}
private void GetOrSetBasketCookieAndUserName()
{
if (Request.Cookies.ContainsKey(Constants.BASKET_COOKIENAME))
{
_username = Request.Cookies[Constants.BASKET_COOKIENAME];
}
if (_username != null) return;
_username = Guid.NewGuid().ToString();
var cookieOptions = new CookieOptions();
cookieOptions.Expires = DateTime.Today.AddYears(10);
Response.Cookies.Append(Constants.BASKET_COOKIENAME, _username, cookieOptions);
}
}
}

58
src/Web/Views/Basket/Index.cshtml → src/Web/Pages/Basket/Index.cshtml

@ -1,17 +1,17 @@
@using Microsoft.eShopWeb.Web.ViewModels
@model BasketViewModel
@page "{handler?}"
@model IndexModel
@{
ViewData["Title"] = "Basket";
}
<section class="esh-catalog-hero">
<div class="container">
<img class="esh-catalog-title" src="../images/main_banner_text.png" />
<img class="esh-catalog-title" src="~/images/main_banner_text.png" />
</div>
</section>
<div class="container">
@if (Model.Items.Any())
@if (Model.BasketModel.Items.Any())
{
<form method="post">
<article class="esh-basket-titles row">
@ -23,9 +23,9 @@
<section class="esh-basket-title col-xs-2">Cost</section>
</article>
<div class="esh-catalog-items row">
@for (int i=0; i< Model.Items.Count; i++)
@for (int i = 0; i < Model.BasketModel.Items.Count; i++)
{
var item = Model.Items[i];
var item = Model.BasketModel.Items[i];
<article class="esh-basket-items row">
<div>
<section class="esh-basket-item esh-basket-item--middle col-lg-3 hidden-lg-down">
@ -44,32 +44,36 @@
</div>
</article>
@*<div class="esh-catalog-item col-md-4">
@item.ProductId
</div>*@
@item.ProductId
</div>*@
<div class="container">
<article class="esh-basket-titles esh-basket-titles--clean row">
<section class="esh-basket-title col-xs-10"></section>
<section class="esh-basket-title col-xs-2">Total</section>
</article>
}
<article class="esh-basket-items row">
<section class="esh-basket-item col-xs-10"></section>
<section class="esh-basket-item esh-basket-item--mark col-xs-2">$ @Model.Total()</section>
</article>
<div class="container">
<article class="esh-basket-titles esh-basket-titles--clean row">
<section class="esh-basket-title col-xs-10"></section>
<section class="esh-basket-title col-xs-2">Total</section>
</article>
<article class="esh-basket-items row">
<section class="esh-basket-item col-xs-10"></section>
<section class="esh-basket-item esh-basket-item--mark col-xs-2">$ @Model.BasketModel.Total().ToString("N2")</section>
</article>
<article class="esh-basket-items row">
<section class="esh-basket-item col-xs-7"></section>
<section class="esh-basket-item col-xs-2">
@*<button class="btn esh-basket-checkout" name="name" value="" type="submit">[ Update ]</button>*@
</section>
</article>
</div>
<article class="esh-basket-items row">
<section class="esh-basket-item col-xs-7"></section>
<section class="esh-basket-item col-xs-2">
@*<button class="btn esh-basket-checkout" name="name" value="" type="submit">[ Update ]</button>*@
</section>
</article>
</div>
}
<section class="esh-basket-item col-xs-push-8 col-xs-4">
<button class="btn esh-basket-checkout" name="updatebutton" value="" type="submit"
asp-action="Index">[ Update ]</button>
<input type="submit" asp-action="Checkout"
asp-page-handler="Update">
[ Update ]
</button>
<input type="submit" asp-page="Checkout"
class="btn esh-basket-checkout"
value="[ Checkout ]" name="action" />
</section>

92
src/Web/Pages/Basket/Index.cshtml.cs

@ -0,0 +1,92 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.eShopWeb.Web.Interfaces;
using Microsoft.eShopWeb.Web.ViewModels;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Web.Pages.Basket
{
public class IndexModel : PageModel
{
private readonly IBasketService _basketService;
private const string _basketSessionKey = "basketId";
private readonly IUriComposer _uriComposer;
private readonly SignInManager<ApplicationUser> _signInManager;
private string _username = null;
private readonly IBasketViewModelService _basketViewModelService;
public IndexModel(IBasketService basketService,
IBasketViewModelService basketViewModelService,
IUriComposer uriComposer,
SignInManager<ApplicationUser> signInManager)
{
_basketService = basketService;
_uriComposer = uriComposer;
_signInManager = signInManager;
_basketViewModelService = basketViewModelService;
}
public BasketViewModel BasketModel { get; set; } = new BasketViewModel();
public async Task OnGet()
{
await SetBasketModelAsync();
}
public async Task<IActionResult> OnPost(CatalogItemViewModel productDetails)
{
if (productDetails?.Id == null)
{
return RedirectToPage("/Index");
}
await SetBasketModelAsync();
await _basketService.AddItemToBasket(BasketModel.Id, productDetails.Id, productDetails.Price, 1);
await SetBasketModelAsync();
return RedirectToPage();
}
public async Task OnPostUpdate(Dictionary<string, int> items)
{
await SetBasketModelAsync();
await _basketService.SetQuantities(BasketModel.Id, items);
await SetBasketModelAsync();
}
private async Task SetBasketModelAsync()
{
if (_signInManager.IsSignedIn(HttpContext.User))
{
BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(User.Identity.Name);
}
else
{
GetOrSetBasketCookieAndUserName();
BasketModel = await _basketViewModelService.GetOrCreateBasketForUser(_username);
}
}
private void GetOrSetBasketCookieAndUserName()
{
if (Request.Cookies.ContainsKey(Constants.BASKET_COOKIENAME))
{
_username = Request.Cookies[Constants.BASKET_COOKIENAME];
}
if (_username != null) return;
_username = Guid.NewGuid().ToString();
var cookieOptions = new CookieOptions { IsEssential = true };
cookieOptions.Expires = DateTime.Today.AddYears(10);
Response.Cookies.Append(Constants.BASKET_COOKIENAME, _username, cookieOptions);
}
}
}

26
src/Web/Pages/Error.cshtml

@ -0,0 +1,26 @@
@page
@model ErrorModel
@{
ViewData["Title"] = "Error";
}
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>

19
src/Web/Pages/Error.cshtml.cs

@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Diagnostics;
namespace Microsoft.eShopWeb.Web.Pages
{
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public class ErrorModel : PageModel
{
public string RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}
}

27
src/Web/Views/Catalog/Index.cshtml → src/Web/Pages/Index.cshtml

@ -1,43 +1,40 @@
@{
@page
@{
ViewData["Title"] = "Catalog";
@model CatalogIndexViewModel
@model IndexModel
}
<section class="esh-catalog-hero">
<div class="container">
<img class="esh-catalog-title" src="../images/main_banner_text.png" />
<img class="esh-catalog-title" src="~/images/main_banner_text.png" />
</div>
</section>
<section class="esh-catalog-filters">
<div class="container">
<form asp-action="Index" asp-controller="Catalog" method="post">
<form method="get">
<label class="esh-catalog-label" data-title="brand">
<select asp-for="@Model.BrandFilterApplied" asp-items="@Model.Brands" class="esh-catalog-filter"></select>
<select asp-for="@Model.CatalogModel.BrandFilterApplied" asp-items="@Model.CatalogModel.Brands" class="esh-catalog-filter"></select>
</label>
<label class="esh-catalog-label" data-title="type">
<select asp-for="@Model.TypesFilterApplied" asp-items="@Model.Types" class="esh-catalog-filter"></select>
<select asp-for="@Model.CatalogModel.TypesFilterApplied" asp-items="@Model.CatalogModel.Types" class="esh-catalog-filter"></select>
</label>
<input class="esh-catalog-send" type="image" src="images/arrow-right.svg" />
</form>
</div>
</section>
<div class="container">
@if (Model.CatalogItems.Any())
@if (Model.CatalogModel.CatalogItems.Any())
{
<partial name="_pagination" for="PaginationInfo" />
<partial name="_pagination" for="CatalogModel.PaginationInfo" />
<div class="esh-catalog-items row">
@foreach (var catalogItem in Model.CatalogItems)
@foreach (var catalogItem in Model.CatalogModel.CatalogItems)
{
<div class="esh-catalog-item col-md-4">
<partial name="_product" model="catalogItem" />
<partial name="_product" for="@catalogItem" />
</div>
}
</div>
<partial name="_pagination" for="PaginationInfo" />
<partial name="_pagination" for="CatalogModel.PaginationInfo" />
}
else
{

26
src/Web/Pages/Index.cshtml.cs

@ -0,0 +1,26 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.eShopWeb.Web.Services;
using Microsoft.eShopWeb.Web.ViewModels;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Web.Pages
{
public class IndexModel : PageModel
{
private readonly ICatalogService _catalogService;
public IndexModel(ICatalogService catalogService)
{
_catalogService = catalogService;
}
public CatalogIndexViewModel CatalogModel { get; set; } = new CatalogIndexViewModel();
public async Task OnGet(CatalogIndexViewModel catalogModel, int? pageId)
{
CatalogModel = await _catalogService.GetCatalogItems(pageId ?? 0, Constants.ITEMS_PER_PAGE, catalogModel.BrandFilterApplied, catalogModel.TypesFilterApplied);
}
}
}

8
src/Web/Pages/Privacy.cshtml

@ -0,0 +1,8 @@
@page
@model PrivacyModel
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>

11
src/Web/Pages/Privacy.cshtml.cs

@ -0,0 +1,11 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace Microsoft.eShopWeb.Web.Pages
{
public class PrivacyModel : PageModel
{
public void OnGet()
{
}
}
}

9
src/Web/ViewComponents/Basket.cs → src/Web/Pages/Shared/Components/BasketComponent/Basket.cs

@ -1,14 +1,13 @@
using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopWeb;
using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.eShopWeb.Web.Interfaces;
using Microsoft.eShopWeb.Web.Pages.Basket;
using Microsoft.eShopWeb.Web.ViewModels;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Web.ViewComponents
namespace Microsoft.eShopWeb.Web.Pages.Shared.Components.BasketComponent
{
public class Basket : ViewComponent
{

13
src/Web/Pages/Shared/Components/BasketComponent/Default.cshtml

@ -0,0 +1,13 @@
@model BasketComponentViewModel
@{
ViewData["Title"] = "My Basket";
}
<a class="esh-basketstatus "
asp-page="/Basket/Index">
<div class="esh-basketstatus-image">
<img src="~/images/cart.png" />
</div>
<div class="esh-basketstatus-badge">
@Model.ItemsCount
</div>
</a>

8
src/Web/Views/Catalog/_pagination.cshtml → src/Web/Pages/Shared/_pagination.cshtml

@ -7,9 +7,7 @@
<div class="col-md-2 col-xs-12">
<a class="esh-pager-item-left esh-pager-item--navigable @Model.Previous"
id="Previous"
asp-controller="Catalog"
asp-action="Index"
asp-route-page="@(Model.ActualPage - 1)"
asp-route-pageid="@(Model.ActualPage - 1)"
aria-label="Previous">
Previous
</a>
@ -24,9 +22,7 @@
<div class="col-md-2 col-xs-12">
<a class="esh-pager-item-right esh-pager-item--navigable @Model.Next"
id="Next"
asp-controller="Catalog"
asp-action="Index"
asp-route-page="@(Model.ActualPage + 1)"
asp-route-pageid="@(Model.ActualPage + 1)"
aria-label="Next">
Next
</a>

10
src/Web/Views/Catalog/_product.cshtml → src/Web/Pages/Shared/_product.cshtml

@ -1,22 +1,14 @@
@model CatalogItemViewModel
<form asp-controller="Basket" asp-action="AddToBasket">
<form asp-page="/Basket/Index" method="post">
<img class="esh-catalog-thumbnail" src="@Model.PictureUri" />
<input class="esh-catalog-button" type="submit" value="[ ADD TO BASKET ]" />
<div class="esh-catalog-name">
<span>@Model.Name</span>
</div>
<div class="esh-catalog-price">
<span>@Model.Price.ToString("N2")</span>
</div>
@*<input type="hidden" asp-for="@Model.CatalogBrand" name="brand" />
<input type="hidden" asp-for="@Model.CatalogBrandId" name="brandId" />
<input type="hidden" asp-for="@Model.CatalogType" name="type" />
<input type="hidden" asp-for="@Model.CatalogTypeId" name="typeId" />
<input type="hidden" asp-for="@Model.Description" name="description" />*@
<input type="hidden" asp-for="@Model.Id" name="id" />
<input type="hidden" asp-for="@Model.Name" name="name" />
<input type="hidden" asp-for="@Model.PictureUri" name="pictureUri" />

9
src/Web/Pages/_ViewImports.cshtml

@ -0,0 +1,9 @@
@using Microsoft.eShopWeb.Web
@using Microsoft.eShopWeb.Web.ViewModels
@using Microsoft.eShopWeb.Web.ViewModels.Account
@using Microsoft.eShopWeb.Web.ViewModels.Manage
@using Microsoft.eShopWeb.Web.Pages
@using Microsoft.AspNetCore.Identity
@using Microsoft.eShopWeb.Infrastructure.Identity
@namespace Microsoft.eShopWeb.Web.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

3
src/Web/Pages/_ViewStart.cshtml

@ -0,0 +1,3 @@
@{
Layout = "_Layout";
}

13
src/Web/Program.cs

@ -1,11 +1,11 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.eShopWeb.Infrastructure.Data;
using System;
using Microsoft.Extensions.Logging;
using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
namespace Microsoft.eShopWeb.Web
{
@ -41,7 +41,6 @@ namespace Microsoft.eShopWeb.Web
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseUrls("http://0.0.0.0:5106")
.UseStartup<Startup>();
}
}

16
src/Web/Properties/launchSettings.json

@ -1,10 +1,10 @@
{
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:5106/",
"sslPort": 0
"applicationUrl": "http://localhost:17469",
"sslPort": 44315
}
},
"profiles": {
@ -15,13 +15,13 @@
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"eShopWeb": {
"Web - PROD": {
"commandName": "Project",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "http://localhost:5106"
"ASPNETCORE_ENVIRONMENT": "Production"
}
}
}
}
}

10
src/Web/Services/BasketViewModelService.cs

@ -1,9 +1,9 @@
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Specifications;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.eShopWeb.ApplicationCore.Entities.BasketAggregate;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Specifications;
using Microsoft.eShopWeb.Web.Interfaces;
using Microsoft.eShopWeb.Web.ViewModels;
using Microsoft.eShopWeb.Web.Pages.Basket;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@ -30,7 +30,7 @@ namespace Microsoft.eShopWeb.Web.Services
var basketSpec = new BasketWithItemsSpecification(userName);
var basket = (await _basketRepository.ListAsync(basketSpec)).FirstOrDefault();
if(basket == null)
if (basket == null)
{
return await CreateBasketForUser(userName);
}

26
src/Web/Services/CatalogService.cs

@ -1,13 +1,13 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.eShopWeb.Web.ViewModels;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.Extensions.Logging;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using System;
using Microsoft.eShopWeb.ApplicationCore.Specifications;
using Microsoft.eShopWeb.Web.ViewModels;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.eShopWeb.Web.Services
{
@ -42,14 +42,12 @@ namespace Microsoft.eShopWeb.Web.Services
_logger.LogInformation("GetCatalogItems called.");
var filterSpecification = new CatalogFilterSpecification(brandId, typeId);
var root = _itemRepository.List(filterSpecification);
var totalItems = root.Count();
var filterPaginatedSpecification =
new CatalogFilterPaginatedSpecification(itemsPage * pageIndex, itemsPage, brandId, typeId);
var itemsOnPage = root
.Skip(itemsPage * pageIndex)
.Take(itemsPage)
.ToList();
// the implementation below using ForEach and Count. We need a List.
var itemsOnPage = _itemRepository.List(filterPaginatedSpecification).ToList();
var totalItems = _itemRepository.Count(filterSpecification);
itemsOnPage.ForEach(x =>
{

17
src/Web/SlugifyParameterTransformer.cs

@ -0,0 +1,17 @@
using Microsoft.AspNetCore.Routing;
using System.Text.RegularExpressions;
namespace Microsoft.eShopWeb.Web
{
public class SlugifyParameterTransformer : IOutboundParameterTransformer
{
public string TransformOutbound(object value)
{
if (value == null) { return null; }
// Slugify value
return Regex.Replace(value.ToString(), "([a-z])([A-Z])", "$1-$2").ToLower();
}
}
}

154
src/Web/Startup.cs

@ -1,19 +1,30 @@
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Routing;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Services;
using Microsoft.eShopWeb.Infrastructure.Data;
using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.eShopWeb.Infrastructure.Logging;
using Microsoft.eShopWeb.Infrastructure.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.eShopWeb.Web.HealthChecks;
using Microsoft.eShopWeb.Web.Interfaces;
using Microsoft.eShopWeb.Web.Services;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Newtonsoft.Json;
using Swashbuckle.AspNetCore.Swagger;
using System;
using System.Linq;
using System.Net.Mime;
using System.Text;
namespace Microsoft.eShopWeb.Web
@ -65,23 +76,17 @@ namespace Microsoft.eShopWeb.Web
ConfigureServices(services);
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
ConfigureCookieSettings(services);
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddDefaultUI(UIFramework.Bootstrap4)
.AddEntityFrameworkStores<AppIdentityDbContext>()
.AddDefaultTokenProviders();
services.ConfigureApplicationCookie(options =>
{
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromHours(1);
options.LoginPath = "/Account/Signin";
options.LogoutPath = "/Account/Signout";
options.Cookie = new CookieBuilder
{
IsEssential = true // required for auth to work without explicit user consent; adjust to suit your privacy policy
};
});
.AddDefaultTokenProviders();
services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
services.AddScoped(typeof(IAsyncRepository<>), typeof(EfRepository<>));
@ -101,40 +106,132 @@ namespace Microsoft.eShopWeb.Web
// Add memory cache services
services.AddMemoryCache();
services.AddMvc()
.SetCompatibilityVersion(AspNetCore.Mvc.CompatibilityVersion.Version_2_1);
services.AddRouting(options =>
{
// Replace the type and the name used to refer to it with your own
// IOutboundParameterTransformer implementation
options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer);
});
services.AddMvc(options =>
{
options.Conventions.Add(new RouteTokenTransformerConvention(
new SlugifyParameterTransformer()));
}
)
.AddRazorPagesOptions(options =>
{
options.Conventions.AuthorizePage("/Basket/Checkout");
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddHttpContextAccessor();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
});
services.AddHealthChecks()
.AddCheck<HomePageHealthCheck>("home_page_health_check")
.AddCheck<ApiHealthCheck>("api_health_check");
_services = services;
_services = services; // used to debug registered services
}
private static void ConfigureCookieSettings(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.ConfigureApplicationCookie(options =>
{
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromHours(1);
options.LoginPath = "/Account/Signin";
options.LogoutPath = "/Account/Signout";
options.Cookie = new CookieBuilder
{
IsEssential = true // required for auth to work without explicit user consent; adjust to suit your privacy policy
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app,
IHostingEnvironment env)
public void Configure(IApplicationBuilder app, IHostingEnvironment env, LinkGenerator linkGenerator)
{
//app.UseDeveloperExceptionPage();
app.UseHealthChecks("/health",
new HealthCheckOptions
{
ResponseWriter = async (context, report) =>
{
var result = JsonConvert.SerializeObject(
new
{
status = report.Status.ToString(),
errors = report.Entries.Select(e => new
{
key = e.Key,
value = Enum.GetName(typeof(HealthStatus), e.Value.Status)
})
});
context.Response.ContentType = MediaTypeNames.Application.Json;
await context.Response.WriteAsync(result);
}
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
ListAllRegisteredServices(app);
ListAllRegisteredServices(app, linkGenerator);
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Catalog/Error");
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc();
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller:slugify=Home}/{action:slugify=Index}/{id?}");
});
}
private void ListAllRegisteredServices(IApplicationBuilder app)
private void ListAllRegisteredServices(IApplicationBuilder app, LinkGenerator linkGenerator)
{
var homePageLink = linkGenerator.GetPathByAction("Index", "Catalog");
var loginLink = linkGenerator.GetPathByAction("SignIn", "Account");
app.Map("/allservices", builder => builder.Run(async context =>
{
var sb = new StringBuilder();
sb.Append("<a href=\"");
sb.Append(homePageLink);
sb.AppendLine("\">Return to site</a> | ");
sb.Append("<a href=\"");
sb.Append(loginLink);
sb.AppendLine("\">Login to site</a>");
sb.Append("<h1>All Services</h1>");
sb.Append("<table><thead>");
sb.Append("<tr><th>Type</th><th>Lifetime</th><th>Instance</th></tr>");
@ -151,5 +248,6 @@ namespace Microsoft.eShopWeb.Web
await context.Response.WriteAsync(sb.ToString());
}));
}
}
}

3
src/Web/ViewModels/Account/RegisterViewModel.cs

@ -1,5 +1,4 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations;
namespace Microsoft.eShopWeb.Web.ViewModels.Account
{

4
src/Web/ViewModels/Manage/TwoFactorAuthenticationViewModel.cs

@ -1,6 +1,4 @@
using System.ComponentModel.DataAnnotations;
namespace Microsoft.eShopWeb.Web.ViewModels.Manage
namespace Microsoft.eShopWeb.Web.ViewModels.Manage
{
public class TwoFactorAuthenticationViewModel
{

6
src/Web/ViewModels/OrderItemViewModel.cs

@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Microsoft.eShopWeb.Web.ViewModels
namespace Microsoft.eShopWeb.Web.ViewModels
{
public class OrderItemViewModel
{

1
src/Web/Views/Account/LoginWith2fa.cshtml

@ -1,4 +1,3 @@
@using Microsoft.eShopWeb.Web.ViewModels.Account
@model LoginWith2faViewModel
@{
ViewData["Title"] = "Two-factor authentication";

4
src/Web/Views/Account/Register.cshtml

@ -1,7 +1,3 @@
@using System.Collections.Generic
@using Microsoft.AspNetCore.Http
@using Microsoft.AspNetCore.Http.Authentication
@using Microsoft.eShopWeb.Web.ViewModels.Account
@model RegisterViewModel
@{
ViewData["Title"] = "Register";

1
src/Web/Views/Account/Signin.cshtml

@ -1,4 +1,3 @@
@using Microsoft.eShopWeb.Web.ViewModels.Account
@model LoginViewModel
@{
ViewData["Title"] = "Log in";

16
src/Web/Views/Basket/Checkout.cshtml

@ -1,16 +0,0 @@
@using Microsoft.eShopWeb.Web.ViewModels
@{
ViewData["Title"] = "Checkout Complete";
@model BasketViewModel
}
<section class="esh-catalog-hero">
<div class="container">
<img class="esh-catalog-title" src="../images/main_banner_text.png" />
</div>
</section>
<div class="container">
<h1>Thanks for your Order!</h1>
<a asp-controller="Catalog" asp-action="Index">Continue Shopping...</a>
</div>

12
src/Web/Views/Manage/Index.cshtml → src/Web/Views/Manage/MyAccount.cshtml

@ -18,15 +18,15 @@
<label asp-for="Email"></label>
@if (Model.IsEmailConfirmed)
{
<div class="input-group">
<input asp-for="Email" class="form-control" />
<span class="input-group-addon" aria-hidden="true"><span class="glyphicon glyphicon-ok text-success"></span></span>
</div>
<div class="input-group">
<input asp-for="Email" class="form-control" />
<span class="input-group-addon" aria-hidden="true"><span class="glyphicon glyphicon-ok text-success"></span></span>
</div>
}
else
{
<input asp-for="Email" class="form-control" />
<button asp-action="SendVerificationEmail" class="btn btn-link">Send verification email</button>
<input asp-for="Email" class="form-control" />
<button asp-action="SendVerificationEmail" class="btn btn-link">Send verification email</button>
}
<span asp-validation-for="Email" class="text-danger"></span>
</div>

19
src/Web/Views/Order/Detail.cshtml

@ -1,5 +1,4 @@
@using Microsoft.eShopWeb.Web.ViewModels
@model OrderViewModel
@model OrderViewModel
@{
ViewData["Title"] = "My Order History";
}
@ -26,14 +25,14 @@
</section>
@*<section class="esh-orders_detail-section">
<article class="esh-orders_detail-titles row">
<section class="esh-orders_detail-title col-xs-12">Description</section>
</article>
<article class="esh-orders_detail-titles row">
<section class="esh-orders_detail-title col-xs-12">Description</section>
</article>
<article class="esh-orders_detail-items row">
<section class="esh-orders_detail-item col-xs-12">@Model.Description</section>
</article>
</section>*@
<article class="esh-orders_detail-items row">
<section class="esh-orders_detail-item col-xs-12">@Model.Description</section>
</article>
</section>*@
<section class="esh-orders_detail-section">
<article class="esh-orders_detail-titles row">
@ -81,7 +80,7 @@
<article class="esh-orders_detail-items row">
<section class="esh-orders_detail-item col-xs-9"></section>
<section class="esh-orders_detail-item esh-orders_detail-item--mark col-xs-2">$ @Model.Total</section>
<section class="esh-orders_detail-item esh-orders_detail-item--mark col-xs-2">$ @Model.Total.ToString("N2")</section>
</article>
</section>
</div>

3
src/Web/Views/Order/Index.cshtml → src/Web/Views/Order/MyOrders.cshtml

@ -1,5 +1,4 @@
@using Microsoft.eShopWeb.Web.ViewModels
@model IEnumerable<OrderViewModel>
@model IEnumerable<OrderViewModel>
@{
ViewData["Title"] = "My Order History";
}

25
src/Web/Views/Shared/_CookieConsentPartial.cshtml

@ -0,0 +1,25 @@
@using Microsoft.AspNetCore.Http.Features
@{
var consentFeature = Context.Features.Get<ITrackingConsentFeature>();
var showBanner = !consentFeature?.CanTrack ?? false;
var cookieString = consentFeature?.CreateConsentCookie();
}
@if (showBanner)
{
<div id="cookieConsent" class="alert alert-info alert-dismissible fade show" role="alert">
Use this space to summarize your privacy and cookie use policy. <a asp-area="" asp-controller="Home" asp-action="Privacy">Learn More</a>.
<button type="button" class="accept-policy close" data-dismiss="alert" aria-label="Close" data-cookie-string="@cookieString">
<span aria-hidden="true">Accept</span>
</button>
</div>
<script>
(function () {
var button = document.querySelector("#cookieConsent button[data-cookie-string]");
button.addEventListener("click", function (event) {
document.cookie = button.dataset.cookieString;
}, false);
})();
</script>
}

117
src/Web/Views/Shared/_Layout.cshtml

@ -1,77 +1,66 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Microsoft.eShopOnWeb</title>
<environment names="Development">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/app.css" />
</environment>
<environment names="Staging,Production">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.5/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
<link rel="stylesheet" href="~/css/app.min.css" asp-append-version="true" />
</environment>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - Microsoft.eShopOnWeb</title>
<environment names="Development">
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
<link rel="stylesheet" href="~/css/app.css" />
</environment>
<environment names="Staging,Production">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha.5/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
<link rel="stylesheet" href="~/css/app.min.css" asp-append-version="true" />
</environment>
<link rel="stylesheet" href="~/css/app.component.css" />
<link rel="stylesheet" href="~/css/basket/basket.component.css" />
<link rel="stylesheet" href="~/css/catalog/pager.css" />
<link rel="stylesheet" href="~/css/catalog/catalog.component.css" />
<link rel="stylesheet" href="~/css/catalog/catalog.component.css" />
<link rel="stylesheet" href="~/css/basket/basket-status/basket-status.component.css" />
<link rel="stylesheet" href="~/css/orders/orders.component.css" />
</head>
<body>
<header class="navbar navbar-light navbar-static-top">
<div class="container">
<article class="row">
<section class="col-lg-7 col-md-6 col-xs-12">
<a asp-area="" asp-controller="Catalog" asp-action="Index" class="navbar-brand">
<img src="../images/brand.png" alt="eShop On Web"/>
</a>
</section>
<header class="navbar navbar-light navbar-static-top">
<div class="container">
<article class="row">
<section class="col-lg-7 col-md-6 col-xs-12">
<a asp-area="" asp-page="/Index" class="navbar-brand">
<img src="~/images/brand.png" alt="eShop On Web" />
</a>
</section>
<partial name="_LoginPartial" />
</article>
</div>
</header>
@RenderBody()
<footer class="esh-app-footer">
<div class="container">
<article class="row">
<section class="col-sm-6">
</section>
<section class="col-sm-6">
<div class="esh-app-footer-text hidden-xs"> e-ShopOnWeb. All rights reserved </div>
</section>
</article>
</div>
</footer>
<environment names="Development">
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
</environment>
<environment names="Staging,Production">
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery">
</script>
<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/bootstrap.min.js"
asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal">
</script>
<script src="~/js/site.min.js" asp-append-version="true"></script>
</environment>
@RenderSection("scripts", required: false)
</article>
</div>
</header>
@RenderBody()
<footer class="esh-app-footer">
<div class="container">
<article class="row">
<section class="col-sm-6"></section>
<section class="col-sm-6">
<div class="esh-app-footer-text hidden-xs"> e-ShopOnWeb. All rights reserved </div>
</section>
</article>
</div>
</footer>
<environment names="Development">
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
</environment>
<environment names="Staging,Production">
<script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.2.0.min.js"
asp-fallback-src="~/lib/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery">
</script>
<script src="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.6/bootstrap.min.js"
asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.min.js"
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal">
</script>
<script src="~/js/site.min.js" asp-append-version="true"></script>
</environment>
@RenderSection("scripts", required: false)
</body>
</html>

107
src/Web/Views/Shared/_LoginPartial.cshtml

@ -1,66 +1,55 @@
@using Microsoft.AspNetCore.Identity
@if (Context.User.Identity.IsAuthenticated)
@if (Context.User.Identity.IsAuthenticated)
{
<section class="col-lg-4 col-md-5 col-xs-12">
<div class="esh-identity">
<form asp-area="" asp-controller="Account" asp-action="SignOut" method="post" id="logoutForm" class="navbar-right">
<section class="esh-identity-section">
@*<div class="esh-identity-name">@User.FindFirst(x => x.Type == "preferred_username").Value</div>*@
<img class="esh-identity-image" src="~/images/arrow-down.png">
</section>
<section class="esh-identity-drop">
<a class="esh-identity-item"
asp-controller="Order"
asp-action="Index">
<div class="esh-identity-name esh-identity-name--upper">My orders</div>
<img class="esh-identity-image" src="~/images/my_orders.png">
</a>
<a class="esh-identity-item"
asp-controller="Manage"
asp-action="Index">
<div class="esh-identity-name esh-identity-name--upper">My account</div>
<img class="esh-identity-image" src="~/images/my_orders.png">
</a>
<a class="esh-identity-item"
href="javascript:document.getElementById('logoutForm').submit()">
<div class="esh-identity-name esh-identity-name--upper">Log Out</div>
<img class="esh-identity-image" src="~/images/logout.png">
</a>
</section>
</form>
</div>
</section>
<section class="col-lg-4 col-md-5 col-xs-12">
<div class="esh-identity">
<form asp-area="" asp-controller="Account" asp-action="SignOut" method="post" id="logoutForm" class="navbar-right">
<section class="esh-identity-section">
@*<div class="esh-identity-name">@User.FindFirst(x => x.Type == "preferred_username").Value</div>*@
<img class="esh-identity-image" src="~/images/arrow-down.png">
</section>
<section class="esh-identity-drop">
<a class="esh-identity-item"
asp-controller="Order"
asp-action="MyOrders">
<div class="esh-identity-name esh-identity-name--upper">My orders</div>
<img class="esh-identity-image" src="~/images/my_orders.png">
</a>
<a class="esh-identity-item"
asp-controller="Manage"
asp-action="MyAccount">
<div class="esh-identity-name esh-identity-name--upper">My account</div>
<img class="esh-identity-image" src="~/images/my_orders.png">
</a>
<a class="esh-identity-item"
href="javascript:document.getElementById('logoutForm').submit()">
<div class="esh-identity-name esh-identity-name--upper">Log Out</div>
<img class="esh-identity-image" src="~/images/logout.png">
</a>
</section>
</form>
</div>
</section>
<section class="col-lg-1 col-xs-12">
@await Component.InvokeAsync("Basket", User.Identity.Name)
</section>
<section class="col-lg-1 col-xs-12">
@await Component.InvokeAsync("Basket", User.Identity.Name)
</section>
}
else
{
<section class="col-lg-1 col-lg-offset-3 col-md-3 col-xs-6">
<div class="esh-identity">
<section class="esh-identity-section">
<div class="esh-identity-item">
<a asp-area="" asp-controller="Account" asp-action="SignIn" class="esh-identity-name esh-identity-name--upper">
Login
</a>
</div>
</section>
</div>
</section>
<section class="col-lg-1 col-xs-12">
@await Component.InvokeAsync("Basket")
</section>
<section class="col-lg-1 col-lg-offset-3 col-md-3 col-xs-6">
<div class="esh-identity">
<section class="esh-identity-section">
<div class="esh-identity-item">
<a asp-area="" asp-controller="Account" asp-action="SignIn" class="esh-identity-name esh-identity-name--upper">
Login
</a>
</div>
</section>
</div>
</section>
<section class="col-lg-1 col-xs-12">
@await Component.InvokeAsync("Basket")
</section>
}

4
src/Web/Views/_ViewImports.cshtml

@ -2,6 +2,8 @@
@using Microsoft.eShopWeb.Web.ViewModels
@using Microsoft.eShopWeb.Web.ViewModels.Account
@using Microsoft.eShopWeb.Web.ViewModels.Manage
@using Microsoft.eShopWeb.Web.Pages
@using Microsoft.AspNetCore.Identity
@using Microsoft.eShopWeb.Infrastructure.Identity
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@namespace Microsoft.eShopWeb.Web.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers

107
src/Web/Web.csproj

@ -1,18 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<RootNamespace>Microsoft.eShopWeb.Web</RootNamespace>
<UserSecretsId>aspnet-Web2-1FA3F72E-E7E3-4360-9E49-1CCCD7FE85F7</UserSecretsId>
<AspNetCoreHostingModel>InProcess</AspNetCoreHostingModel>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="1.50.5" />
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.1.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="2.1.0" PrivateAssets="All" />
</ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="4.0.1" />
</ItemGroup>
<ItemGroup>
<Folder Include="Views\Catalog\" />
<Folder Include="wwwroot\fonts\" />
</ItemGroup>
<ItemGroup>
@ -37,6 +38,98 @@
<Content Update="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Update="Views\Shared\Error.cshtml">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="Views\Shared\_Layout.cshtml">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="Views\Shared\_LoginPartial.cshtml">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="Views\Shared\_ValidationScriptsPartial.cshtml">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="Views\_ViewImports.cshtml">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
<Content Update="Views\_ViewStart.cshtml">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Account\Lockout.cshtml" />
</ItemGroup>
<ItemGroup>
<_ContentIncludedByDefault Remove="Views\Account\Lockout.cshtml" />
<_ContentIncludedByDefault Remove="Views\Account\LoginWith2fa.cshtml" />
<_ContentIncludedByDefault Remove="Views\Account\Register.cshtml" />
<_ContentIncludedByDefault Remove="Views\Account\Signin.cshtml" />
<_ContentIncludedByDefault Remove="Views\Manage\ChangePassword.cshtml" />
<_ContentIncludedByDefault Remove="Views\Manage\Disable2fa.cshtml" />
<_ContentIncludedByDefault Remove="Views\Manage\EnableAuthenticator.cshtml" />
<_ContentIncludedByDefault Remove="Views\Manage\ExternalLogins.cshtml" />
<_ContentIncludedByDefault Remove="Views\Manage\GenerateRecoveryCodes.cshtml" />
<_ContentIncludedByDefault Remove="Views\Manage\ResetAuthenticator.cshtml" />
<_ContentIncludedByDefault Remove="Views\Manage\SetPassword.cshtml" />
<_ContentIncludedByDefault Remove="Views\Manage\TwoFactorAuthentication.cshtml" />
<_ContentIncludedByDefault Remove="Views\Manage\_Layout.cshtml" />
<_ContentIncludedByDefault Remove="Views\Manage\_ManageNav.cshtml" />
<_ContentIncludedByDefault Remove="Views\Manage\_StatusMessage.cshtml" />
<_ContentIncludedByDefault Remove="Views\Manage\_ViewImports.cshtml" />
<_ContentIncludedByDefault Remove="Views\Order\Detail.cshtml" />
<_ContentIncludedByDefault Remove="Views\Shared\Components\Basket\Default.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Account\LoginWith2fa.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Account\Register.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Account\Signin.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Manage\_Layout.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Manage\_ManageNav.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Manage\_StatusMessage.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Manage\_ViewImports.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Manage\ChangePassword.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Manage\Disable2fa.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Manage\EnableAuthenticator.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Manage\ExternalLogins.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Manage\GenerateRecoveryCodes.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Manage\ResetAuthenticator.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Manage\SetPassword.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Manage\TwoFactorAuthentication.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Order\Detail.cshtml" />
</ItemGroup>
<ItemGroup>
<UpToDateCheckInput Remove="Views\Shared\Components\Basket\Default.cshtml" />
</ItemGroup>
</Project>

3
src/Web/appsettings.Development.json

@ -1,6 +1,5 @@
{
{
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Debug",
"System": "Information",

3
src/Web/appsettings.json

@ -10,6 +10,7 @@
"Default": "Warning",
"Microsoft": "Warning",
"System": "Warning"
}
},
"AllowedHosts": "*"
}
}

10
src/Web/bower.json

@ -1,10 +0,0 @@
{
"name": "asp.net",
"private": true,
"dependencies": {
"bootstrap": "3.3.7",
"jquery": "2.2.0",
"jquery-validation": "1.14.0",
"jquery-validation-unobtrusive": "3.2.6"
}
}

24
src/Web/bundleconfig.json

@ -1,24 +0,0 @@
// Configure bundling and minification for the project.
// More info at https://go.microsoft.com/fwlink/?LinkId=808241
[
{
"outputFileName": "wwwroot/css/app.min.css",
// An array of relative input file paths. Globbing patterns supported
"inputFiles": [
"wwwroot/css/app.css"
]
},
{
"outputFileName": "wwwroot/js/site.min.js",
"inputFiles": [
"wwwroot/js/site.js"
],
// Optionally specify minification options
"minify": {
"enabled": true,
"renameLocals": true
},
// Optionally generate .map file
"sourceMap": false
}
]

42
src/Web/compilerconfig.json

@ -1,42 +0,0 @@
[
{
"outputFile": "wwwroot/css/orders/orders.component.css",
"inputFile": "wwwroot/css/orders/orders.component.scss"
},
//{
// "outputFile": "wwwroot/css/orders/orders-new/orders-new.component.css",
// "inputFile": "wwwroot/css/orders/orders-new/orders-new.component.scss"
//},
//{
// "outputFile": "wwwroot/css/orders/orders-detail/orders-detail.component.css",
// "inputFile": "wwwroot/css/orders/orders-detail/orders-detail.component.scss"
//},
{
"outputFile": "wwwroot/css/catalog/catalog.component.css",
"inputFile": "wwwroot/css/catalog/catalog.component.scss"
},
{
"outputFile": "wwwroot/css/basket/basket.component.css",
"inputFile": "wwwroot/css/basket/basket.component.scss"
},
{
"outputFile": "wwwroot/css/basket/basket-status/basket-status.component.css",
"inputFile": "wwwroot/css/basket/basket-status/basket-status.component.scss"
},
//{
// "outputFile": "wwwroot/css/shared/components/header/header.css",
// "inputFile": "wwwroot/css/shared/components/header/header.scss"
//},
//{
// "outputFile": "wwwroot/css/shared/components/identity/identity.css",
// "inputFile": "wwwroot/css/shared/components/identity/identity.scss"
//},
//{
// "outputFile": "wwwroot/css/shared/components/pager/pager.css",
// "inputFile": "wwwroot/css/shared/components/pager/pager.scss"
//},
{
"outputFile": "wwwroot/css/app.component.css",
"inputFile": "wwwroot/css/app.component.scss"
}
]

40
src/Web/wwwroot/lib/jquery-validation/.bower.json

@ -1,40 +0,0 @@
{
"name": "jquery-validation",
"homepage": "http://jqueryvalidation.org/",
"repository": {
"type": "git",
"url": "git://github.com/jzaefferer/jquery-validation.git"
},
"authors": [
"Jörn Zaefferer <joern.zaefferer@gmail.com>"
],
"description": "Form validation made easy",
"main": "dist/jquery.validate.js",
"keywords": [
"forms",
"validation",
"validate"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"demo",
"lib"
],
"dependencies": {
"jquery": ">= 1.7.2"
},
"version": "1.14.0",
"_release": "1.14.0",
"_resolution": {
"type": "version",
"tag": "1.14.0",
"commit": "c1343fb9823392aa9acbe1c3ffd337b8c92fed48"
},
"_source": "git://github.com/jzaefferer/jquery-validation.git",
"_target": ">=1.8",
"_originalSource": "jquery-validation"
}

3
src/WebRazorPages/.bowerrc

@ -1,3 +0,0 @@
{
"directory": "wwwroot/lib"
}

3
src/WebRazorPages/Areas/Identity/Pages/_ViewStart.cshtml

@ -0,0 +1,3 @@
@{
Layout = "/Pages/Shared/_Layout.cshtml";
}

16
src/WebRazorPages/Data/ApplicationDbContext.cs

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace WebRazorPages.Data
{
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
}
}

236
src/WebRazorPages/Data/Migrations/00000000000000_CreateIdentitySchema.Designer.cs

@ -0,0 +1,236 @@
// <auto-generated />
using System;
using WebRazorPages.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace WebRazorPages.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("00000000000000_CreateIdentitySchema")]
partial class CreateIdentitySchema
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.0-preview1")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<string>("SecurityStamp");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128);
b.Property<string>("ProviderKey")
.HasMaxLength(128);
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider")
.HasMaxLength(128);
b.Property<string>("Name")
.HasMaxLength(128);
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

220
src/WebRazorPages/Data/Migrations/00000000000000_CreateIdentitySchema.cs

@ -0,0 +1,220 @@
using System;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
namespace WebRazorPages.Data.Migrations
{
public partial class CreateIdentitySchema : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<string>(nullable: false),
Name = table.Column<string>(maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(maxLength: 256, nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<string>(nullable: false),
UserName = table.Column<string>(maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(maxLength: 256, nullable: true),
Email = table.Column<string>(maxLength: 256, nullable: true),
NormalizedEmail = table.Column<string>(maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(nullable: false),
PasswordHash = table.Column<string>(nullable: true),
SecurityStamp = table.Column<string>(nullable: true),
ConcurrencyStamp = table.Column<string>(nullable: true),
PhoneNumber = table.Column<string>(nullable: true),
PhoneNumberConfirmed = table.Column<bool>(nullable: false),
TwoFactorEnabled = table.Column<bool>(nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(nullable: true),
LockoutEnabled = table.Column<bool>(nullable: false),
AccessFailedCount = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
RoleId = table.Column<string>(nullable: false),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserClaims",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
UserId = table.Column<string>(nullable: false),
ClaimType = table.Column<string>(nullable: true),
ClaimValue = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserLogins",
columns: table => new
{
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
ProviderKey = table.Column<string>(maxLength: 128, nullable: false),
ProviderDisplayName = table.Column<string>(nullable: true),
UserId = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
table.ForeignKey(
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
RoleId = table.Column<string>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<string>(nullable: false),
LoginProvider = table.Column<string>(maxLength: 128, nullable: false),
Name = table.Column<string>(maxLength: 128, nullable: false),
Value = table.Column<string>(nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
table.ForeignKey(
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
table: "AspNetRoles",
column: "NormalizedName",
unique: true,
filter: "[NormalizedName] IS NOT NULL");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserClaims_UserId",
table: "AspNetUserClaims",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserLogins_UserId",
table: "AspNetUserLogins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_RoleId",
table: "AspNetUserRoles",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "EmailIndex",
table: "AspNetUsers",
column: "NormalizedEmail");
migrationBuilder.CreateIndex(
name: "UserNameIndex",
table: "AspNetUsers",
column: "NormalizedUserName",
unique: true,
filter: "[NormalizedUserName] IS NOT NULL");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AspNetRoleClaims");
migrationBuilder.DropTable(
name: "AspNetUserClaims");
migrationBuilder.DropTable(
name: "AspNetUserLogins");
migrationBuilder.DropTable(
name: "AspNetUserRoles");
migrationBuilder.DropTable(
name: "AspNetUserTokens");
migrationBuilder.DropTable(
name: "AspNetRoles");
migrationBuilder.DropTable(
name: "AspNetUsers");
}
}
}

234
src/WebRazorPages/Data/Migrations/ApplicationDbContextModelSnapshot.cs

@ -0,0 +1,234 @@
// <auto-generated />
using System;
using WebRazorPages.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace WebRazorPages.Data.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.2.0-preview1")
.HasAnnotation("Relational:MaxIdentifierLength", 128)
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Name")
.HasMaxLength(256);
b.Property<string>("NormalizedName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasName("RoleNameIndex")
.HasFilter("[NormalizedName] IS NOT NULL");
b.ToTable("AspNetRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("RoleId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUser", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("AccessFailedCount");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken();
b.Property<string>("Email")
.HasMaxLength(256);
b.Property<bool>("EmailConfirmed");
b.Property<bool>("LockoutEnabled");
b.Property<DateTimeOffset?>("LockoutEnd");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256);
b.Property<string>("NormalizedUserName")
.HasMaxLength(256);
b.Property<string>("PasswordHash");
b.Property<string>("PhoneNumber");
b.Property<bool>("PhoneNumberConfirmed");
b.Property<string>("SecurityStamp");
b.Property<bool>("TwoFactorEnabled");
b.Property<string>("UserName")
.HasMaxLength(256);
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasName("UserNameIndex")
.HasFilter("[NormalizedUserName] IS NOT NULL");
b.ToTable("AspNetUsers");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
b.Property<string>("ClaimType");
b.Property<string>("ClaimValue");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasMaxLength(128);
b.Property<string>("ProviderKey")
.HasMaxLength(128);
b.Property<string>("ProviderDisplayName");
b.Property<string>("UserId")
.IsRequired();
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("RoleId");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId");
b.Property<string>("LoginProvider")
.HasMaxLength(128);
b.Property<string>("Name")
.HasMaxLength(128);
b.Property<string>("Value");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

4
src/WebRazorPages/Dockerfile

@ -7,7 +7,7 @@
#
# RUN COMMAND
# docker run --name eshopweb --rm -it -p 5107:5107 webrazor
FROM microsoft/dotnet:2.1-sdk AS build
FROM microsoft/dotnet:2.2-sdk AS build
WORKDIR /app
COPY *.sln .
@ -17,7 +17,7 @@ RUN dotnet restore
RUN dotnet publish -c Release -o out
FROM microsoft/dotnet:2.1-aspnetcore-runtime AS runtime
FROM microsoft/dotnet:2.2-aspnetcore-runtime AS runtime
WORKDIR /app
COPY --from=build /app/src/WebRazorPages/out ./

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save