Merge branch 'master' into pr/119

This commit is contained in:
Eric Fleming
2019-01-22 20:33:06 -05:00
149 changed files with 3069 additions and 1053 deletions

View File

@@ -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:

View File

@@ -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:

View File

@@ -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}

View File

@@ -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>

View File

@@ -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

View File

@@ -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
{

View File

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

View File

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

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;}
}
}

View File

@@ -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);
}

View File

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

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

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

View File

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

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

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

View File

@@ -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
{

View File

@@ -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>

View File

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

View File

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

View File

@@ -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";
}
}

View File

@@ -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");
}
}

View File

@@ -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
{ }
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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)

View File

@@ -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));

View File

@@ -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)
{
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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");
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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 ./

View File

@@ -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.");
}
}
}

View File

@@ -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.");
}
}
}

View File

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

View File

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

View File

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

View File

@@ -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
{

View File

@@ -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>

View File

@@ -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);
}
}
}

View File

@@ -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>
<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>
}
<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>
<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>

View File

@@ -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);
}
}
}

View File

@@ -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>

View File

@@ -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;
}
}
}

View File

@@ -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
{

View File

@@ -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);
}
}
}

View File

@@ -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>

View File

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

View File

@@ -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
{

View File

@@ -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>

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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

View File

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

View File

@@ -1,11 +1,11 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.eShopWeb.Infrastructure.Data;
using System;
using Microsoft.Extensions.Logging;
using Microsoft.eShopWeb.Infrastructure.Identity;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.eShopWeb.Infrastructure.Data;
using Microsoft.eShopWeb.Infrastructure.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>();
}
}

View File

@@ -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"
}
}
}
}
}

View File

@@ -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);
}

View File

@@ -1,13 +1,13 @@
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
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;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.eShopWeb.Web.ViewModels;
using Microsoft.eShopWeb.ApplicationCore.Entities;
using Microsoft.Extensions.Logging;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using System;
using Microsoft.eShopWeb.ApplicationCore.Specifications;
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 filterPaginatedSpecification =
new CatalogFilterPaginatedSpecification(itemsPage * pageIndex, itemsPage, brandId, typeId);
var totalItems = root.Count();
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 =>
{

View File

@@ -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();
}
}
}

View File

@@ -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)
{
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<AppIdentityDbContext>()
.AddDefaultTokenProviders();
ConfigureCookieSettings(services);
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
};
});
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddDefaultUI(UIFramework.Bootstrap4)
.AddEntityFrameworkStores<AppIdentityDbContext>()
.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 = services;
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; // 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());
}));
}
}
}

View File

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

View File

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

View File

@@ -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
{

View File

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

View File

@@ -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";

View File

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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

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

View File

@@ -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>
}

View File

@@ -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>

View File

@@ -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">
<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>
@*<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-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">
<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>
<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-xs-12">
@await Component.InvokeAsync("Basket")
</section>
}

View File

@@ -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

View File

@@ -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>

View File

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

View File

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

View File

@@ -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"
}
}

View File

@@ -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
}
]

View File

@@ -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"
}
]

View File

@@ -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"
}

View File

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

View File

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

View File

@@ -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)
{
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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");
}
}
}

View File

@@ -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
}
}
}

View File

@@ -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 have changed in this diff Show More